[WPFカスタムコントロールライブラリ]カスタムエキスパンダー

オリジナル: [WPFカスタムコントロールライブラリ]カスタムエキスパンダー

1.はじめに

前の記事では、リサイズがパンダ簡単なアニメーション効果を達成するために使用を説明し、業績も良いですが、唯一の拡大/縮小やフェードイン/フェードアウトアニメーション(すべてのリサイズはパンダだけ偶発的機能を模倣した後)の欠如。このトピックでは、アニメーションでカスタマイズされたExtendedExpanderを測定し続けています。

2. ExtendedExpander需要

ExpandSite内のControlTemplateので、直接、下の折りたたまれた状態で、折りたたまれたフェードインアニメーションを実行する方法を実現していなかったパンダのリサイズを使用して簡単には表示されません。少し、次の要件を満たすように変更アニメーションエキスパンダーのより良い状態を見て:

  • 引張
  • フェード
  • 上記の2つの効果がXAMLで定義することができます

次のように、最終的な業績は以下のとおりです。

3.アイデアの実現

SilverlightToolkitを模倣、私はまた、制御パーセンテージストレッチパンダのコンテンツのプロパティでExpandableContentControlコントロールを使用します。(なお、SilverlightToolkitパンダにはアニメーションの延伸、ExpandableContentControl内部AccordionItemを使用しません)。ExpandableContentControlの割合は完全に0が完全に折り畳まれ、1を展開し、コントロールの配置の割合を制御する属性。

拡張使用visualSTATEの制御は、/のControlTemplateでアニメーションを崩壊しました。前にVisualStateGroup.Transitions遷移アニメーションコントロールのVusialState.Storyboard制御visualSTATEの最終値記事導入しました。

lt;Border BorderBrush=quot;{TemplateBinding BorderBrush}quot; BorderThickness=quot;{TemplateBinding BorderThickness}quot; Background=quot;{TemplateBinding Background}quot; CornerRadius=quot;3quot; SnapsToDevicePixels=quot;truequot;gt;
    lt;VisualStateManager.VisualStateGroupsgt;
        lt;VisualStateGroup x:Name=quot;ExpansionStatesquot;gt;
            lt;VisualStateGroup.Transitionsgt;
                lt;VisualTransition GeneratedDuration=quot;0:0:0.3quot;gt;
                    lt;VisualTransition.GeneratedEasingFunctiongt;
                        lt;QuarticEase EasingMode=quot;EaseOutquot;/gt;
                    lt;/VisualTransition.GeneratedEasingFunctiongt;
                lt;/VisualTransitiongt;
            lt;/VisualStateGroup.Transitionsgt;
            lt;VisualState x:Name=quot;Expandedquot;/gt;
            lt;VisualState x:Name=quot;Collapsedquot;gt;
                lt;Storyboardgt;
                    lt;DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=quot;(UIElement.Opacity)quot; Storyboard.TargetName=quot;ExpandableContentControlquot;gt;
                        lt;EasingDoubleKeyFrame KeyTime=quot;0quot; Value=quot;0quot;/gt;
                    lt;/DoubleAnimationUsingKeyFramesgt;
                    lt;DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=quot;Percentagequot; Storyboard.TargetName=quot;ExpandableContentControlquot;gt;
                        lt;EasingDoubleKeyFrame KeyTime=quot;0quot; Value=quot;0quot;/gt;
                    lt;/DoubleAnimationUsingKeyFramesgt;
                lt;/Storyboardgt;
            lt;/VisualStategt;
        lt;/VisualStateGroupgt;
    lt;/VisualStateManager.VisualStateGroupsgt;
    lt;DockPanelgt;
        lt;ToggleButton x:Name=quot;HeaderSitequot; ContentTemplate=quot;{TemplateBinding HeaderTemplate}quot; ContentTemplateSelector=quot;{TemplateBinding HeaderTemplateSelector}quot; Content=quot;{TemplateBinding Header}quot; DockPanel.Dock=quot;Topquot; Foreground=quot;{TemplateBinding Foreground}quot; FontWeight=quot;{TemplateBinding FontWeight}quot; FocusVisualStyle=quot;{StaticResource ExpanderHeaderFocusVisual}quot; FontStyle=quot;{TemplateBinding FontStyle}quot; FontStretch=quot;{TemplateBinding FontStretch}quot; FontSize=quot;{TemplateBinding FontSize}quot; FontFamily=quot;{TemplateBinding FontFamily}quot; HorizontalContentAlignment=quot;{TemplateBinding HorizontalContentAlignment}quot; IsChecked=quot;{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}quot; Margin=quot;1quot; MinWidth=quot;0quot; MinHeight=quot;0quot; Padding=quot;{TemplateBinding Padding}quot; Style=quot;{StaticResource ExpanderDownHeaderStyle}quot; VerticalContentAlignment=quot;{TemplateBinding VerticalContentAlignment}quot;/gt;
        lt;Primitives:ExpandableContentControl x:Name=quot;ExpandableContentControlquot; HorizontalContentAlignment=quot;{TemplateBinding HorizontalContentAlignment}quot; VerticalContentAlignment=quot;{TemplateBinding VerticalContentAlignment}quot;
                                             Margin=quot;{TemplateBinding Padding}quot; ClipToBounds=quot;Truequot;gt;
            lt;ContentPresenter x:Name=quot;ExpandSitequot; DockPanel.Dock=quot;Bottomquot; Focusable=quot;falsequot; HorizontalAlignment=quot;{TemplateBinding HorizontalContentAlignment}quot;  VerticalAlignment=quot;{TemplateBinding VerticalContentAlignment}quot;/gt;
        lt;/Primitives:ExpandableContentControlgt;
    lt;/DockPanelgt;
lt;/Bordergt;
lt;ControlTemplate.Triggersgt;
    lt;Trigger Property=quot;IsExpandedquot; Value=quot;falsequot;gt;
        lt;Setter Property=quot;IsHitTestVisiblequot; TargetName=quot;ExpandableContentControlquot; Value=quot;Falsequot;/gt;
    lt;/Triggergt;
    ...
lt;/ControlTemplate.Triggersgt;

このようなパンダとそのControlTemplateのアニメーションの実現にのみ最小限の変更。ExpandableContentControlにメインのコード・ロジック。

4. ExpandableContentControlを実装

次のようにContentControlに、その定義のパーセンテージのプロパティから派生ExpandableContentControl:

public static readonly DependencyProperty PercentageProperty =
    DependencyProperty.Register(nameof(Percentage),
                                typeof(double),
                                typeof(ExpandableContentControl),
                                new FrameworkPropertyMetadata(1d, FrameworkPropertyMetadataOptions.AffectsMeasure));

FrameworkPropertyMetadataOptionsれ、依存関係プロパティの動作を定義するために使用されるAffectsMeasure尺度でアレンジするので、値の変化が再測定の依存性を必要とすることを意味するが起こるので、これはAffectsMeasure実際に再レイアウトするために2段階の要件です。機能や製品の導入InvalidateMeasureと同じ程度。

親要素応じた割合でMeasureOverrideで達成することができ、彼らが必要とするどのくらいのスペース、そして効果をストレッチアニメーション動作パーセンテージプロパティを使用して伝えます。

protected override Size MeasureOverride(Size constraint)
{
    int count = VisualChildrenCount;
    Size childConstraint = new Size(Double.PositiveInfinity, Double.PositiveInfinity);
    UIElement child = (count gt; 0) ? GetVisualChild(0) as UIElement : null;
    var result = new Size();
    if (child != null)
    {
        child.Measure(childConstraint);
        result = child.DesiredSize;
    }

    return new Size(result.Width * Percentage, result.Height * Percentage);
}

最後に、無サイズ制限ので、子要素を配置し、UIのサブ要素が範囲外となり、そうoverrid GetLayoutClip機能は、サブ表示部は、それらのサイズを超えて要素を超えているか否かを制御することができるClipToBounds制御属性。

protected override Geometry GetLayoutClip(Size layoutSlotSize)
{
    if (ClipToBounds)
        return new RectangleGeometry(new Rect(RenderSize));
    else
        return null;
}

限りでのControlTemplateにExpandableContentControlエキスパンダーなどした後、あなたは完了です。

5.アコーディオンを模倣します

実装が単純すぎる、少なすぎるコンテンツので、そうアコーディオンを模倣するためにどのような方法。

アコーディオンは、通常、アコーディオンのように変換されますか?次のように通常もExpandableContentControlで、使用するプログラムの左側のナビゲーションメニューで簡単に真似することができます:

private void OnLoaded(object sender, RoutedEventArgs e)
{
    var expanders = new Listlt;KinoExpandergt;();
    Expander firstExpander = null;
    for (int i = 0; i lt; 10; i++)
    {
        var expander = new KinoExpander() { Header = quot;This is AccordionItem quot; + i };
        if (i == 0)
            firstExpander = expander;

        Grid.SetRow(expander, i);
        var panel = new StackPanel();
        panel.Children.Add(new CheckBox { Content = quot;Calendarquot; });
        panel.Children.Add(new CheckBox { Content = quot;中国节假日quot; });
        panel.Children.Add(new CheckBox { Content = quot;Birthdaysquot; });
        expander.Content = panel;
        MenuRoot.Children.Add(expander);
        MenuRoot.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Auto) });
        int index = i;
        expander.Expanded += (s, args) =gt;
        {

            var lastExpander = expanders.Where(p =gt; p.IsExpanded amp;amp; p != s).FirstOrDefault();
            if (lastExpander != null)
                lastExpander.IsExpanded = false;

            MenuRoot.RowDefinitions[index].Height = new GridLength(1, GridUnitType.Star);
        };

        expander.Collapsed += (s, args) =gt;
          {
              if (expanders.Any(p =gt; p.IsExpanded) == false)
              {
                  expander.IsExpanded = true;
                  return;
              }

              MenuRoot.RowDefinitions[index].Height = new GridLength(1, GridUnitType.Auto);
          };
        expanders.Add(expander);
    }


    firstExpander.IsExpanded = true;
}

MenuRootが空のグリッドである、上記のコードは、エキスパンダーRowDefinitions MenuRootを制御するために使用され、現在選択さに応じて変化します。

次のように最終的な結果は以下のとおりです。

6.おわりに

虽然实现了Expander,但我想这种方式会影响到Expander中ScrollViewer的计算,所以最好还是不要把ScrollViewer放进Expander。

写完这篇文章才发觉可能把这篇和上一篇调换下比较好,因为这篇的Measure的用法更简单。

其实有不少方案可以实现,但为了介绍Measure搞到有点舍近求远了。例如直接用LayoutTransform就挺好的。

不过这种动画效果不怎么好看,所以很多控件库基本上都实现了自己的带动画的Expander控件,例如Telerik开源了UI for UWP控件库,里面的RadExpanderControl是个漂亮优雅的方案,应该可以轻易地移植到WPF(不过某些情况运行起来卡卡的)。

其它控件库的AccordionItem也可以实现类似的功能,可以当作Expander来用,例如Silverlight Toolkit,移植起来应该也不复杂。

另外有没有从上面ExtendedExpander的ControlTemplate感受到不换行的XAML有多烦?Blend产生的样式默认就是这样的。ExtendedExpander的XAML没有使用之前的每个属性一行的方式写,这样的好处是很容易看清楚结构,但在分辨率不高的显示器,或者在Github上根本看不到后面的属性,很容易因为看不到添加在最后的属性犯错(而且我的博客园主题,代码框里还没有滚动条)。使用哪种格式化见仁见智,这篇文章的样式因为是从别的地方复制的,既然保持了原格式就顺便用来讲解一下格式的这个问题,正好HeaderSite的ToggleButton几乎是PresentationFramework.Aero2主题里最长的一行,感受一下这有多欢乐。最终选择使用哪种方式视乎团队人员的显示器有多大,但为了博客里看起来方便我会尽量选择每个属性一行的格式。

7. 参考

Expander 概述 _ Microsoft Docs

Customizing WPF Expander with ControlTemplate - CodeProject

FrameworkPropertyMetadataOptions Enum (System.Windows) _ Microsoft Docs

FrameworkElement.MeasureOverride(Size) Method (System.Windows) Microsoft Docs.html

8. 源码

Kino.Toolkit.Wpf_Expander at master

おすすめ

転載: www.cnblogs.com/lonelyxmas/p/11242110.html