如何使用触发器来影响相关的网格元素?



作为WPF的一个练习,我正在涉足一个类似数独的网格。

考虑以下(简化的(示例

XAML

<Window x:Class="SO_WPF_Question_Sample.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:SO_WPF_Question_Sample"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<Style x:Key="MouseOverStyle" TargetType="{x:Type Label}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red"/>
<!-- I want something to happen here -->
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<ItemsControl ItemsSource="{Binding Path=Items}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="1,1,1,1">
<Label Content="{Binding}"
Style="{StaticResource MouseOverStyle}"
/>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>

代码隐藏

public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel();
}
}

视图模型

public class MainWindowViewModel // INotifyPropertyChanged omitted for simplicity
{
public MainWindowViewModel()
{
PopulateElements(4);
}
public IEnumerable<Cell> Items { get; set; }
private void PopulateElements(int size)
{
int div = (int)Math.Sqrt(size);
IList<Cell> items = new List<Cell>();
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
items.Add(new Cell()
{
X = i,
Y = j,
Z = j / div + div * (i / div)
});
}
}
Items = items;
}
}

波科类

public class Cell
{
public int X { get; set; }
public int Y { get; set; }
public int Z { get; set; }
public override string ToString()
{
return "Row: " + X + " Col: " + Y + " Box: " + Z;
}
}

唯一有趣的一点是:每当我将鼠标悬停在网格元素上时,它就会变成红色。

我想要的:每当我将鼠标悬停在网格元素上时,我也想突出显示所有"同级"单元格。 即 X、Y 和 Z 等于活动单元格的那些。

我不知道如何实现这一目标,但我可以考虑一些策略。

主动策略: 在Trigger,以某种方式触发某种方法(类似于Command?(或其他什么?(,让解析网格中的所有Cell,如果它们是同级,则在它们上设置一些属性,并定义一个Style来响应DataTrigger

被动策略: 触发时,设置一些 ViewModel 属性,该属性触发Cell订阅的事件(INotifyPropertChanged浮现在脑海中(,在Cell设置一个属性中有一个事件处理程序,并再次具有一个带有DataTriggerStyle。我可以在脑海中看到这项工作,但这种方法存在耦合问题。另外,我将如何传达有关悬停在网格元素上的信息?

在研究这个问题时,我遇到了EventTriggerInteraction,但这些似乎主要面向动画。可能有一种更好/更简单的方法。我感觉我想多了。

我希望得到一些建议,促使我采取一种关于如何解决这个问题的方法,如果可能的话,为什么这种方法是有利的。

更新事实证明,我的问题真正归结为如何使用EventTrigger调用Command(带有CommandParameter(。关于SO有很多答案,其中很多都涉及MVVM Light。我通过使用Microsoft.Xaml.Behaviors.Wpf解决了它,现在可以看到MVVM Light可能是更好/更简单的方法。

仅从经验来看,我偏向于与第一个提出的解决方案类似的内容。

将以下命名空间添加到 xaml:

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="http://www.galasoft.ch/mvvmlight"

然后使用 EventToCommand:

<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseEnter">
<cmd:EventToCommand Command="{Binding HoverCellCommand}"  PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>

在视图模型中,你会有类似的东西...

public RelayCommand<MouseEventArgs> HoverCellCommand {get; private set;}
HoverCellCommand = new RelayCommand<MouseEventArgs>(p => 
{
//find cell siblings, etc...
});

为了将来参考我自己,这就是我在不使用MVVM Light 的情况下所做的。相反,我安装了Microsoft.Xaml.Behaviors.WpfNuGet 包来获取Behaviors的东西(在 Blend 中称为Interactivity(

首先是基本的东西:创建一个命令:

public class ChangeSiblingColorCommand : ICommand
{
private readonly Action<Cell> _action;
#pragma warning disable CS0067 // The event is never used
public event EventHandler CanExecuteChanged;
#pragma warning restore CS0067 // The event is never used
public ChangeSiblingColorCommand(Action<Cell> action)
{
_action = action;
}
public bool CanExecute(object parameter)
{
return (parameter is Cell c);
}
public void Execute(object parameter)
{
_action((Cell)parameter);
}
}

然后将该命令包含在视图模型中,以便可以将其绑定到:

public MainWindowViewModel()
{
this.ChangeSiblingColorCommand = new ChangeSiblingColorCommand(HighlightSiblings);
}
public ICommand ChangeSiblingColorCommand { get; private set; }
private void HighlightSiblings(Cell cell)
{
var siblings = cell.Siblings(Items);
foreach (Cell c in siblings)
{
c.IsSiblingHighlighted = !c.IsSiblingHighlighted;
}
}

最后,使用以下相当可怕的 XAML 在视图中挂接命令:

<ItemsControl ItemsSource="{Binding Path=Items}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label Style="{StaticResource MouseOverStyle}" Content="{Binding Mode=OneWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseEnter">
<i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType=UniformGrid,
Mode=FindAncestor},
Path=DataContext.ChangeSiblingColorCommand }"
CommandParameter="{Binding}"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeave">
<i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType=UniformGrid,
Mode=FindAncestor},
Path=DataContext.ChangeSiblingColorCommand }"
CommandParameter="{Binding}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Label>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

请务必在视图中包含此引用:

xmlns:i="http://schemas.microsoft.com/xaml/behaviors"

最新更新