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 theirDevicestateproperty which causes aTriggerto get executed inUserControl1.

【讨论】:

关于如何从表达式混合界面执行此操作的任何线索?顺便说一句,我想做的是根据其特定对象上的布尔值分别更改每个项目的 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 附加到对象属性的主要内容,如果未能解决你的问题,请参考以下文章

WPF -- PasswordBox数据绑定方法

WPF -- PasswordBox数据绑定方法

如何通过XAML,WPF中的数据绑定设置VisualState INITIALIZATION

WPF附加属性

WPF 依赖属性和附加属性

WPF 精修篇 附加属性