WPF将VisualState附加到对象属性

时间:2016-04-25 07:54:02

标签: wpf blend visualstatemanager

我正在研究表达Blend for VS2015,我有ListBox绑定到ObservableCollection个自定义对象。这些对象暴露出Properties出现的NotifyPropertyChanged,一切都很好。

我可以绑定部分,如果ItemTemplate到那些Properties并且我的列表工作得很好但我想要做的是根据某个VisualState设置bool (已配置或未配置)。我还创建了一些事件(配置,confLost)并尝试在触发器面板中定位这些事件,但是没有任何效果。

如何将VisualStates绑定到绑定对象的成员?

3 个答案:

答案 0 :(得分:2)

ItemTemplate属性与任何其他DependencyProperty一样,可随时设置/重置,其视觉效果将反映在UI上。请参阅下面的示例,其中我将bool值绑定到ToggleButton状态,并ItemControl's ItemTemplate相应地更改为 visual

更新:我设计了一个Device类,它具有设备名称和状态,可以产生类似的情况。另一个类MyVisualStateManager用于创建可绑定属性。原因VisualStateManager类没有公开要直接绑定的任何属性。代码如下:

<强> XMAL

<Window x:Class="WpfStackOverflowTempProject.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:WpfStackOverflowTempProject"
    >
    <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="WpfStackOverflowTempProject.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));
        }
    }
}

帮助程序类: 此类公开了可绑定到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 {Devicestate {1}}触发property which causes a的UserControl1 to get executed in

list Item

答案 1 :(得分:1)

尽管Kylo的解决方案可行,但微软的人们已经开始使用无代码,3次点击的解决方案来完成这样一个简单的操作。

解决方案是在行为上,有一个名为GoToStateAction的行为,您必须将其中一个添加到您的控件中,您可以设置触发器(可以设置为DataTrigger)。在我的情况下,我绑定到枚举类型的属性。 然后你可以设置比较和值(等于“ReadyToUse”)

然后作为比较的结果,您可以触发特定对象的状态更改,设置对象,并从一个漂亮的组合框中选择状态。甚至还有一个用于使用转换的checbox。

VS screenshot

答案 2 :(得分:0)

  

我的意思是在Blend的用户界面中,点击以获得有条件的工作,

  1. 在Blend中查找Objects and Timeline面板。
  2. 在您定义的ItemTemplate中选择一个控件(你已经设置了一个吗?),你想让状态发生变化。 在上面的Kylo示例中,我们选择了TextBox
  3. 右键单击并选择Edit Style创建新样式(最有可能)或Edit a Copy,其中包含任何现有的继承样式。
  4. enter image description here

    从那里可以使用属性选项卡来更改细节。很可能您将直接在xaml中进行特定操作。

    enter image description here

    即使是第2版的这些文档,它们仍然适用,如果没有其他任何内容可以为您提供Blend的概述

    Create a style resource

    Style and template overview