【问题标题】:How to attach visual state to a dependency property?如何将视觉状态附加到依赖属性?
【发布时间】:2021-05-12 11:17:06
【问题描述】:

MainWindow 包含一个 ItemsControl,它显示来自列表集合的数据。 Name 和 ControlState 属性附加到 Text 和 VisualState 依赖属性。 VisualStateManager.GoToState() 总是返回 false。如何将视觉状态附加到依赖属性,以便执行视觉状态的更改?

Сode:

主窗口:

<Window x:Class="WpfFW.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfFW"
    mc:Ignorable="d"
    DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}"
    Title="MainWindowFWTest" Height="100" Width="300">
<ItemsControl ItemsSource="{Binding list}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <local:UserControl1  Text="{Binding Name}" VisualState="{Binding ControlState}"
                                 Width="200" BorderBrush="Black" BorderThickness="2" Padding="2"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        list = new ObservableCollection<ControlData>() {
            new ControlData() { Name = "Control 1", ControlState = 0 },
        new ControlData() { Name = "Control 2", ControlState = 1 }};
        InitializeComponent();
    }
    public ObservableCollection<ControlData> list { get; set; }
}

public class ControlData : INotifyPropertyChanged
{
    private string _Name;
    public string Name
    {
        get => _Name;
        set
        {
            _Name = value;
            SetProperty(nameof(Name));
        }
    }

    private int _ControlState;
    public int ControlState
    {
        get => _ControlState;
        set
        {
            _ControlState = value;
            SetProperty(nameof(ControlState));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    public void SetProperty(string name)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
    }
}

用户控制1

<UserControl x:Class="WpfFW.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" 
         xmlns:local="clr-namespace:WpfFW"
         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 Text, RelativeSource={RelativeSource AncestorType=local:UserControl1, Mode=FindAncestor}}" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
public partial class UserControl1 : UserControl
{
    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }

    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register("Text", typeof(string), typeof(UserControl1), new PropertyMetadata(""));

    public string VisualState
    {
        get { return (string)GetValue(VisualStateProperty); }
        set { SetValue(VisualStateProperty, value); }
    }

    public static readonly DependencyProperty VisualStateProperty =
        DependencyProperty.Register("VisualState", typeof(string), typeof(UserControl1), new PropertyMetadata(new PropertyChangedCallback(VisualStateChanged)));

    public static void VisualStateChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        if (e.NewValue != null)
        {
            string state = e.NewValue.ToString();
            var control = o as FrameworkElement;
            bool b = VisualStateManager.GoToState(control, "state" + state, true);
        }
    }

    public UserControl1()
    {
        InitializeComponent();
    }
}

【问题讨论】:

    标签: c# wpf


    【解决方案1】:

    我认为你犯了几个错误。首先,VisualStateGroup 是在Grid 而不是UserControl 本身中定义的。在这种情况下,您需要在调用 VisualStateManager 方法时指定Grid。其次,VisualState 名称区分大小写。第三,在这种情况下,VisualStateManager.GoToElementState 方法是合适的,如Remarks 中所述。

    总之,如果您将Grid 命名为“LayoutRoot”,您的 VisualStateChanged 方法将如下所示:

    public static void VisualStateChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        if (e.NewValue != null)
        {
            string state = e.NewValue.ToString();
            var control = o as UserControl1;
            bool b = VisualStateManager.GoToElementState(control.LayoutRoot, "State" + state, true);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2020-12-22
      • 2013-03-19
      • 2017-09-25
      • 2011-12-26
      • 1970-01-01
      • 2014-07-29
      • 1970-01-01
      • 2010-09-05
      • 1970-01-01
      相关资源
      最近更新 更多