我正在尝试实现自定义Border,它绑定到ViewModel中的bool属性,每当该属性更改时,我都想用Border做一些动画。
ViewModel属性有OnPropertyChanged接口,它看起来像这样:
public bool Enable_control
{
get { return _enable_ctl; }
set { _enable_ctl = value; OnPropertyChanged(); }
}
private bool _enable_ctl;
这就是我在xaml:中绑定自定义边界的方式
<cust_border:Border_slide ShowSlide={Binding Enable_control}"/>
这是我的自定义边境管制代码:
public class Border_slide : Border
{
public Border_slide()
{
}
public bool ShowSlide
{
get { return (bool)GetValue(ShowSlideProperty); }
set { SetValue(ShowSlideProperty, value);}
}
public static readonly DependencyProperty ShowSlideProperty =
DependencyProperty.Register("ShowSlide", typeof(bool), typeof(Border_slide), new
FrameworkPropertyMetadata(true, new PropertyChangedCallback(ShowSlideChanged)));
private static void ShowSlideChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//I would like to do animation of control when property of ViewModel changes - presumably here,
//but PropertyChangedCallback gets triggered only once - not on every Enable_control change!
}
}
所以,问题是:如何从Viewmodel属性更改中正确更新UserControl的依赖属性,以便下一步对UserControl做些什么
有一段时间我做了和你类似的事情。
就我而言,我希望它会有所帮助,因为我是这样解决的。但它可能与你想象的不同,所以如果我错过了什么,请告诉我!
我已经将我的示例源代码上传到GitHub
这里是示例源代码。https://github.com/ncoresoftsource/stackoverflowsample/tree/main/src/answers/dependency-border-animation
视图模型
public class MainViewModel : ObservableObject
{
private bool _enable_ctl;
public bool Enable_control
{
get { return _enable_ctl; }
set { _enable_ctl = value; OnPropertyChanged(); }
}
}
主窗口
我用了Label而不是Border。由于Border不能使用ControlTemplate,因此可以将其约束为使用和展开动画。
<CheckBox Content="Switch" IsChecked="{Binding Enable_control}"/>
<Label Style="{StaticResource SLIDER}"/>
App.xaml
<Style TargetType="Label" x:Key="SLIDER">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="#AAAAAA"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="100"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Label">
<Border x:Name="border" Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Opacity="0">
<ContentPresenter/>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Enable_control}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation From="0" To="1" Duration="00:00:0.5"
Storyboard.TargetName="border"
Storyboard.TargetProperty="Opacity"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation From="1" To="0" Duration="00:00:0.5"
Storyboard.TargetName="border"
Storyboard.TargetProperty="Opacity"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
已解决。Clemens部分是对的——我的代码没有任何问题,物业确实收到了更改通知。我的问题在其他地方——一个变量在属性更改之前并没有得到值,以及依赖属性的默认值。我现在把这些东西都整理好了,一切如愿以偿。
这是我的完整代码,可能对某人有用,至少我觉得它是这样的:
这是一个自定义边框,可以用于左边距的滑动动画。这意味着你可以使用这个边框将一些菜单从左侧滑动到所需的右侧位置,只需在里面添加控件,就可以开始了。代码也可以用于其他控件(面板等(,但我使用了Border,因为我可以用它轻松地圆角或做其他漂亮的UI演示。
背后的逻辑是,如果设置MoveTo属性,则Border将滑动到所需的左边距位置。如果不这样做,则"边框"将滑动到屏幕中心,这意味着您可以使用此"边框"在主窗口中将菜单滑动到居中或左对齐的视图中。这实际上就是创建它的全部意义——因为你不能在动画中使用动态值To或From,因为这些都是自由值。所以我不得不创建这个自定义控件,因为我无法对屏幕中心进行动画处理(宽度需要首先计算(。不管怎样,在这里:
public class Border_slide : Border
{
public Border_slide()
{
Loaded += Border_slide_Loaded;
}
private void Border_slide_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
//Save starting Margin.Left
StartX = Margin.Left;
}
///<summary>Property for starting X location</summary>
private double StartX
{
get { return _startX; }
set { _startX = value; }
}
private double _startX;
public static DependencyProperty MoveToProperty =
DependencyProperty.Register("MoveTo", typeof(double), typeof(Border_slide), new PropertyMetadata(default(double)));
///<summary>Property thats sets to what Margin.Left we want move Border</summary>
public double MoveTo
{
get { return (double)GetValue(MoveToProperty); }
set { SetValue(MoveToProperty, value); }
}
public bool ShowSlide
{
get { return (bool)GetValue(ShowSlideProperty); }
set { SetValue(ShowSlideProperty, value); }
}
public static readonly DependencyProperty ShowSlideProperty =
DependencyProperty.Register("ShowSlide", typeof(bool), typeof(Border_slide), new FrameworkPropertyMetadata(true,
new PropertyChangedCallback(ShowSlideChanged)));
private static void ShowSlideChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var brd = d as Border_slide;
//Animate Margin, when Property changes
if (((bool)e.NewValue) == true) //When property is true, Border is allready at desired location, so we move It to first x- location
{
ThicknessAnimation back_to_start_X_location = new ThicknessAnimation
{
BeginTime = TimeSpan.FromSeconds(0),
Duration = TimeSpan.FromSeconds(1),
From = new Thickness(brd.Margin.Left, 0, 0, 0),
To = new Thickness(brd.StartX, 0, 0, 0)
};
brd.BeginAnimation(Border.MarginProperty, back_to_start_X_location);
}
else //If property is False we need to move Border to desired X- location
{
//If we don't set MoveTo property then move Border to center of screen
if (brd.MoveTo == default(double))
{
var X_center = Application.Current.MainWindow.ActualWidth / 2 - brd.Width / 2;
ThicknessAnimation to_center_X_location = new ThicknessAnimation
{
BeginTime = TimeSpan.FromSeconds(0),
Duration = TimeSpan.FromSeconds(1),
From = new Thickness(brd.ZacetniX, 0, 0, 0),
To = new Thickness(X_center, 0, 0, 0)
};
brd.BeginAnimation(Border.MarginProperty, to_center_X_location);
}
else //If MoveTo property is set then move Border to desired X-location
{
ThicknessAnimation to_desired_X_location = new ThicknessAnimation
{
BeginTime = TimeSpan.FromSeconds(0),
Duration = TimeSpan.FromSeconds(1),
From = new Thickness(brd.StartX, 0, 0, 0),
To = new Thickness(brd.MoveTo, 0, 0, 0)
};
brd.BeginAnimation(Border.MarginProperty, to_desired_X_location);
}
}
}
}
因此,正如您所看到的,边界的移动取决于您设置的起始边距。Left(这是必须的(和/或MoveTo属性,绑定到ViewModel中的一些bool值。在我的情况下,当我在视图中禁用所有其他控件时,我会滑动这个边框,这样我也可以在视图中产生模糊效果。如果您将它用于不同的用途,请小心ShowSlide属性的默认值(我的默认值是true(。当然,你也可以重新调整对不同事物的控制。。。
例如XAML中的用法:
<my_controls:Border_slide Margin="-500,0,0,0" MoveTo="5" ShowSlide="{Binding Enable_control}">