[WPF 사용자 정의 컨트롤 라이브러리] 사용자 정의 확장기

원본 : [WPF 사용자 정의 컨트롤 라이브러리] 사용자 정의 확장기

1. 소개

이전 기사는 영업 실적도 좋은, 사용 리사이가 확장기 간단한 애니메이션 효과를 달성 설명 있지만 (결국 리사이은 부수적 인 기능 확장기를 모방) / 축소 및 페이드 인 / 페이드 아웃 애니메이션의 부족을 확장합니다. 이 항목에서는 애니메이션을 사용자 정의 ExtendedExpander을 측정하고 있습니다.

2. ExtendedExpander 수요

ExpandSite에서의 ControlTemplate 때문에 직접 낮은 붕괴 된 상태에 접어 페이드 애니메이션을 할 수있는 방법을 몰랐어요 확장기 Resizer는 사용하기 쉬운이 숨겨져 있습니다. 약간은 다음과 같은 요구 사항을 충족하기 위해 변경 애니메이션 확장기의 더 나은 상태를 보면 :

  • 인장
  • 페이드
  • 위의 두 가지 효과는 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실제로 재 배열을 위해 두 단계의 요구 사항이다. 기능 및 문서의 도입 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