&_wpf树形导航菜单">
赞
踩
如下图所示一个布局:点击向左按钮,折叠树形导航菜单面板,拖拽左右分割滑块(WPF里叫GridSplitter)可以调整区域分割宽度.
界面样式选用了Materail DesginUI ,十分好用,外观漂亮,开发常规需求,足够使用了。
Xmal中的布局,其中有一列"menuLeft" 双向绑定了ViewModel里的MenuWidth属性, GridSplitter左右拖拽时动态改变了MenuWidth属性值;
- <Grid Grid.Row="2">
- <Grid.ColumnDefinitions>
- <ColumnDefinition x:Name="menuLeft" Width="{Binding MenuWidth,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged, Converter={StaticResource doubleToGrid}}">
- </ColumnDefinition>
- <ColumnDefinition Width="8*"/>
- </Grid.ColumnDefinitions>
- <Grid>
- </Grid>
- <Grid Grid.Column="1">
- </Grid>
- <GridSplitter HorizontalAlignment="Right" DragDelta="GridSplitter_DragDelta" Width="10" Height="400"/>
- </Grid>
ColumnDefinition的Width 类型为GridLength,非Double型,此处做了一个转化:
- public class DoubletoGridConvert : IValueConverter
- {
- public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
- {
- var n = (double)value;
- return new GridLength(n);
- }
-
- public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
- {
- GridLength length = (GridLength)value;
- if (length != null)
- return length.Value;
- return 0;
- }
- }
注意:在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点,具体为:
- <Storyboard x:Key="menuStoryBoard">
- <utility:GridLengthAnimation BeginTime="00:00:00" Completed="GridLengthAnimation_Completed" FillBehavior="Stop" Duration="0:0:0.3" Storyboard.TargetName="menuLeft" Storyboard.TargetProperty="Width"/>
- </Storyboard>
动画结束后:
- private void GridLengthAnimation_Completed(object sender, EventArgs e)
- {
- this.menuLeft.BeginAnimation(ColumnDefinition.WidthProperty, null);
- }
其中动画产生的代码为:
- MenuCollapseCommand = new DelegateCommand(() =>
- {
- IsMenuCollapse = !IsMenuCollapse;
- var gla = MenuStoryboard.Children[0] as GridLengthAnimation;
- if (IsMenuCollapse)
- {
- menuWidthBeforeCollpase = MenuWidth;
- gla.From = new GridLength(MenuWidth, GridUnitType.Pixel);
- gla.To = new GridLength(minMenuWidth, GridUnitType.Pixel);
- MenuStoryboard.Begin();
- MenuWidth = minMenuWidth;
- }
- else
- {
- gla.From = new GridLength(minMenuWidth, GridUnitType.Pixel);
- gla.To = new GridLength(menuWidthBeforeCollpase, GridUnitType.Pixel);
- MenuStoryboard.Begin();
- MenuWidth = menuWidthBeforeCollpase;
- }
- });
考虑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中折叠按钮的写法:
- <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" >
- <i:Interaction.Triggers>
- <i:EventTrigger EventName="MouseLeftButtonDown">
- <i:InvokeCommandAction Command="{Binding MenuCollapseCommand}"/>
- <ei:ControlStoryboardAction ControlStoryboardOption="Play" Storyboard="{StaticResource menuStoryBoard}">
- <!--<ei:ControlStoryboardAction.Storyboard>
- <Storyboard>
- <utility:GridLengthAnimation BeginTime="00:00:00" Completed="GridLengthAnimation_Completed" FillBehavior="Stop" Duration="0:0:0.3" Storyboard.TargetName="menuLeft" Storyboard.TargetProperty="Width"/>
- </Storyboard>
- </ei:ControlStoryboardAction.Storyboard>-->
- </ei:ControlStoryboardAction>
- </i:EventTrigger>
- </i:Interaction.Triggers>
- </md:PackIcon>
此处的StoryBoard为:
- <Storyboard x:Key="StoryBoard">
- <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"/>
- </Storyboard>
From 和 to 依赖属性可以绑定ViewModel的值,此处写死,仅Demo说明
写在最后:GridLengthAnimation 综合百度他人blog得来:附加于此,供参考
- internal class GridLengthAnimation : AnimationTimeline
- {
- static GridLengthAnimation()
- {
- FromProperty = DependencyProperty.Register("From", typeof(GridLength), typeof(GridLengthAnimation));
- ToProperty = DependencyProperty.Register("To", typeof(GridLength), typeof(GridLengthAnimation));
- }
-
- public static readonly DependencyProperty FromProperty;
- public GridLength From
- {
- get
- {
- return (GridLength)GetValue(GridLengthAnimation.FromProperty);
- }
- set
- {
- SetValue(GridLengthAnimation.FromProperty, value);
- }
- }
-
- public static readonly DependencyProperty ToProperty;
- public GridLength To
- {
- get
- {
- return (GridLength)GetValue(GridLengthAnimation.ToProperty);
- }
- set
- {
- SetValue(GridLengthAnimation.ToProperty, value);
- }
- }
-
- public override Type TargetPropertyType
- {
- get
- {
- return typeof(GridLength);
- }
- }
-
- protected override System.Windows.Freezable CreateInstanceCore()
- {
- return new GridLengthAnimation();
- }
-
- public override object GetCurrentValue(object defaultOriginValue,
- object defaultDestinationValue, AnimationClock animationClock)
- {
- double fromVal = ((GridLength)GetValue(GridLengthAnimation.FromProperty)).Value;
- double toVal = ((GridLength)GetValue(GridLengthAnimation.ToProperty)).Value;
-
- if (fromVal > toVal)
- {
- return new GridLength((1 - animationClock.CurrentProgress.Value) * (fromVal - toVal) + toVal,
- ((GridLength)GetValue(GridLengthAnimation.FromProperty)).GridUnitType);
- }
- else
- return new GridLength(animationClock.CurrentProgress.Value * (toVal - fromVal) + fromVal,
- ((GridLength)GetValue(GridLengthAnimation.ToProperty)).GridUnitType);
- }
-
- //public override object GetCurrentValue(object defaultOriginValue, object defaultDestinationValue, AnimationClock animationClock)
- //{
- // double fromVal = ((GridLength)GetValue(FromProperty)).Value;
- // double toVal = ((GridLength)GetValue(ToProperty)).Value;
- // double progress = animationClock.CurrentProgress.Value;
-
- // IEasingFunction easingFunction = EasingFunction;
- // if (easingFunction != null)
- // {
- // progress = easingFunction.Ease(progress);
- // }
-
-
- // if (fromVal > toVal)
- // return new GridLength((1 - progress) * (fromVal - toVal) + toVal, GridUnitType.Star);
-
- // return new GridLength(progress * (toVal - fromVal) + fromVal, GridUnitType.Star);
- //}
-
- /// <summary>
- /// The <see cref="EasingFunction" /> dependency property's name.
- /// </summary>
- public const string EasingFunctionPropertyName = "EasingFunction";
-
- /// <summary>
- /// Gets or sets the value of the <see cref="EasingFunction" />
- /// property. This is a dependency property.
- /// </summary>
- public IEasingFunction EasingFunction
- {
- get
- {
- return (IEasingFunction)GetValue(EasingFunctionProperty);
- }
- set
- {
- SetValue(EasingFunctionProperty, value);
- }
- }
-
- /// <summary>
- /// Identifies the <see cref="EasingFunction" /> dependency property.
- /// </summary>
- public static readonly DependencyProperty EasingFunctionProperty = DependencyProperty.Register(
- EasingFunctionPropertyName,
- typeof(IEasingFunction),
- typeof(GridLengthAnimation),
- new UIPropertyMetadata(null));
-
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。