Привязка событий WPF к ViewModel (для некомандных классов) - PullRequest
17 голосов
/ 21 июня 2011

Я работаю над второй версией приложения, и как часть переписывания я должен перейти к архитектуре MVVM.Я получаю давление, чтобы поместить абсолютно каждый бит кода в класс модели представления - на C # в коде позади файла не одобряется.(Я знаю, я знаю ... Я понимаю, что код не является плохой вещью, но на этот раз это не мой вызов).

Для объектов, которые реализуют интерфейс команд, это легко.Мне удалось найти массу информации о том, как связать команду этих объектов с ICommand в модели представления.Проблема заключается в объектах, которые не имеют этого интерфейса, например,

<ListBox
   x:Name="myListBox"
   MouseDoubleClick="myCallbackFunction">

<!-- ... -->

</ListBox>

. Я хочу знать, как связать событие MouseDoubleClick для Listbox с myCallbackFunction, которая реализована в модели представления.Это вообще возможно?

Спасибо!

Ответы [ 4 ]

8 голосов
/ 21 июня 2011

Это напрямую невозможно.Это может быть сделано через Attached Property или Behavior, хотя все еще будет немного сложно найти и вызвать соответствующий метод (это можно сделать с помощью Reflection довольно легко).

При этом обычнообрабатывается с помощью ICommand - например, MVVM Light имеет отличное поведение EventToCommand для сопоставления любого события с ICommand в ViewModel.Преимущество использования ICommand состоит в том, что вы все еще можете использовать DataBinding, поскольку ICommand предоставляется как свойство.

4 голосов
/ 05 июля 2016

WPF поддерживает расширения разметки для событий начиная с .NET 4.5. Используя эту возможность, я реализовал невероятно универсальное расширение для привязки методов и написал об этом здесь:

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);
}
4 голосов
/ 22 июня 2011

Чтобы напрямую ответить на ваш вопрос, обратитесь к Почему следует избегать кода в шаблоне WPF MVVM? Он предлагает две возможные вещи, которые вы хотите.

Однако, почему вы хотите привязать MouseDoubleClick ListBox к вашей ICommand в viewmodel?

Альтернативный способ - написать метод в коде для регистрации MouseDoubleClick. Это не плохо из-за следующих фактов.

  1. Значимым связыванием данных является взаимодействие между представлением и моделью представления. Например, когда пользователь вводит некоторый текст в TextBox, модель представления также обновляется. Напротив, если модель представления получает данные из базы данных, она будет показана в представлении. Однако не в этом случае ICommand в вашей модели представления связывается с представлением.

  2. Конечно, CanExcute ICommand будет важен для вашей модели представления, но во многих случаях он не связан с моделью представления или не имеет значения. В этом случае разница между связыванием ICommand и написанием codebehind заключается в том, что событие MouseDoubleClick связывается с ICommand или регистрируется с помощью обработчика событий.

1 голос
/ 21 июня 2011

Одним из способов может быть обработка события в коде позади и вызов соответствующего метода модели представления из кода позади

Вы также можете перейти к некоторой готовой библиотеке команд, такой как это руководствокоторый использует ACB

...