&_wpf树形导航菜单">
当前位置:   article > 正文

WPF GridLength折叠动画+ GridSplitter 拖拽布局_wpf树形导航菜单

wpf树形导航菜单

如下图所示一个布局:点击向左按钮,折叠树形导航菜单面板,拖拽左右分割滑块(WPF里叫GridSplitter)可以调整区域分割宽度.

界面样式选用了Materail DesginUI ,十分好用,外观漂亮,开发常规需求,足够使用了。

Xmal中的布局,其中有一列"menuLeft" 双向绑定了ViewModel里的MenuWidth属性, GridSplitter左右拖拽时动态改变了MenuWidth属性值;

  1. <Grid Grid.Row="2">
  2. <Grid.ColumnDefinitions>
  3. <ColumnDefinition x:Name="menuLeft" Width="{Binding MenuWidth,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged, Converter={StaticResource doubleToGrid}}">
  4. </ColumnDefinition>
  5. <ColumnDefinition Width="8*"/>
  6. </Grid.ColumnDefinitions>
  7. <Grid>
  8. </Grid>
  9. <Grid Grid.Column="1">
  10. </Grid>
  11. <GridSplitter HorizontalAlignment="Right" DragDelta="GridSplitter_DragDelta" Width="10" Height="400"/>
  12. </Grid>

ColumnDefinition的Width 类型为GridLength,非Double型,此处做了一个转化:

  1. public class DoubletoGridConvert : IValueConverter
  2. {
  3. public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  4. {
  5. var n = (double)value;
  6. return new GridLength(n);
  7. }
  8. public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  9. {
  10. GridLength length = (GridLength)value;
  11. if (length != null)
  12. return length.Value;
  13. return 0;
  14. }
  15. }

注意:在menuLeft 的WidthProperty 属性动画结束后,GridSplitter再无法继续拖拽了。这个可能是WPF本身设计的原因,要解决此问题,有三种做法:

There are three ways to do this with storyboard animations:

  • Set the animation's FillBehavior property to Stop

  • Remove the entire Storyboard.

  • Remove the animation from the individual property.

出处:https://msdn.microsoft.com/zh-cn/library/aa970493.aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-4

我这里的做法是:结合1和3点,具体为:

  1. <Storyboard x:Key="menuStoryBoard">
  2. <utility:GridLengthAnimation BeginTime="00:00:00" Completed="GridLengthAnimation_Completed" FillBehavior="Stop" Duration="0:0:0.3" Storyboard.TargetName="menuLeft" Storyboard.TargetProperty="Width"/>
  3. </Storyboard>

  动画结束后:

  1. private void GridLengthAnimation_Completed(object sender, EventArgs e)
  2.         {
  3.             this.menuLeft.BeginAnimation(ColumnDefinition.WidthProperty, null);
  4.         }

其中动画产生的代码为:

  1. MenuCollapseCommand = new DelegateCommand(() =>
  2. {
  3. IsMenuCollapse = !IsMenuCollapse;
  4. var gla = MenuStoryboard.Children[0] as GridLengthAnimation;
  5. if (IsMenuCollapse)
  6. {
  7. menuWidthBeforeCollpase = MenuWidth;
  8. gla.From = new GridLength(MenuWidth, GridUnitType.Pixel);
  9. gla.To = new GridLength(minMenuWidth, GridUnitType.Pixel);
  10. MenuStoryboard.Begin();
  11. MenuWidth = minMenuWidth;
  12. }
  13. else
  14. {
  15. gla.From = new GridLength(minMenuWidth, GridUnitType.Pixel);
  16. gla.To = new GridLength(menuWidthBeforeCollpase, GridUnitType.Pixel);
  17. MenuStoryboard.Begin();
  18. MenuWidth = menuWidthBeforeCollpase;
  19. }
  20. });

考虑WPF MVVM的设计模式:控制UI的行为通常写在ViewModel中,在点击折叠按钮时直接使用事件触发器完成。 

首先安装Microsoft.Expression.Interactions: 

 

 添加命名空间的引用:

   xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"

Xaml中折叠按钮的写法:

  1. <md:PackIcon Cursor="Hand" ToolTip="折叠" ToolTipService.InitialShowDelay="0" Background="Transparent" VerticalAlignment="Center" Visibility="{Binding IsMenuCollapse, Converter={StaticResource boolToVisible}, ConverterParameter=true}" HorizontalAlignment="Right" Margin="10 0" Kind="ArrowLeft" Width="25" Height="25" Foreground="Gainsboro" >
  2. <i:Interaction.Triggers>
  3. <i:EventTrigger EventName="MouseLeftButtonDown">
  4. <i:InvokeCommandAction Command="{Binding MenuCollapseCommand}"/>
  5. <ei:ControlStoryboardAction ControlStoryboardOption="Play" Storyboard="{StaticResource menuStoryBoard}">
  6. <!--<ei:ControlStoryboardAction.Storyboard>
  7. <Storyboard>
  8. <utility:GridLengthAnimation BeginTime="00:00:00" Completed="GridLengthAnimation_Completed" FillBehavior="Stop" Duration="0:0:0.3" Storyboard.TargetName="menuLeft" Storyboard.TargetProperty="Width"/>
  9. </Storyboard>
  10. </ei:ControlStoryboardAction.Storyboard>-->
  11. </ei:ControlStoryboardAction>
  12. </i:EventTrigger>
  13. </i:Interaction.Triggers>
  14. </md:PackIcon>

此处的StoryBoard为:

  1. <Storyboard x:Key="StoryBoard">
  2.                 <utility:GridLengthAnimation BeginTime="00:00:00" From="260" To="12" Completed="GridLengthAnimation_Completed" FillBehavior="Stop" Duration="0:0:0.3" Storyboard.TargetName="menuLeft" Storyboard.TargetProperty="Width"/>
  3.             </Storyboard>

From 和 to 依赖属性可以绑定ViewModel的值,此处写死,仅Demo说明

写在最后:GridLengthAnimation 综合百度他人blog得来:附加于此,供参考

  1. internal class GridLengthAnimation : AnimationTimeline
  2. {
  3. static GridLengthAnimation()
  4. {
  5. FromProperty = DependencyProperty.Register("From", typeof(GridLength), typeof(GridLengthAnimation));
  6. ToProperty = DependencyProperty.Register("To", typeof(GridLength), typeof(GridLengthAnimation));
  7. }
  8. public static readonly DependencyProperty FromProperty;
  9. public GridLength From
  10. {
  11. get
  12. {
  13. return (GridLength)GetValue(GridLengthAnimation.FromProperty);
  14. }
  15. set
  16. {
  17. SetValue(GridLengthAnimation.FromProperty, value);
  18. }
  19. }
  20. public static readonly DependencyProperty ToProperty;
  21. public GridLength To
  22. {
  23. get
  24. {
  25. return (GridLength)GetValue(GridLengthAnimation.ToProperty);
  26. }
  27. set
  28. {
  29. SetValue(GridLengthAnimation.ToProperty, value);
  30. }
  31. }
  32. public override Type TargetPropertyType
  33. {
  34. get
  35. {
  36. return typeof(GridLength);
  37. }
  38. }
  39. protected override System.Windows.Freezable CreateInstanceCore()
  40. {
  41. return new GridLengthAnimation();
  42. }
  43. public override object GetCurrentValue(object defaultOriginValue,
  44. object defaultDestinationValue, AnimationClock animationClock)
  45. {
  46. double fromVal = ((GridLength)GetValue(GridLengthAnimation.FromProperty)).Value;
  47. double toVal = ((GridLength)GetValue(GridLengthAnimation.ToProperty)).Value;
  48. if (fromVal > toVal)
  49. {
  50. return new GridLength((1 - animationClock.CurrentProgress.Value) * (fromVal - toVal) + toVal,
  51. ((GridLength)GetValue(GridLengthAnimation.FromProperty)).GridUnitType);
  52. }
  53. else
  54. return new GridLength(animationClock.CurrentProgress.Value * (toVal - fromVal) + fromVal,
  55. ((GridLength)GetValue(GridLengthAnimation.ToProperty)).GridUnitType);
  56. }
  57. //public override object GetCurrentValue(object defaultOriginValue, object defaultDestinationValue, AnimationClock animationClock)
  58. //{
  59. // double fromVal = ((GridLength)GetValue(FromProperty)).Value;
  60. // double toVal = ((GridLength)GetValue(ToProperty)).Value;
  61. // double progress = animationClock.CurrentProgress.Value;
  62. // IEasingFunction easingFunction = EasingFunction;
  63. // if (easingFunction != null)
  64. // {
  65. // progress = easingFunction.Ease(progress);
  66. // }
  67. // if (fromVal > toVal)
  68. // return new GridLength((1 - progress) * (fromVal - toVal) + toVal, GridUnitType.Star);
  69. // return new GridLength(progress * (toVal - fromVal) + fromVal, GridUnitType.Star);
  70. //}
  71. /// <summary>
  72. /// The <see cref="EasingFunction" /> dependency property's name.
  73. /// </summary>
  74. public const string EasingFunctionPropertyName = "EasingFunction";
  75. /// <summary>
  76. /// Gets or sets the value of the <see cref="EasingFunction" />
  77. /// property. This is a dependency property.
  78. /// </summary>
  79. public IEasingFunction EasingFunction
  80. {
  81. get
  82. {
  83. return (IEasingFunction)GetValue(EasingFunctionProperty);
  84. }
  85. set
  86. {
  87. SetValue(EasingFunctionProperty, value);
  88. }
  89. }
  90. /// <summary>
  91. /// Identifies the <see cref="EasingFunction" /> dependency property.
  92. /// </summary>
  93. public static readonly DependencyProperty EasingFunctionProperty = DependencyProperty.Register(
  94. EasingFunctionPropertyName,
  95. typeof(IEasingFunction),
  96. typeof(GridLengthAnimation),
  97. new UIPropertyMetadata(null));
  98. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/511811
推荐阅读
相关标签
  

闽ICP备14008679号