WPF 将 VisualState 附加到对象 属性
WPF Attach VisualState to Object Property
我正在为 VS2015 开发表达式混合,我有一个 ListBox
绑定到一个 ObservableCollection
自定义对象。这些对象公开了 NotifyPropertyChanged
出现的 Properties
,并且一切正常。
如果 ItemTemplate
与那些 Properties
和我的列表工作良好,我可以绑定部件,但我想做的是根据某个 [=18] 设置 VisualState
=](是否已配置)。我还创建了一些事件(已配置、confLost)并尝试在触发器面板中定位这些事件,但是……没有任何效果。
如何将 VisualStates
绑定到绑定对象的成员??
ItemTemplate
属性 与其他 DependencyProperty
一样工作,它可以随时 set/reset,它的视觉效果将反映在 UI
上。请参阅下面的示例,其中我已将 bool
值绑定到 ToggleButton
状态,并且 ItemControl's
ItemTemplate
相应更改呈现不同的 visual
.
更新: 我设计了一个Device
class 有设备名称和它的状态来制造类似的情况。另一个 class MyVisualStateManager
创建一个可绑定的 属性。因为 VisualStateManager
class 没有暴露任何 属性 直接绑定。代码如下:
XMAL
<Window x:Class="WpfWhosebugTempProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}"
xmlns:local="clr-namespace:WpfWhosebugTempProject"
>
<ItemsControl ItemsSource="{Binding list}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:UserControl1 DataContext="{Binding Name}" Width="200" BorderBrush="Black" BorderThickness="2" Padding="2">
<local:UserControl1.Style>
<Style TargetType="{x:Type local:UserControl1}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataContext.DeviceState}" Value="0">
<Setter Property="local:MyVisualStateManager.VisualState" Value="State1" />
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataContext.DeviceState}" Value="1">
<Setter Property="local:MyVisualStateManager.VisualState" Value="State2" />
</DataTrigger>
</Style.Triggers>
</Style>
</local:UserControl1.Style>
</local:UserControl1>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
用户控件:
<UserControl x:Class="WpfWhosebugTempProject.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="Common">
<VisualState x:Name="State1">
<Storyboard>
<DoubleAnimation To="1" Duration="0:00:2" Storyboard.TargetName="State1Panel" Storyboard.TargetProperty="(UIElement.Opacity)" />
<DoubleAnimation To="0" Duration="0:00:3" Storyboard.TargetName="State2Panel" Storyboard.TargetProperty="(UIElement.Opacity)" />
</Storyboard>
</VisualState>
<VisualState x:Name="State2">
<Storyboard>
<DoubleAnimation To="0" Duration="0:00:3" Storyboard.TargetName="State1Panel" Storyboard.TargetProperty="(UIElement.Opacity)" />
<DoubleAnimation To="1" Duration="0:00:2" Storyboard.TargetName="State2Panel" Storyboard.TargetProperty="(UIElement.Opacity)" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border Name="State2Panel" Background="Green" Opacity="0"/>
<Border Name="State1Panel" Background="Red" Opacity="1"/>
<TextBlock Text="{Binding Path=.}" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
DataContext:
public partial class MainWindow : Window
{
public MainWindow()
{
list = new List<Device>();
list.Add(new Device() {Name="Device 1",DeviceState = 0 });
list.Add(new Device() { Name = "Device 2", DeviceState = 1 });
list.Add(new Device() { Name = "Device 3", DeviceState = 0 });
list.Add(new Device() { Name = "Device 4", DeviceState = 2 });
list.Add(new Device() { Name = "Device 5", DeviceState = 1 });
InitializeComponent();
}
public List<Device> list { get; set; }
}
public class Device : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set
{
name = value;
updateProperty("Name");
}
}
private int deviceState;
public int DeviceState
{
get { return deviceState; }
set
{
deviceState = value;
updateProperty("DeviceState");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void updateProperty(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
Helper Class: 这个 class 公开了一个附加的 属性 VisualState
可以绑定到 xaml
.
中的任何值
public class MyVisualStateManager
{
public static string GetVisualState(DependencyObject obj)
{
return (string)obj.GetValue(VisualStateProperty);
}
public static void SetVisualState(DependencyObject obj, string value)
{
obj.SetValue(VisualStateProperty, value);
}
// Using a DependencyProperty as the backing store for VisualState. This enables animation, styling, binding, etc...
public static readonly DependencyProperty VisualStateProperty =
DependencyProperty.RegisterAttached("VisualState", typeof(string), typeof(MyVisualStateManager), new PropertyMetadata(new PropertyChangedCallback(VisualStateChanged)));
public static void VisualStateChanged(DependencyObject Do, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != null)
{
string state = e.NewValue.ToString();
var control = Do as FrameworkElement;
VisualStateManager.GoToState(control, state, true);
}
}
}
输出
Different Item representing different devices and visual is changed on basis of their
Devicestateproperty which causes a
Triggerto get executed in
UserControl1.
I mean in the user interface of Blend, where to click to get the conditional working,
- 在 Blend 中寻找
Objects and Timeline
面板。
- Select 您定义的 ItemTemplate 中的控件(您已经设置了一个,对吗?),您希望其状态发生变化。 在上面 Kylo 的示例中,我们将 selected
TextBox
。
- 右键单击并 select
Edit Style
创建新样式(最有可能)或 Edit a Copy
适用于任何现有继承样式的样式。
从那里可以在属性选项卡上更改细节。您很可能会直接在 xaml 中进行特定操作。
尽管这些文档适用于版本 2,但它们仍然适用,如果没有别的可以让您对 Blend 有一个概览
尽管 Kylo 的解决方案可以证明是可行的,但 Microsoft 的人员已经为如此简单的操作开发了一个无需代码、只需单击 3 次的解决方案。
解决方案是行为,有一种行为称为 GoToStateAction
,您必须将其中之一添加到您的控件中,然后您可以在其中设置触发器(可以设置为 DataTrigger)。在我的例子中,我绑定了枚举类型的 属性。
然后你可以设置比较和值(等于"ReadyToUse")
然后作为比较的结果,您可以触发特定对象的状态更改,设置对象,然后 select 来自漂亮组合框的状态。甚至还有一个用于使用您的转换的复选框。
我正在为 VS2015 开发表达式混合,我有一个 ListBox
绑定到一个 ObservableCollection
自定义对象。这些对象公开了 NotifyPropertyChanged
出现的 Properties
,并且一切正常。
如果 ItemTemplate
与那些 Properties
和我的列表工作良好,我可以绑定部件,但我想做的是根据某个 [=18] 设置 VisualState
=](是否已配置)。我还创建了一些事件(已配置、confLost)并尝试在触发器面板中定位这些事件,但是……没有任何效果。
如何将 VisualStates
绑定到绑定对象的成员??
ItemTemplate
属性 与其他 DependencyProperty
一样工作,它可以随时 set/reset,它的视觉效果将反映在 UI
上。请参阅下面的示例,其中我已将 bool
值绑定到 ToggleButton
状态,并且 ItemControl's
ItemTemplate
相应更改呈现不同的 visual
.
更新: 我设计了一个Device
class 有设备名称和它的状态来制造类似的情况。另一个 class MyVisualStateManager
创建一个可绑定的 属性。因为 VisualStateManager
class 没有暴露任何 属性 直接绑定。代码如下:
XMAL
<Window x:Class="WpfWhosebugTempProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}"
xmlns:local="clr-namespace:WpfWhosebugTempProject"
>
<ItemsControl ItemsSource="{Binding list}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:UserControl1 DataContext="{Binding Name}" Width="200" BorderBrush="Black" BorderThickness="2" Padding="2">
<local:UserControl1.Style>
<Style TargetType="{x:Type local:UserControl1}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataContext.DeviceState}" Value="0">
<Setter Property="local:MyVisualStateManager.VisualState" Value="State1" />
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataContext.DeviceState}" Value="1">
<Setter Property="local:MyVisualStateManager.VisualState" Value="State2" />
</DataTrigger>
</Style.Triggers>
</Style>
</local:UserControl1.Style>
</local:UserControl1>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
用户控件:
<UserControl x:Class="WpfWhosebugTempProject.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="Common">
<VisualState x:Name="State1">
<Storyboard>
<DoubleAnimation To="1" Duration="0:00:2" Storyboard.TargetName="State1Panel" Storyboard.TargetProperty="(UIElement.Opacity)" />
<DoubleAnimation To="0" Duration="0:00:3" Storyboard.TargetName="State2Panel" Storyboard.TargetProperty="(UIElement.Opacity)" />
</Storyboard>
</VisualState>
<VisualState x:Name="State2">
<Storyboard>
<DoubleAnimation To="0" Duration="0:00:3" Storyboard.TargetName="State1Panel" Storyboard.TargetProperty="(UIElement.Opacity)" />
<DoubleAnimation To="1" Duration="0:00:2" Storyboard.TargetName="State2Panel" Storyboard.TargetProperty="(UIElement.Opacity)" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border Name="State2Panel" Background="Green" Opacity="0"/>
<Border Name="State1Panel" Background="Red" Opacity="1"/>
<TextBlock Text="{Binding Path=.}" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
DataContext:
public partial class MainWindow : Window
{
public MainWindow()
{
list = new List<Device>();
list.Add(new Device() {Name="Device 1",DeviceState = 0 });
list.Add(new Device() { Name = "Device 2", DeviceState = 1 });
list.Add(new Device() { Name = "Device 3", DeviceState = 0 });
list.Add(new Device() { Name = "Device 4", DeviceState = 2 });
list.Add(new Device() { Name = "Device 5", DeviceState = 1 });
InitializeComponent();
}
public List<Device> list { get; set; }
}
public class Device : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set
{
name = value;
updateProperty("Name");
}
}
private int deviceState;
public int DeviceState
{
get { return deviceState; }
set
{
deviceState = value;
updateProperty("DeviceState");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void updateProperty(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
Helper Class: 这个 class 公开了一个附加的 属性 VisualState
可以绑定到 xaml
.
public class MyVisualStateManager
{
public static string GetVisualState(DependencyObject obj)
{
return (string)obj.GetValue(VisualStateProperty);
}
public static void SetVisualState(DependencyObject obj, string value)
{
obj.SetValue(VisualStateProperty, value);
}
// Using a DependencyProperty as the backing store for VisualState. This enables animation, styling, binding, etc...
public static readonly DependencyProperty VisualStateProperty =
DependencyProperty.RegisterAttached("VisualState", typeof(string), typeof(MyVisualStateManager), new PropertyMetadata(new PropertyChangedCallback(VisualStateChanged)));
public static void VisualStateChanged(DependencyObject Do, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != null)
{
string state = e.NewValue.ToString();
var control = Do as FrameworkElement;
VisualStateManager.GoToState(control, state, true);
}
}
}
输出
Different Item representing different devices and visual is changed on basis of their
Devicestateproperty which causes a
Triggerto get executed in
UserControl1.
I mean in the user interface of Blend, where to click to get the conditional working,
- 在 Blend 中寻找
Objects and Timeline
面板。 - Select 您定义的 ItemTemplate 中的控件(您已经设置了一个,对吗?),您希望其状态发生变化。 在上面 Kylo 的示例中,我们将 selected
TextBox
。 - 右键单击并 select
Edit Style
创建新样式(最有可能)或Edit a Copy
适用于任何现有继承样式的样式。
从那里可以在属性选项卡上更改细节。您很可能会直接在 xaml 中进行特定操作。
尽管这些文档适用于版本 2,但它们仍然适用,如果没有别的可以让您对 Blend 有一个概览
尽管 Kylo 的解决方案可以证明是可行的,但 Microsoft 的人员已经为如此简单的操作开发了一个无需代码、只需单击 3 次的解决方案。
解决方案是行为,有一种行为称为 GoToStateAction
,您必须将其中之一添加到您的控件中,然后您可以在其中设置触发器(可以设置为 DataTrigger)。在我的例子中,我绑定了枚举类型的 属性。
然后你可以设置比较和值(等于"ReadyToUse")
然后作为比较的结果,您可以触发特定对象的状态更改,设置对象,然后 select 来自漂亮组合框的状态。甚至还有一个用于使用您的转换的复选框。