WPF 将 VisualState 附加到对象属性
Posted
技术标签:
【中文标题】WPF 将 VisualState 附加到对象属性【英文标题】:WPF Attach VisualState to Object Property 【发布时间】:2016-04-25 07:54:01 【问题描述】:我正在为 VS2015 开发表达式 Blend,我有一个 ListBox
绑定到一个 ObservableCollection
的自定义对象。那些对象暴露了Properties
,产生了NotifyPropertyChanged
,一切正常。
如果ItemTemplate
到那些Properties
并且我的列表运行良好,我可以将部分绑定,但我想做的是根据某个bool
设置VisualState
(是否已配置)。我还创建了一些事件(已配置,confLost)并尝试在触发器面板中定位这些事件,但.. 没有任何效果。
如何将VisualStates
绑定到绑定对象的成员??
【问题讨论】:
嗨 Javirs,你能发布你的对象类代码吗?或者它的一个sn-p。到时候我会帮你的 @Master 如我的回答所示,根本不需要对象的代码。 【参考方案1】:ItemTemplate
属性与任何其他DependencyProperty
一样工作,可以随时设置/重置,其视觉效果将反映在UI
上。请参阅下面的示例,其中我将bool
值绑定到ToggleButton
状态,ItemControl's
ItemTemplate
相应地更改以呈现不同的visual
。
更新:我设计了一个 Device
类,它具有设备名称和状态,以产生类似的情况。另一个类MyVisualStateManager
创建一个可绑定的属性。原因 VisualStateManager
类不公开任何要直接绑定的属性。代码如下:
XMAL
<Window x:Class="Wpf***TempProject.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:Wpf***TempProject"
>
<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="Wpf***TempProject.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>
数据上下文:
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 类: 该类公开了一个附加属性 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.
【讨论】:
关于如何从表达式混合界面执行此操作的任何线索?顺便说一句,我想做的是根据其特定对象上的布尔值分别更改每个项目的 VisualState,但我想很容易从你的示例中推断出如何到达那里。您能否从表达式 Blend 界面详细说明如何做到这一点? @javirs in expression blend...您的意思是从某个配置中读取该布尔值? 我的意思是在 Blend 的用户界面中,点击哪里可以获得条件工作,VisualStates 之间的切换(带有定义的动画)取决于 Data 变量。到目前为止,我根本没有编辑 xaml,只是使用 Blend 面板。而且一切都很好。我想继续这样做。 很抱歉我现在很忙,所以我想我要到周末才能进行测试,但是你的代码在世界上很有意义并且完全符合我的情况寻找。我非常惊讶状态不会随着一些内置的东西(行为?)而自动改变,但额外类的使用会有所帮助。我会尽快测试,我会告诉你(并选择你的答案) 正如预期的那样,有一种更简单的解决方案,GoToStateAction
行为允许触发器(可以是 DataTrigger),因此您可以更改对象的状态.. 0 代码行,4点击...我将发布一个不错的屏幕截图,其中包含详细信息作为答案。我想以任何方式亲自感谢您为回答所付出的努力。【参考方案2】:
虽然 Kylo 的解决方案可以证明是可行的,但 Microsoft 的人员已经为这样一个简单的操作开发了一种无需代码、单击 3 次即可完成的解决方案。
解决方案是行为,有一种行为称为GoToStateAction
,您必须将其中之一添加到您的控件中,然后您可以在其中设置触发器(可以设置为 DataTrigger)。就我而言,我绑定到枚举类型的属性。
然后你可以设置比较和值(等于“ReadyToUse”)
然后,作为比较的结果,您可以触发特定对象的状态更改,设置对象,然后从漂亮的组合框中选择状态。甚至还有一个用于使用过渡的复选框。
【讨论】:
不错...您还应该发布在这些之后更改的代码...这是实际的关键。【参考方案3】:我的意思是在 Blend 的用户界面中,点击哪里可以让条件工作,
-
在 Blend 中查找
Objects and Timeline
面板。
在您定义的 ItemTemplate 中选择您想要更改状态的控件(您是否设置了一个?)。 在上面 Kylo 的示例中,我们会选择 TextBox
。
右键单击并选择 Edit Style
创建新样式(最有可能)或 Edit a Copy
适用于任何现有继承样式。
从那里可以在属性选项卡上更改细节。您很可能会直接在 xaml 中工作以执行特定操作。
即使这些文档适用于第 2 版,它们仍然适用,如果没有其他内容可以为您提供 Blend 的概述
Create a style resource
Style and template overview
【讨论】:
以上是关于WPF 将 VisualState 附加到对象属性的主要内容,如果未能解决你的问题,请参考以下文章