WPF中, 如何将控件的触发事件绑定到ViewModel
在DataGrid 等控件中, 有很多这种带闪电符号的触发事件. 如果用传统的事件驱动, 则直接在后台中建立 一个private PropertyChanged(Sender s, EventAgars Args) 即可. 但是如果需要绑定到ViewModel的话? 应该怎么做?带闪电符号的触发事件实现viewModel绑定前端触发事件的写法:<DataGridx:Name="myDataG
在DataGrid 等控件中, 有很多这种带闪电符号的触发事件. 如果用传统的事件驱动, 则直接在后台中建立 一个private PropertyChanged(Sender s, EventAgars Args) 即可. 但是如果需要绑定到ViewModel的话? 应该怎么做?
带闪电符号的触发事件
实现viewModel绑定前端触发事件的写法:
<DataGrid
x:Name="myDataGrid"
AlternationCount="2"
AutoGenerateColumns="False"
FontSize="24"
ItemsSource="{Binding Students}"
SelectedItem="{Binding SelectStudent}"
SelectionMode="Extended">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding DataGridSelectedCommand}" CommandParameter="{Binding ElementName=myDataGrid, Path=SelectedItems}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="BorderThickness" Value="0,0,0,3" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="Margin" Value="0" />
<Setter Property="MinHeight" Value="25" />
<Setter Property="MinWidth" Value="0" />
<Setter Property="Background" Value="#2B2C31" />
<Setter Property="SnapsToDevicePixels" Value="True" />
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridCell">
<Grid Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.CellStyle>
<DataGrid.Columns>
<DataGridTextColumn
Width="*"
Binding="{Binding Id}"
Header="Id" />
<DataGridTextColumn
Width="*"
Binding="{Binding Age}"
Header="年龄" />
<DataGridTextColumn
Width="*"
Binding="{Binding Name}"
Header="姓名" />
<DataGridTemplateColumn Header="操作">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="5"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Background="White"
FontFamily="{StaticResource fontAwesome}"
FontSize="24"
Tag="修改"
Text="">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseUp">
<i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=DataContext.DataGridUpDateCommand}" CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Foreground" Value="Orange" />
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Foreground" Value="Black" />
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock
Margin="5"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Background="White"
FontFamily="{StaticResource fontAwesome}"
Tag="删除"
Text="">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseUp">
<i:InvokeCommandAction Command="{Binding DataContext.DataGridDeleteCommand, RelativeSource={RelativeSource AncestorType=UserControl}}" CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Foreground" Value="Red" />
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Foreground" Value="Black" />
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
分析核心代码:
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding DataGridSelectedCommand}" CommandParameter="{Binding ElementName=myDataGrid, Path=SelectedItems}" />
</i:EventTrigger>
</i:Interaction.Triggers>
i的命名空间 : xmlns:i=“http://schemas.microsoft.com/xaml/behaviors”
使用触发器, 事件触发器, 将前端的触发事件写在EventName中 SelectionChanged. 然后把事件绑定到后台, 将多选的Student 以CommandParameter的形式传入后端
后端代码:
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using MathNet.Numerics.Distributions;
using NavTest.Eneities;
using SqlSugar;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NavTest.ViewModels
{
public partial class Page3ViewModel:ObservableObject
{
[ObservableProperty]
private ObservableCollection<Student> students = new();
public Page3ViewModel()
{
for (int i = 0; i < 15; i++)
{
Students.Add(
new()
{
Id = i + 1,
Name = $"StudentName{i}",
Age = $"{i}+10",
Description = $"str+{i}"
}
);
}
}
[RelayCommand]
public void DataGridDelete(Student student)
{
}
//[RelayCommand]
//public void DataGridUpDate(Student student)
//{
//}
public RelayCommand<Student> DataGridUpDateCommand => new RelayCommand<Student>((arg) =>
{
});
[RelayCommand]
public void DataGridSelected(IList<object> objs)
{
MyStudents = new();
foreach (var item in objs)
{
if (item is Student stu)
{
MyStudents.Add(stu);
}
}
Student stu1 = SelectStudent;
}
[RelayCommand]
public void ItemControlCmd(Student student)
{
}
[ObservableProperty]
private ObservableCollection<Student> myStudents = new ();
[ObservableProperty]
private Student selectStudent = new();
}
}
完整的前端代码:
<UserControl
x:Class="NavTest.Views.Page3"
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:hc="https://handyorg.github.io/handycontrol"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:local="clr-namespace:NavTest.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mv="clr-namespace:NavTest.ViewModels"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:tt="clr-namespace:NavTest.Eneities"
xmlns:vc="clr-namespace:NavTest.Components"
d:DataContext="{d:DesignInstance mv:Page3ViewModel}"
d:DesignHeight="450"
d:DesignWidth="800"
FontSize="24"
mc:Ignorable="d">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<DataGrid
x:Name="myDataGrid"
AlternationCount="2"
AutoGenerateColumns="False"
FontSize="24"
ItemsSource="{Binding Students}"
SelectedItem="{Binding SelectStudent}"
SelectionMode="Extended">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding DataGridSelectedCommand}" CommandParameter="{Binding ElementName=myDataGrid, Path=SelectedItems}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="BorderThickness" Value="0,0,0,3" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="Margin" Value="0" />
<Setter Property="MinHeight" Value="25" />
<Setter Property="MinWidth" Value="0" />
<Setter Property="Background" Value="#2B2C31" />
<Setter Property="SnapsToDevicePixels" Value="True" />
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridCell">
<Grid Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.CellStyle>
<DataGrid.Columns>
<DataGridTextColumn
Width="*"
Binding="{Binding Id}"
Header="Id" />
<DataGridTextColumn
Width="*"
Binding="{Binding Age}"
Header="年龄" />
<DataGridTextColumn
Width="*"
Binding="{Binding Name}"
Header="姓名" />
<DataGridTemplateColumn Header="操作">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="5"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Background="White"
FontFamily="{StaticResource fontAwesome}"
FontSize="24"
Tag="修改"
Text="">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseUp">
<i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=DataContext.DataGridUpDateCommand}" CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Foreground" Value="Orange" />
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Foreground" Value="Black" />
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock
Margin="5"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Background="White"
FontFamily="{StaticResource fontAwesome}"
Tag="删除"
Text="">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseUp">
<i:InvokeCommandAction Command="{Binding DataContext.DataGridDeleteCommand, RelativeSource={RelativeSource AncestorType=UserControl}}" CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Foreground" Value="Red" />
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Foreground" Value="Black" />
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<WrapPanel
Grid.Row="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<TextBlock
Margin="5,5,5,5"
Foreground="White"
Text="测试1" />
<TextBlock
Margin="5,5,5,5"
Foreground="White"
Text="测试2" />
<TextBlock
Margin="5,5,5,5"
Foreground="White"
Text="测试3" />
<TextBlock
Margin="5,5,5,5"
Foreground="White"
Text="测试4" />
<TextBlock
Margin="5,5,5,5"
Foreground="White"
Text="测试5" />
<TextBlock
Margin="5,5,5,5"
Foreground="White"
Text="测试6" />
</WrapPanel>
<Grid Grid.Row="1" Grid.Column="1">
<ItemsControl AlternationCount="2" ItemsSource="{Binding MyStudents}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border x:Name="border" Padding="2">
<StackPanel>
<TextBlock Foreground="White" Text="{Binding Name}" />
<TextBlock Foreground="White" Text="{Binding Age}" />
<Button
Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=DataContext.ItemControlCmdCommand}"
CommandParameter="{Binding}"
Content="{Binding Description}"
Foreground="White" />
</StackPanel>
</Border>
<DataTemplate.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
<Setter TargetName="border" Property="Background" Value="red" />
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Grid>
</UserControl>
如果在一个有ItemSource的控件内找不到 后台的Property 和command, 可以尝试 binding ElementName 和binding Relationsource
例如command 可以 binding Relationsource = {RelationSource AncestorType = window} path = datacontext. xxxcommand
commandparameter={binding} 返回单个class为数据源
CommandParameter=“{Binding ElementName=myDataGrid, Path=SelectedItems}” 绑定别的控件上的属性 或者 用child的方式
一些补充的内容:
除了BooleanToVisibilityConverter之外,WPF框架还提供了许多其他的内置转换器。以下是一些常用的系统现有转换器的示例:
BooleanToVisibilityConverter: 将bool值转换为Visibility枚举值。
InverseBooleanConverter: 反转bool值,将true转换为false,将false转换为true。
StringFormatConverter: 格式化字符串,可以将值与指定的格式字符串进行组合。
DateTimeConverter: 将DateTime对象转换为不同格式的字符串,或者将字符串转换为DateTime对象。
BrushConverter: 将字符串表示的颜色转换为Brush对象。
ValueConverterGroup: 将多个转换器组合成一个组,按照顺序依次进行转换。
EnumToStringConverter: 将枚举值转换为对应的字符串表示。
NumericUpDownConverter: 用于增加和减少数值类型的转换器。
CollectionViewSource: 用于在集合和视图之间进行转换和筛选。
这只是一些常见的示例,WPF框架提供了更多的内置转换器,可以满足各种转换需求。你可以根据具体的场景和需求选择合适的转换器来使用。
eventTrriger
DataTrigger
常用触发器:
CallMethodAction
ChangePropertyAction
InvokeCommandAction
CallMethodAction 和 InvokeCommandAction 是在 WPF 中用于触发操作的两种不同方式,它们有一些区别,并且在特定的情况下可能不可互相替代。
CallMethodAction:CallMethodAction 允许你直接调用指定的方法。你可以通过设置 TargetObject 属性指定要调用方法的对象,并使用 MethodName 属性指定要调用的方法名称。这种方式适用于简单的、特定于 UI 的操作,例如在按钮点击或其他事件发生时执行某个方法。它可以方便地将事件触发与方法调用关联起来,但缺点是它与 UI 逻辑紧耦合,并且无法利用 WPF 中的命令系统。
InvokeCommandAction:InvokeCommandAction 允许你通过绑定一个命令来执行操作。你可以使用 Command 属性绑定到一个实现了 ICommand 接口的命令对象。当触发与行为关联的事件时,命令的 Execute 方法将被调用,而命令的 CanExecute 方法决定是否可以执行。这种方式更符合 MVVM 架构和解耦原则,它将 UI 逻辑与业务逻辑分离,并提供了更好的可测试性和可重用性。
虽然 CallMethodAction 和 InvokeCommandAction 实现了类似的功能,但在大多数情况下,推荐使用 InvokeCommandAction 和命令模式来处理用户交互。它更符合 MVVM 设计模式的理念,并且提供了更好的灵活性和可扩展性。但在一些简单的场景下,CallMethodAction 也可以作为一种快速临时解决方案。
因此,根据具体的需求和架构设计,你可以选择使用 CallMethodAction 或 InvokeCommandAction 来触发操作。
在 WPF 中,CallMethodAction 是一种交互行为(Interaction Behavior),它允许你通过 XAML 触发调用特定方法。它是 System.Windows.Interactivity 命名空间下的一个类,需要通过添加对 System.Windows.Interactivity 程序集的引用才能使用。
CallMethodAction 可以用于任何具有无参数的方法。它与事件触发器(EventTrigger)一起使用,当特定事件发生时,将触发并调用绑定的方法。
以下是使用 CallMethodAction 的示例:
CallMethodAction :===============
<StackPanel>
<Button Content="Click Me">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:CallMethodAction TargetObject="{Binding}" MethodName="HandleButtonClick" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</StackPanel>
关闭窗口,用到window的close方法:
<Button
Background="Red"
Content=""
Style="{StaticResource ControlButtonStyle}"
ToolTip="Close">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:CallMethodAction MethodName="Close" TargetObject="{Binding RelativeSource={RelativeSource AncestorType=Window}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
ChangePropertyAction=============
例如:TargetObject="{Binding} 返回了DataContext,就是ViewModel,它的PropertyName就是里面的属性IsMarker
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:ChangePropertyAction PropertyName="IsMarker" Value="True" TargetObject="{Binding}"/>
</i:EventTrigger>
<i:EventTrigger EventName="Closing">
<i:ChangePropertyAction PropertyName="IsMarker" Value="False" TargetObject="{Binding}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
最小化,用到window的Minimized属性:
<Button
Background="#22FFFFFF"
Content=""
Style="{StaticResource ControlButtonStyle}"
ToolTip="Minimize">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:ChangePropertyAction
PropertyName="WindowState"
TargetObject="{Binding RelativeSource={RelativeSource AncestorType=Window}}"
Value="Minimized" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button Content="Change Background">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:ChangePropertyAction TargetObject="{Binding ElementName=MyBorder}"
PropertyName="Background"
Value="Red" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Border x:Name="MyBorder" Width="100" Height="100" Background="Green" />
<Button Content="Change Background">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:ChangePropertyAction TargetObject="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"
PropertyName="Background"
Value="Red" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
=============
<RadioButton
Width="200"
Height="50"
Content="连接PLC"
FontSize="18"
Foreground="White"
Style="{DynamicResource RadioButtonMenuStyle}"
Tag="">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<i:InvokeCommandAction Command="{Binding ConnCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=RadioButton}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</RadioButton>
============
在 <i:EventTrigger> 元素中,可以使用不同的 EventName 属性来指定常见事件的名称,以触发相应的动作或触发器。以下是一些常用的 EventName 值示例:
Loaded:当元素加载完成时触发。
Unloaded:当元素卸载时触发。
MouseEnter:当鼠标指针进入元素时触发。
MouseLeave:当鼠标指针离开元素时触发。
MouseDown:当鼠标按下按钮时触发。
MouseUp:当鼠标释放按钮时触发。
Click:当元素被点击时触发。
Checked:当复选框或单选按钮的选中状态改变时触发。
TextChanged:当文本框的文本内容改变时触发。
PreviewMouseMove:鼠标在元素上移动。
PreviewMouseUp:鼠标释放按钮。
MouseEnter:鼠标进入元素。
MouseLeave:鼠标离开元素。
PreviewMouseWheel:滚动鼠标滚轮。
键盘事件:
PreviewKeyDown:按下键盘上的键。
PreviewKeyUp:释放键盘上的键。
KeyDown:按下键盘上的键(冒泡事件)。
KeyUp:释放键盘上的键(冒泡事件)。
SelectionChanged:当下拉列表、列表框或其他选择控件的选择项发生改变时触发。
这只是一些常见的 EventName 值示例,实际上,可以根据具体的控件和需求,选择适合的事件名称。根据控件的类型和事件的定义,可以在相关文档或控件的事件文档中找到更多可用的 EventName 值。
<Button Content="Toggle Size" Width="100" Height="30">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:Interaction.Behaviors>
<ei:ChangePropertyAction TargetObject="{Binding RelativeSource={RelativeSource AncestorType=Window}}"
PropertyName="WindowState">
<ei:ChangePropertyAction.Value>
<System:WindowState>
<System:WindowState x:FactoryMethod="FromValue">
<System:WindowState.Normal />
<System:WindowState.Maximized />
</System:WindowState>
</System:WindowState>
</ei:ChangePropertyAction.Value>
</ei:ChangePropertyAction>
</i:Interaction.Behaviors>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
========i:DataTrigger========
{Binding RelativeSource = {RelativeSource Self}
{Binding RelativeSource = {RelativeSource AncestorType=Button}}
{Binding ElementName=TestTBlock, Path=Name}
<Button Content="Click Me">
<i:Interaction.Triggers>
<i:DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path = IsMouseOver}" Value="False">
<i:ChangePropertyAction TargetObject="{Binding RelativeSource={RelativeSource Self}}"
PropertyName="BackGround"
Value="Red" />
</i:DataTrigger>
</i:Interaction.Triggers>
</Button>
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)