C# WPF将UI事件绑定到ViewModel中的命令
我正在按照MVVM对一个简单的应用程序进行重构,我的问题是如何将SelectionChanged事件从代码中移到viewModel中?我看过一些将元素绑定到命令的示例,但没有完全理解。有人能帮忙吗。谢谢 任何人都可以使用下面的代码提供解决方案吗?非常感谢强>C# WPF将UI事件绑定到ViewModel中的命令,c#,wpf,mvvm,C#,Wpf,Mvvm,我正在按照MVVM对一个简单的应用程序进行重构,我的问题是如何将SelectionChanged事件从代码中移到viewModel中?我看过一些将元素绑定到命令的示例,但没有完全理解。有人能帮忙吗。谢谢 任何人都可以使用下面的代码提供解决方案吗?非常感谢 public partial class MyAppView : Window { public MyAppView() { InitializeComponent(); this.DataC
public partial class MyAppView : Window
{
public MyAppView()
{
InitializeComponent();
this.DataContext = new MyAppViewModel ();
// Insert code required on object creation below this point.
}
private void contactsList_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
//TODO: Add event handler implementation here.
//for each selected contact get the labels and put in collection
ObservableCollection<AggregatedLabelModel> contactListLabels = new ObservableCollection<AggregatedLabelModel>();
foreach (ContactListModel contactList in contactsList.SelectedItems)
{
foreach (AggregatedLabelModel aggLabel in contactList.AggLabels)
{
contactListLabels.Add(aggLabel);
}
}
//aggregate the contactListLabels by name
ListCollectionView selectedLabelsView = new ListCollectionView(contactListLabels);
selectedLabelsView.GroupDescriptions.Add(new PropertyGroupDescription("Name"));
tagsList.ItemsSource = selectedLabelsView.Groups;
}
}
公共部分类MyAppView:窗口
{
公共MyAppView()
{
初始化组件();
this.DataContext=新的MyAppViewModel();
//在此点下方插入创建对象所需的代码。
}
私人无效联系人列表\u选择已更改(对象发送者,System.Windows.Controls.SelectionChangedEventArgs e)
{
//TODO:在此处添加事件处理程序实现。
//对于每个选定的联系人,获取标签并放入集合
ObservableCollection contactListLabels=新的ObservableCollection();
foreach(contactsList中的ContactListModel contactList.SelectedItems)
{
foreach(contactList.AggregatedLabelModel Aggregatabel中的AggregatedLabelModel Aggregatabel)
{
contactListLabels.Add(聚合标签);
}
}
//按名称聚合contactListLabels
ListCollectionView selectedLabelsView=新建ListCollectionView(contactListLabels);
SelectedLabelView.GroupDescriptions.Add(新属性GroupDescription(“名称”);
tagsList.ItemsSource=selectedLabelsView.Groups;
}
}
您的最佳选择是使用Windows.Interactivity
。使用EventTriggers
将ICommand
附加到任何RoutedEvent
这里有一篇文章可以帮助您入门:您应该使用Windows.Interactivity命名空间中的
EventTrigger
与InvokeCommandAction
结合使用。以下是一个例子:
<ListBox ...>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SelectedItemChangedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
通过执行添加引用>程序集>扩展
,可以引用System.Windows.Interactivity
完整的
i
名称空间是:xmlns:i=“clr名称空间:System.Windows.Interactivity;assembly=System.Windows.Interactivity”
这个问题也有类似的问题
我处理这个问题的方法是在ViewModel中有一个SelectedItem属性,然后将ListBox中的SelectedItem或任何东西绑定到该属性。正如@Cameron MacFarland提到的,我只需双向绑定到ViewModel上的一个属性。在属性设置器中,您可以根据需要执行任何逻辑操作,例如添加到联系人列表中 但是,我不必调用属性“SelectedItem”,因为viewModel不应该知道视图层以及它如何与其属性交互。我会叫它CurrentContact之类的
显然,除非您只是想创建命令作为练习等。要重构它,您需要改变您的思维。您将不再处理“选择更改”事件,而是将所选项目存储在viewmodel中。然后,您将使用双向数据绑定,这样当用户选择一个项目时,您的viewmodel将被更新,当您更改所选项目时,您的视图将被更新。我将遵循本节中的顶部答案 基本上,视图模型将包含所有项目的列表和选定项目的列表。然后,您可以将一个行为附加到管理所选项目列表的列表框 这样做意味着你没有任何代码隐藏,xaml相当容易理解,而且这种行为也可以在你的应用程序的其他地方重复使用
<ListBox ItemsSource="{Binding AllItems}" Demo:SelectedItems.Items="{Binding SelectedItems}" SelectionMode="Multiple" />
命令
{eb:EventBinding}(查找命令的简单命名模式)
{eb:EventBinding命令=CommandName}
命令参数
$e(事件数)
$this或$this.Property
串
有时,当需要绑定自定义usercontrol的事件时,通过交互触发器将事件绑定到命令的解决方案不起作用。 在这种情况下,可以使用自定义行为 声明绑定行为,如:
public class PageChangedBehavior
{
#region Attached property
public static ICommand PageChangedCommand(DependencyObject obj)
{
return (ICommand)obj.GetValue(PageChangedCommandProperty);
}
public static void SetPageChangedCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(PageChangedCommandProperty, value);
}
public static readonly DependencyProperty PageChangedCommandProperty =
DependencyProperty.RegisterAttached("PageChangedCommand", typeof(ICommand), typeof(PageChangedBehavior),
new PropertyMetadata(null, OnPageChanged));
#endregion
#region Attached property handler
private static void OnPageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as PageControl;
if (control != null)
{
if (e.NewValue != null)
{
control.PageChanged += PageControl_PageChanged;
}
else
{
control.PageChanged -= PageControl_PageChanged;
}
}
}
static void PageControl_PageChanged(object sender, int page)
{
ICommand command = PageChangedCommand(sender as DependencyObject);
if (command != null)
{
command.Execute(page);
}
}
#endregion
}
然后将其绑定到xaml中的命令:
<controls:PageControl
Grid.Row="2"
CurrentPage="{Binding Path=UsersSearchModel.Page,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
PerPage="{Binding Path=UsersSearchModel.PageSize,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Count="{Binding Path=UsersSearchModel.SearchResults.TotalItemCount}"
behaviors:PageChangedBehavior.PageChangedCommand="{Binding PageChangedCommand}">
</controls:PageControl>
考虑一下,它的所有者是Microsoft
,您可以在该页面中看到它
所有者是mthamil,谁能告诉我它可靠吗
Microsoft.Xaml.Behaviors.Wpf
的示例:
<UserControl ...
xmlns:behaviors="http://schemas.microsoft.com/xaml/behaviors"
...>
<Button x:Name="button">
<behaviors:Interaction.Triggers>
<behaviors:EventTrigger EventName="Click" SourceObject="{Binding ElementName=button}">
<behaviors:InvokeCommandAction Command="{Binding ClickCommand}" />
</behaviors:EventTrigger>
</behaviors:Interaction.Triggers>
</Button>
</UserControl>
这是一个使用
标记扩展的实现。尽管具有低级别的性质(这在本场景中是必需的),但XAML代码非常简单:
XAML
<SomeControl Click="{local:EventBinding EventToCommand}" CommandParameter="{local:Int32 12345}" />
我知道现在有点晚了,但是微软已经将他们的Xaml.Behaviors开源了,现在只需一个名称空间就可以更轻松地使用交互性
首先将Microsoft.Xaml.Behaviors.Wpf Nuget packge添加到项目中。
添加xmlns:behaviors=”http://schemas.microsoft.com/xaml/behaviors“将命名空间添加到您的
xaml
然后像这样使用它
<Button Width="150" Style="{DynamicResource MaterialDesignRaisedDarkButton}">
<behaviours:Interaction.Triggers>
<behaviours:EventTrigger EventName="Click">
<behaviours:InvokeCommandAction Command="{Binding OpenCommand}" PassEventArgsToCommand="True"/>
</behaviours:EventTrigger>
</behaviours:Interaction.Triggers>
Open
</Button>
OpenCommand = new RelayCommand<object>(OnOpenClicked, (o) => { return true; });
private void OnOpenClicked(object parameter)
{
Logger.Info(parameter?.GetType().Name);
}
“参数”将是路由事件对象
如果你好奇的话,还有日志
2020-12-15 11:40:36.3600 | INFO | MyApplication.ViewModels.MainWindowViewModel | RoutedEventTargets
如您所见,记录的类型名为RoutedEventArgs
在这里可以找到RelayCommand激励
PS:您可以绑定到任何控件的任何事件。就像关闭窗口的事件一样,您将得到相应的事件。我认为,作为一名相当新手的程序员,我正在努力改变思维的部分!我不同意视图模型不应该知道视图层。毕竟,它是视图的一个模型。它不应该在视图中操作对象,但这只是为了在索引中实例化它
OpenCommand = new RelayCommand<object>(OnOpenClicked, (o) => { return true; });
private void OnOpenClicked(object parameter)
{
Logger.Info(parameter?.GetType().Name);
}