0

如何根据不同视觉树节点中另一个元素的触发器中的样式修改视觉树中一个元素的样式

例如,我有一个颜色列表,

        ColorList = new List<ColorViewModel>();
        ColorList.Add(new ColorViewModel() { ColorCode = "#FF0000", ColorName="Red" });
        ColorList.Add(new ColorViewModel() { ColorCode = "#00FF00", ColorName="Green" });
        ColorList.Add(new ColorViewModel() { ColorCode = "#0000FF", ColorName="Blue" });
        this.DataContext = this;

我在 ItemsControl 中显示颜色,在另一个 ItemsControl 中显示它们的名称,当我将鼠标悬停在它们的名称上时,我想增加相应颜色的颜色框的大小。

我尝试根据元素名称设置触发器,但由于范围不同。以下是示例代码,涵盖了我的复杂场景。有没有一种 xaml 方法可以克服这个问题?任何帮助表示赞赏。

<StackPanel Orientation="Horizontal">
    <ItemsControl ItemsSource="{Binding ColorList}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Rectangle Width="20" Height="20" Fill="{Binding ColorCode}">
                    <Rectangle.Style>
                        <Style TargetType="Rectangle">
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding ElementName=ColorName, Path=IsMouseOver}" Value="True">
                                    <Setter Property="Width" Value="30"/>
                                    <Setter Property="Height" Value="30"/>
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </Rectangle.Style>
                </Rectangle>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
    <ItemsControl ItemsSource="{Binding ColorList}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <TextBlock x:Name="ColorName" Text="{Binding ColorName}"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</StackPanel>
4

1 回答 1

0

UIElement.IsMouseOver如果有设置器,则可以仅使用 XAML 。由于它是只读的,因此不能成为Binding. 这IsMouseOver是只读的非常有意义,因为它旨在仅在鼠标输入上进行内部设置。

因此,需要扩展ListBox(或ItemsControl)或实现附加行为。

为了IsMouseOver在两个控件之间传输标志信息,您可以向数据模型添加一个专用属性。数据模型(或DataContext)是两个项目控件之间的唯一链接。

在以下示例中,此属性应具有以下定义:

private bool isPreviewEnabled;
public bool IsPreviewEnabled
{
  get => this.isPreviewEnabled;
  set
  {
    this.isPreviewEnabled = value;
    OnPropertyChanged(nameof(this.IsPreviewEnabled));
  }
}

请注意,模型应该实现INotifyPropertyChanged.

下面的示例是一个附加行为,它通过提供附加属性作为绑定目标来委托鼠标悬停标志。

元素.cs

public class Element : DependencyObject
{
  #region IsMouseOver attached property

  public static readonly DependencyProperty IsMouseOverElementProperty = DependencyProperty.RegisterAttached(
    "IsMouseOver",
    typeof(bool?),
    typeof(Element),
    new FrameworkPropertyMetadata(
      default(bool?), 
      FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, 
      OnBindngAttached));

  public static void SetIsMouseOver(DependencyObject attachingElement, bool? value) => attachingElement.SetValue(Element.IsMouseOverProperty, value);

  public static bool? GetIsMouseOver(DependencyObject attachingElement) => (bool) attachingElement.GetValue(Element.IsMouseOverProperty);

  #endregion

  private static void OnBindngAttached(DependencyObject d, DependencyPropertyChangedEventArgs e)
  {
    // Only listen to mouse events when the binding initializes the property.
    // This guarantees single subscription.
    if (d is FrameworkElement frameworkElement && e.OldValue == null)
    {
      frameworkElement.MouseEnter += DelegateIsMouseEnter;
      frameworkElement.MouseLeave += DelegateIsMouseLeave;
    }
  }

  private static void DelegateIsMouseEnter(object sender, MouseEventArgs e)
  {
    var attachedElement = sender as DependencyObject;
    SetIsMouseOver(attachedElement, true);
  }

  private static void DelegateIsMouseLeave(object sender, MouseEventArgs e)
  {
    var attachedElement = sender as DependencyObject;
    SetIsMouseOver(attachedElement, false);
  }
}

主窗口.xaml

<StackPanel Orientation="Horizontal">
  <ItemsControl ItemsSource="{Binding ColorList}">
    <ItemsControl.ItemTemplate>
      <DataTemplate">
        <Rectangle Fill="{Binding ColorCode}">
          <Rectangle.Style>
            <Style TargetType="Rectangle">
              <Setter Property="Width" Value="20" />
              <Setter Property="Height" Value="20" />
              <Style.Triggers>
                <DataTrigger Binding="{Binding IsPreviewEnabled}" Value="True">
                  <Setter Property="Width" Value="30" />
                  <Setter Property="Height" Value="30" />
                </DataTrigger>
              </Style.Triggers>
            </Style>
          </Rectangle.Style>
        </Rectangle>
      </DataTemplate>
    </ItemsControl.ItemTemplate>
  </ItemsControl>

  <ItemsControl ItemsSource="{Binding ColorList}">
    <ItemsControl.ItemContainerStyle>
      <Style TargetType="ContentPresenter">

        <!-- 
          Delegate the IsMouseOver to the data model, 
          which is the data context of the item container 
        -->
        <Setter Property="Element.IsMouseOver" 
                Value="{Binding IsPreviewEnabled}" />
      </Style>
    </ItemsControl.ItemContainerStyle>
    <ItemsControl.ItemTemplate>
      <DataTemplate>
        <TextBlock Text="{Binding ColorName}" />
      </DataTemplate>
    </ItemsControl.ItemTemplate>
  </ItemsControl>
</StackPanel>
于 2020-06-28T15:29:10.373 回答