【问题标题】:Set Target of Storyboard to Button inside a DataTemplate WPF将情节提要的目标设置为 DataTemplate WPF 中的按钮
【发布时间】:2021-05-24 14:48:35
【问题描述】:

我们正在尝试根据窗口大小设置按钮的高度和宽度。我们希望使用视觉状态来实现这一点。我们想将 Storyboard Target 设置为 DataTemplate 内的按钮。由于 DataTemplate 的名称范围,无法使用 StoryBoard.TargetName 直接按名称设置目标。在 XAML 中有什么方法可以做到这一点。

参考-

XAML:

<UserControl x:Class="ResizeSampleApp.Controls.ResizeUserControl"
             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:ResizeSampleApp.Controls"
             mc:Ignorable="d" 
             SizeChanged="CurrentWindow_SizeChanged"
             x:Name="DashBoard"
             >


    <Grid x:Name="grid">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="ResizeStates">
                <VisualState x:Name="FirstHorizontalBreakpoint">
                    <Storyboard >
                        <DoubleAnimation To="116" Storyboard.TargetName="TargetBtn" Storyboard.TargetProperty="Height"></DoubleAnimation>
                        <DoubleAnimation To="182" Storyboard.TargetName="TargetBtn" Storyboard.TargetProperty="Width"></DoubleAnimation>
                    </Storyboard>
                </VisualState>
                <VisualState x:Name="MinSize">
                    <Storyboard>
                        <DoubleAnimation To="100" Storyboard.TargetName="TargetBtn" Storyboard.TargetProperty="Height"></DoubleAnimation>
                        <DoubleAnimation To="197" Storyboard.TargetName="TargetBtn" Storyboard.TargetProperty="Width"></DoubleAnimation>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

        <StackPanel Orientation="Vertical">

            <Grid Margin="10">
                <ItemsControl Name="icTodoList">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <Button x:Name="TargetBtn" Content="{Binding Title}" />
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </Grid>
        </StackPanel>
    </Grid>
</UserControl>

C#:

public class TodoItem
    {
        public string Title { get; set; }
       
    }
    public partial class ResizeUserControl : UserControl
    {
        public ResizeUserControl()
        {
            InitializeComponent();

            List<TodoItem> items = new List<TodoItem>();
            items.Add(new TodoItem() { Title = "Testing 1 " });
            items.Add(new TodoItem() { Title = "Testing 2" });
            items.Add(new TodoItem() { Title = "Testing 3" });

            icTodoList.ItemsSource = items;


        }
        public void CurrentWindow_SizeChanged(object sender, SizeChangedEventArgs sizeChangedEventArgs)
        {
            

            if (this.ActualWidth > 847)
            { 
                VisualStateManager.GoToState(this.DashBoard, "FirstHorizontalBreakpoint", false);
            }
            else
            {
                VisualStateManager.GoToState(this.DashBoard, "MinSize", false);
            }
        }
}

提前致谢。

【问题讨论】:

    标签: wpf xaml storyboard datatemplate


    【解决方案1】:

    不能使用视觉状态动画来为DataTemplate 中的属性设置动画。 DataTemplate 只是一个模板,它不是可视化树的一部分,在定义故事板的那一刻。将为每个模板化目标复制模板,例如每个项目容器。这意味着将有多个名称为 TargetBtn 的按钮 - 从模板范围之外查看时。因此,您必须将动画移动到 DataTemplate 范围内,这意味着在模板内部。

    除了控件的视觉状态定义之外,您还应该添加属性以将视觉状态反映为对象状态。
    Microsoft Docs 建议对每个状态执行此操作是最佳做法:添加属性如 IsMouseOver 以指示 MouseOver 状态或 IsFocusedFocused 状态串联,仅举一些框架示例.
    这允许您的控件将视觉状态表达为真实的实例状态,可以在控件的直接内容的上下文之外使用 - 视觉状态只能由 VisualStateManager 使用。

    然后您可以使用这个通常是公共的只读依赖属性来触发,例如一个DataTrigger,您在DataTemplate 中定义它来为模板的内容设置动画:

    ResizeUserControl.xaml.cs

    public partial class ResizeUserControl : UserControl
    {
      #region IsExpandedViewEnabled read-only dependency property
    
      protected static readonly DependencyPropertyKey IsExpandedViewEnabledPropertyKey = DependencyProperty.RegisterReadOnly(
        "IsExpandedViewEnabled",
        typeof(bool),
        typeof(ResizeUserControl),
        new PropertyMetadata(default(bool)));
    
      public static readonly DependencyProperty IsExpandedViewEnabledProperty = MyControl.IsExpandedViewEnabledPropertyKey.DependencyProperty;
    
      public bool IsExpandedViewEnabled
      {
        get => (bool) GetValue(MyControl.IsExpandedViewEnabledProperty);
        private set => SetValue(MyControl.IsExpandedViewEnabledPropertyKey, value);
      }
    
      #endregion IsExpandedViewEnabled read-only dependency property
    
      public ResizeUserControl()
      {
        InitializeComponent();
      }
    
      #region Overrides of FrameworkElement
    
      protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
      {
        base.OnRenderSizeChanged(sizeInfo);
        if (this.ActualWidth > 847)
        {
          VisualStateManager.GoToState(this, "FirstHorizontalBreakpoint", false);
          this.IsExpandedViewEnabled = true;
        }
        else
        {
          VisualStateManager.GoToState(this, "MinSize", false);
          this.IsExpandedViewEnabled = false;
        }
      }
    }
    

    ResizeUserControl.xaml

    <UserControl>
      <Grid x:Name="grid">
        <StackPanel Orientation="Vertical">
          <Grid Margin="10">
            <ItemsControl Name="icTodoList"
                          ItemsSource="{Binding DataItems}">
              <ItemsControl.ItemTemplate>
                <DataTemplate>
                  <Button x:Name="TargetBtn" Height="60"
                          Content="{Binding Title}" />
    
                  <DataTemplate.Triggers>
                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=IsExpandedViewEnabled}"
                                 Value="True">
                      <DataTrigger.EnterActions>
                        <BeginStoryboard>
                          <Storyboard>
                            <DoubleAnimation To="116" Storyboard.TargetName="TargetBtn" Storyboard.TargetProperty="Height" />
                            <DoubleAnimation To="182" Storyboard.TargetName="TargetBtn" Storyboard.TargetProperty="Width" />
                          </Storyboard>
                        </BeginStoryboard>
                      </DataTrigger.EnterActions>
    
                      <DataTrigger.ExitActions>
                        <BeginStoryboard>
                          <Storyboard>
                            <DoubleAnimation To="100" Storyboard.TargetName="TargetBtn" Storyboard.TargetProperty="Height" />
                            <DoubleAnimation To="197" Storyboard.TargetName="TargetBtn" Storyboard.TargetProperty="Width" />
                          </Storyboard>
                        </BeginStoryboard>
                      </DataTrigger.ExitActions>
                    </DataTrigger>
                  </DataTemplate.Triggers>
                </DataTemplate>
              </ItemsControl.ItemTemplate>
            </ItemsControl>
          </Grid>
        </StackPanel>
      </Grid>
    </UserControl>
    

    【讨论】:

    • 非常感谢!这是有效的。主要应用程序遵循 MVVM 模式。我之前确实尝试通过绑定 ViewModel 的属性来使用 DataTrigger。但这没有用。
    猜你喜欢
    • 2010-12-11
    • 1970-01-01
    • 2016-09-30
    • 2011-01-11
    • 2016-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-11
    相关资源
    最近更新 更多