我正在使用应用程序的第二个版本,作为重写的一部分,我必须转向MVVM架构。我正面临压力,要在视图模型类中放置绝对的所有代码 - 在代码隐藏文件中使用c#是不受欢迎的。 (我知道,我知道......我知道背后的代码不是坏事,但这次不是我的电话。)
对于实现命令界面的对象,很容易。我已经能够找到大量关于如何将这些对象的Command绑定到视图模型中的ICommand的信息。问题出在没有这种接口的对象上,例如
<ListBox
x:Name="myListBox"
MouseDoubleClick="myCallbackFunction">
<!-- ... -->
</ListBox>
我想知道如何将Listbox的MouseDoubleClick事件绑定到myCallbackFunction,这是在视图模型中实现的。这甚至可能吗?
谢谢!
答案 0 :(得分:8)
这不是直接可行的。它可以通过附加属性或行为来完成,但找到并调用适当的方法仍然有点棘手(这可以通过Reflection很容易地完成)。
话虽这么说,这通常是通过ICommand
处理的 - 例如,MVVM Light有一个很好的EventToCommand行为,可以将任何事件映射到ViewModel上的ICommand。使用ICommand的优点是您仍然可以使用DataBinding,因为ICommand是作为属性公开的。
答案 1 :(得分:4)
要直接回答您的问题,请参阅Why to avoid the codebehind in WPF MVVM pattern?它提出了您想要的两件事。
但是,为什么要将ListBox的MouseDoubleClick绑定到viewmodel中的ICommand?
另一种方法是在代码隐藏中编写一个方法来注册MouseDoubleClick。由于以下事实,这并不错。
有意义的数据绑定是视图和视图模型之间的交互。例如,当用户向TextBox输入一些文本时,也会更新视图模型。相反,如果视图模型从数据库获取数据,它将在视图中显示。但是,视图模型中的ICommand不会绑定到视图。
当然,ICommand的CanExcute对你的viewmodel很重要,但在很多情况下,它与viewmodel无关或不关心。在这种情况下,ICommand绑定和编写代码隐藏之间的区别在于MouseDoubleClick事件与ICommand绑定或使用事件处理程序注册。
答案 2 :(得分:4)
从.NET 4.5开始,WPF支持事件的标记扩展。使用该功能,我实现了一个非常通用的方法绑定扩展,并在此处写了:
http://www.singulink.com/CodeIndex/post/building-the-ultimate-wpf-event-method-binding-extension
可用于使用完整属性路径语法绑定到方法,支持绑定和其他标记扩展作为参数,并自动路由到与提供的参数的签名匹配的方法。以下是一些使用示例:
<!-- Basic usage -->
<Button Click="{data:MethodBinding OpenFromFile}" Content="Open" />
<!-- Pass in a binding as a method argument -->
<Button Click="{data:MethodBinding Save, {Binding CurrentItem}}" Content="Save" />
<!-- Another example of a binding, but this time to a property on another element -->
<ComboBox x:Name="ExistingItems" ItemsSource="{Binding ExistingItems}" />
<Button Click="{data:MethodBinding Edit, {Binding SelectedItem, ElementName=ExistingItems}}" />
<!-- Pass in a hard-coded method argument, XAML string automatically converted to the proper type -->
<ToggleButton Checked="{data:MethodBinding SetWebServiceState, True}"
Content="Web Service"
Unchecked="{data:MethodBinding SetWebServiceState, False}" />
<!-- Pass in sender, and match method signature automatically -->
<Canvas PreviewMouseDown="{data:MethodBinding SetCurrentElement, {data:EventSender}, ThrowOnMethodMissing=False}">
<controls:DesignerElementTypeA />
<controls:DesignerElementTypeB />
<controls:DesignerElementTypeC />
</Canvas>
<!-- Pass in EventArgs -->
<Canvas MouseDown="{data:MethodBinding StartDrawing, {data:EventArgs}}"
MouseMove="{data:MethodBinding AddDrawingPoint, {data:EventArgs}}"
MouseUp="{data:MethodBinding EndDrawing, {data:EventArgs}}" />
<!-- Support binding to methods further in a property path -->
<Button Content="SaveDocument" Click="{data:MethodBinding CurrentDocument.DocumentService.Save, {Binding CurrentDocument}}" />
查看模型方法签名:
public void OpenFromFile();
public void Save(DocumentModel model);
public void Edit(DocumentModel model);
public void SetWebServiceState(bool state);
public void SetCurrentElement(DesignerElementTypeA element);
public void SetCurrentElement(DesignerElementTypeB element);
public void SetCurrentElement(DesignerElementTypeC element);
public void StartDrawing(MouseEventArgs e);
public void AddDrawingPoint(MouseEventArgs e);
public void EndDrawing(MouseEventArgs e);
public class Document
{
// Fetches the document service for handling this document
public DocumentService DocumentService { get; }
}
public class DocumentService
{
public void Save(Document document);
}
答案 3 :(得分:1)
答案 4 :(得分:0)
尝试使用EventBinder,它将使您可以将方法直接绑定到任何事件,包括您自己的事件,而无需将方法包装在ICommand容器中。
https://github.com/Serg046/EventBinder
https://www.nuget.org/packages/EventBinder
支持.NET Framework 3.0 +和.NET Core 3.0 +
功能:
.
分隔符,属性和字段绑定到嵌套对象
支持$
符号和位置编号($0
,$1
,
等){Binding}
作为参数用法:
public class ViewModel
{
public MetadataViewModel Metadata { get; } = new MetadataViewModel();
public async Task ShowMessage(string msg, decimal centenary, double year)
{
await Task.Delay(0);
MessageBox.Show(msg + centenary + year);
}
public class MetadataViewModel
{
public void ShowInfo(Window window, double windowWidth, ViewModel viewModel, object sender, MouseButtonEventArgs eventArgs)
{
var sb = new StringBuilder("Window width: ")
.AppendLine(windowWidth.ToString())
.Append("View model type: ").AppendLine(viewModel.GetType().Name)
.Append("Sender type: ").AppendLine(sender.GetType().Name)
.Append("Clicked button: ").AppendLine(eventArgs.ChangedButton.ToString())
.Append("Mouse X: ").AppendLine(eventArgs.GetPosition(window).X.ToString())
.Append("Mouse Y: ").AppendLine(eventArgs.GetPosition(window).Y.ToString());
MessageBox.Show(sb.ToString());
}
}
}
绑定:
<Window xmlns:e="clr-namespace:EventBinder;assembly=EventBinder" Name="Wnd">
<Rectangle Fill="LightGray" Name="Rct"
MouseLeftButtonDown="{e:EventBinding ShowMessage, `Happy `, 20m, 20.0 }"
MouseRightButtonDown="{e:EventBinding Metadata.ShowInfo, {Binding ElementName=Wnd},
{Binding ElementName=Wnd, Path=ActualWidth}, {Binding}, $0, $1 }" />
</Window>
或
EventBinding.Bind(Rct, nameof(Rct.MouseLeftButtonDown),
nameof(ViewModel.ShowMessage),
"`Happy `", 20m, 20.0);
EventBinding.Bind(Rct, nameof(Rct.MouseRightButtonDown),
nameof(ViewModel.Metadata) + "." + nameof(ViewModel.Metadata.ShowInfo),
new Binding { ElementName = nameof(Wnd)},
new Binding("ActualWidth") { ElementName = nameof(Wnd) },
new Binding(),
"$0", "$1");