2010-02-10 10 views
8

Używam od pewnego czasu wzorca MVVM, ale wciąż mam problemy w rzeczywistych sytuacjach. Oto kolejny: Używam polecenia i dymkuję zdarzenie, które ma być obsługiwane w ViewModelu. Jak na razie dobrze. Ale projekt, w którym używam MVVM, jest biblioteką klasową. Po uruchomieniu kodu komend, muszę mieć możliwość wysłania obiektu z powrotem do aplikacji wywołującej. Jakie są sugerowane sposoby na to?Rozkazywanie w MVVM (WPF) - jak zwrócić wartość?

W szczególności: W mojej aplikacji do wywoływania mam stronę XAML powiązaną bezpośrednio z ViewModel biblioteki, który zawiera obiekt "Thing1". Po kliknięciu przycisku wywoływana jest metoda w ViewModel (wywołaj ją jako "CopyThing1()"). Kopiuje "Thing1", aby utworzyć "Thing2". Następnie muszę wysłać "Thing2" z powrotem do aplikacji wywołującej.

Dzięki!

Odpowiedz

1

Chociaż informacja o dowodzeniu była jasna i poprawna, nie można jej było zastosować w moim przypadku, ponieważ odpowiedź wymagała by była w aplikacji wywołującej NIE używającej MVVM i nie miała to być tylko odpowiedź UI. Zbadałem Prism, ale uznałem, że jest to zbyt skomplikowane na to, czego potrzebuję w tej chwili. Skończyło się na podnoszenie i obsługi zdarzeń, jak opisano tutaj -> WPF MVVM Correct Way to Fire Event on View From ViewModel

1

Jeśli Thing2 jest inną właściwością w twoim modelu wyświetlania, możesz użyć normalnego INotifyPropertyChanged, aby poinformować UI o zmianie.

Można również użyć Prism „s EventAggregator bardziej oddzielonej od produkcji podejścia

+0

Dzięki. To było moje oryginalne myślenie, ale odpowiedź w aplikacji do nawiązywania połączeń nie będzie wyłącznie odpowiedzią interfejsu użytkownika. Nie wiedziałem więc, jak utworzyć kreację Thing2, aby rozpocząć wydarzenie, na które może odpowiedzieć osoba odpowiadająca za kod aplikacji. –

+0

Właściwie, czy wiesz o dobrym przykładzie użycia EventAggregator? Poza tym, jaka jest przewaga wersji Prism nad wersją out-of-the-box? –

+0

OK, teraz to rozumiem. Nie zdawałem sobie sprawy, skąd pochodzi Prism. Czy QuickStart to najprostszy przykład, jaki znasz? –

10

polecenia nie zwracają wartości, zmieniają stan aplikacji. W przypadku ICommand dołączonych do ViewModels jest to całkiem proste, ponieważ możesz to zrobić, po prostu mutując ViewModel po wykonaniu polecenia.

Używanie RelayCommand z Josh Smith's excellent MVVM article:

public class MyViewModel : INotifyPropertyChanged 
{ 
    private readonly ICommand mutateCommand; 
    private Thing thing; 

    public MyViewModel() 
    { 
     this.mutateCommand = new RelayCommand(this.Mutate); 
    } 

    public ICommand MutateCommand 
    { 
     get { return this.mutateCommand; } 
    } 

    public Thing Thing 
    { 
     get { return this.thing; } 
     set 
     { 
      this.thing = value; 
      // raise PropertyChanged event here... 
     } 
    } 

    private void Mutate(object parameter) 
    { 
     this.Thing = new Thing(); 
    } 
} 

Po wywołaniu myVM.MutateCommand.Execute(new object()); można uzyskać dostęp do nowej wartości myVM.Thing.

+0

Dzięki. Myślałem o tym. W rzeczywistości mój przykład nie był najlepszy.Naprawdę staram się zrobić, to mieć jeden obiekt (Thing1), do którego moja strona XAML jest powiązana, a następnie kliknąć przycisk na tej stronie i mieć drugą rzecz (Thing2 - ta sama struktura co Thing1, ale nie Thing1). . Więc mogę zadeklarować Thing1 i Thing2 w moim ViewModelu. Kiedy tworzona jest Thing2, strona powiązana musi odpowiadać za pomocą kodu, tzn. Nie jest to po prostu odpowiedź związana z interfejsem, która jest potrzebna. Nie wiem, jak uzyskać kod płatniczy, aby odpowiedzieć na to zdarzenie niezwiązane z interfejsem użytkownika. –

+0

@ml_black: Nie używaj kodu źródłowego; użyj ViewModels i pozwól, aby Widoki po prostu odzwierciedlały zmianę ViewModels. Komunikacja idzie tylko w jedną stronę. To znacznie czystsze podejście. –

+0

Zgadzam się. Ale aplikacja wywołująca nie używa ViewModels. I ten ViewModel jest przeznaczony dla więcej niż jednej aplikacji do wywoływania. Tak więc w tym przypadku informacje z Thing2 muszą zostać skonsumowane przez każdą aplikację wywołującą na swój własny sposób. Biblioteka ma być wyłącznie MVVM. –

1

Idealnym rozwiązaniem jest zdefiniowanie nowej klasy dziedziczone z ICommand następująco:

public abstract class Command2<T1, T2> : ICommand { 
    public abstract T2 Execute2(T1 parameter); 
} 

public class DelegateCommand2<T1, T2> : Command2<T1, T2> { 
    public DelegateCommand2(Func<T1, T2> execute, Predicate<T1> canExecute) { 
     _execute = execute; 
     _canExecute = canExecute; 
    } 

    public override T2 Execute2(T1 parameter) { 
     if (CanExecute(parameter) == false) 
      return default(T2); 
     else 
      return _execute((T1)parameter); 
    } 
} 

Zauważ, że Execute2 zwraca wartość podobnie jak normalna funkcja. Oto jak z niego korzystać.

private readonly ICommand _commandExample = new DelegateCommand2<int, Point3D>(
     commandExample_Executed, 
     commandExample_CanExecute 
    ); 

    public Command2<int, Point_3D> CommandExample { 
     get { 
      return (Command2<int, Point_3D>) _commandExample; 
     } 
    } 

    private static Point3D commandExample_Executed(int index) { 
     return Fun1(index); //Fun1 returns a Point_3D 
    } 

    private static bool commandExample_CanExecute(int index) { 
     return true; 
    } 

Wywołania Execute2 zamiast Execute zwrócą wartość.

Powiązane problemy