2011-06-21 11 views
17

Pracuję nad drugą wersją aplikacji, a jako część przepisywania muszę przejść do architektury MVVM. Mam presję, aby umieścić absolutnie każdy kawałek kodu w klasie modelu widoku - mając kod C# w kodzie za plikiem jest niezadowolony. (Wiem, wiem ... Rozumiem, że kodeks nie jest zły, ale tym razem nie jest to moje wezwanie).Wiązanie zdarzeń WPF z ViewModel (dla klas innych niż Command)

Dla obiektów, które implementują interfejs poleceń, jest to łatwe. Udało mi się znaleźć mnóstwo informacji o tym, jak powiązać Command tych obiektów z ICommand w modelu widoku. Problem dotyczy obiektów, które nie mają tego interfejsu, np.

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

<!-- ... --> 

</ListBox> 

Chcę wiedzieć, w jaki sposób powiązać zdarzenie MouseDoubleClick dla listbox myCallbackFunction, który jest realizowany w modelu widoku. Czy to możliwe?

Dzięki!

+1

Duplikat pytanie http://stackoverflow.com/questions/939388/binding-commands-to-events –

Odpowiedz

8

Nie jest to bezpośrednio możliwe. Można to zrobić za pomocą dołączonej własności lub zachowania, choć znalezienie i wywołanie odpowiedniej metody byłoby trochę trudne (można to dość łatwo przeprowadzić za pomocą Reflection).

Jest to zwykle obsługiwane przez ICommand - Na przykład MVVM Light ma świetne zachowanie EventToCommand, aby zmapować dowolne zdarzenie do ICommand na ViewModel. Zaletą korzystania z ICommand jest to, że nadal można korzystać z DataBinding, ponieważ ICommand jest prezentowana jako właściwość.

1

Jednym ze sposobów może być obsłużyć zdarzenia w kodzie tyłu i wywołać odpowiednią metodę widzenia modelu z kodu za

Można też pójść do jakiegoś gotowego komendanta biblioteki jak this samouczka, który korzysta ACB

4

Aby bezpośrednio odpowiedzieć na swoje pytanie, zapoznaj się z Why to avoid the codebehind in WPF MVVM pattern? Sugeruje dwie możliwe rzeczy, które chcesz.

Dlaczego jednak chcesz powiązać MouseDoubleClick ListBox z ICommand w viewmodel?

Alternatywnym sposobem jest zapisanie metody w kodzie w celu zarejestrowania MouseDoubleClick. Nie jest źle ze względu na następujące fakty.

  1. Znaczące wiązanie danych to interakcja między widokiem a widokiemmodem. Na przykład, gdy użytkownik wprowadza jakiś tekst do TextBox, aktualizowany jest również viewmodel. Wręcz przeciwnie, jeśli viewmodel pobiera dane z bazy danych, zostanie to pokazane w widoku. Jednak nie jest tak, że ICommand w twoim modelu widoku wiąże się z widokiem.

  2. Oczywiście CanExcute w ICommand byłby ważny dla twojego viewmodelu, ale w wielu przypadkach nie jest związany z viewmodel lub nie dotyczy. W tym przypadku różnica między powiązaniem ICommand a pisaniem kodu jest taka, że ​​zdarzenie MouseDoubleClick jest powiązane z ICommand lub zarejestrowane w procedurze obsługi zdarzeń.

+0

Impreza MouseDoubleClick na ListBox był tylko przykładem, który istniał w starszym kodzie i mogę nie zachować tego konkretnego wydarzenia. Chciałbym jednak wiedzieć, czy istnieje sposób na uniknięcie kodu kryjącego się za takim wydarzeniem. A mój kod ma obecnie prostą procedurę obsługi zdarzeń w kodzie, za którym wywołuje metodę w modelu widoku. Ale z powodów politycznych w moim biurze, zostałem poinstruowany, aby znaleźć sposób, aby uniknąć pisania kodu w kodzie. – RobotNerd

+0

Jako górna część mojej odpowiedzi i @Reed Copsey również wspomniałem powyżej, są dwie możliwe rzeczy, które chcesz użyć, czyli Przywiązaną Właściwość lub Zachowanie. Są one opisane na [moje pytanie] (http://stackoverflow.com/questions/6421372/why-to-avoid-the-codebehind-in-wpf-mvvm-pattern). Jednak chcę wiedzieć, dlaczego jesteś zachęcany do unikania pisania kodu w hehind. Myślę, że odpowiedź @ slugster na [moje pytanie] (http://stackoverflow.com/questions/6421372/why-to-avoid-the-codebehind-in-wpf-mvvm-pattern) pomoże ci. –

+0

Powodem jest to, że logika biznesowa musi być całkowicie oddzielona od interfejsu HMI (pracuję z "purystami"). Próbowałem jednak rozwiązań, które łączyłeś powyżej, ale nie byłem w stanie uzyskać jeszcze żadnej pracy. Każde potencjalne rozwiązanie jest znacznie mniej eleganckie niż zwykłe użycie kodu jako pośrednika. Postanowiłem trzymać się mojego oryginalnego kodu i sprawdzić, czy uda mi się to przeforsować podczas mojej kolejnej recenzji. – RobotNerd

4

WPF obsługuje rozszerzenia znaczników zdarzeń z poziomu .NET 4.5.Korzystanie z tej możliwości, I wdrożone niezwykle wszechstronną metodę rozszerzenia wiążącą i pisał o tym tutaj:

http://www.singulink.com/CodeIndex/post/building-the-ultimate-wpf-event-method-binding-extension

Może być stosowany do wiązania się do metody z użyciem pełnej składni ścieżki nieruchomości, wspiera powiązania i inne rozszerzenia znaczników jako argumenty, i automatycznie kieruje do metody zgodnej z sygnaturą podanych argumentów. Oto kilka przykładów wykorzystania:

<!-- 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}}" /> 

Zobacz metoda modelu Podpisy:

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); 
} 
+1

To jest właściwy sposób robienia tego. – John