作为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
设置一个属性中有一个事件处理程序,并再次具有一个带有DataTrigger
的Style
。我可以在脑海中看到这项工作,但这种方法存在耦合问题。另外,我将如何传达有关悬停在网格元素上的信息?
在研究这个问题时,我遇到了EventTrigger
和Interaction
,但这些似乎主要面向动画。可能有一种更好/更简单的方法。我感觉我想多了。
我希望得到一些建议,促使我采取一种关于如何解决这个问题的方法,如果可能的话,为什么这种方法是有利的。
更新事实证明,我的问题真正归结为如何使用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.Wpf
NuGet 包来获取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"