Binding różnych widoków do ContentControl w jednym oknie WPF MVVM

0

Witam, struktura mojego programu wygląda na tą chwilę następująco:
MainView Window
HomePageView UserControl
UserPreferencesView UserControl
MainViewModel

W głównym widoku, w oknie mam trzy razy ComboBox, z których użytkownik będzie wybierał zawartość ContentControl, czyli na początek UserPreferencesView, gdzie będą jakieś opcje ustawień użytkownika, zmiana hasła itd. Poniżej w skrócie kod XAML

<Window ...

    <Window.DataContext>
        <viewmodel:MainViewModel />
    </Window.DataContext>

    <Grid>
...
            <ComboBox Grid.Column="0" Margin="10" VerticalContentAlignment="Center"
             HorizontalContentAlignment="Center"
            IsEditable="True"
            IsReadOnly="True"
            Text="Konto"
             ItemsSource="{Binding Path=AccountActions}"
             SelectedItem="{Binding SelectedAccountAction}">
            </ComboBox>
        </Grid>

        <ContentControl Grid.Row="1" Content="{Binding Content}"/>
        
    </Grid>
</Window>

Oraz MainViewModel

namespace Program.Viewmodels
{
    public class MainViewModel:INotifyPropertyChanged
    {
        public MainViewModel()
        {
            AccountActions = new ObservableCollection<string>
        {
            "Ustawienia",
        };

            content = new HomePageView();
        }


        //Menu Konto
        #region 

        public ObservableCollection<string> AccountActions { get; private set; }

        private string vm_SelectedAccountAction;

        public event PropertyChangedEventHandler PropertyChanged;

        public string SelectedAccountAction
        {
            get
            {
                return vm_SelectedAccountAction;
            }
            set
            {
                if (vm_SelectedAccountAction == value)
                    return;

                vm_SelectedAccountAction = value;
                MakeAction(vm_SelectedAccountAction);
            }
        }



        public void MakeAction(string SelectedAction)
        {
            switch (SelectedAction)
            {
                case "Ustawienia":
                    content = new UserPreferencesView();
                    break;

            }
        }

        #endregion

        private object content { get; set; }

        public object Content
        {
            get { return content; }
            set { content = value;
                NotifyPropertyChanged(Content);
            }
        }

        private void NotifyPropertyChanged(object content)
        {
            throw new NotImplementedException();
        }
    }
}

Moim celem jest aby po wybraniu opcji "Ustawienia" w ComboBox, pojawiał się widok UserPreferencesView. Niestety tak się nie dzieje, mogę zmieniać ContentControl tylko poprzez konstruktor, czyli HomePageView się ładuje. Co powinienem zmienić w kodzie?

4

Zacznij od tego że to co robisz to nie jest MVVM, ponieważ w MainViewModel używasz widoków HomePageView,UserPreferencesView, czyli wprowadziłeś zależność view modeli do widoków, która w MVVM nie jest dozwolona. Więc jak osiągnąć to co chcesz i by to było poprawne z MVVM:
każdy Twój widok powinien mieć odpowiadający VIewModel : HomePageView -> HomePageViewModel, i w sowim MainViewModelu zamiast podmieniać widoki podmieniasz viewmodele.
Dalej potrzebujemy wyświetlić odpowiedni widok (UserControl) w naszym głównym widoku w zależności od tego jak view model ustawiłeś, dokonuje się tego za pomocą DataTemplate :

<DataTemplate DataType="{x:Type vm:MyViewModelType}">
    <views:MyViewType />
</DataTemplate>

I zamiast ContentControl, można użyć chociażby ItemPresenter

Znalazłem pełny przykład Simple navigation technique in WPF using MVVM, o dziwo ciężko było znaleźć poprawny przykład przez googla :D

1

I kolejny przykład jak zrobić nawigację.
https://rachel53461.wordpress.com/2011/12/18/navigation-with-mvvm-2/

A poza tym wydaje mi się że potrzeba też bindować w dwie strony. Czyli dla Comboboxa ustawić Mode=TwoWay

0

Dzięki za bardzo pomocne odpowiedzi, zacząłem od przykładu podanego przez neves, nie rozumiem jednak co to za klasa BaseCommand?

1

Jest to klasa implementująca interfejs ICommand. Do tej klasy przekazujesz metode która ma się wykonąc np po kilknięciu buttona. W tym przykładzie odpowiednikiem jest RelayCommand
https://rachel53461.wordpress.com/2011/05/08/simplemvvmexample/

0

@szydlak: właściwie zamiast wynajdować koło na nowo lepiej skorzystać z gotowych rozwiązań vide Prism lub MVVMLight. Po co samemu bawić się w implementowanie interfejsów do komend czy PropertyChanged i takie tam, skoro to wszystko ktoś już zrobił? IMHO szkoda czasu na to.

0
grzesiek51114 napisał(a):

@szydlak: właściwie zamiast wynajdować koło na nowo lepiej skorzystać z gotowych rozwiązań vide Prism lub MVVMLight. Po co samemu bawić się w implementowanie interfejsów do komend czy PropertyChanged i takie tam, skoro to wszystko ktoś już zrobił? IMHO szkoda czasu na to.

Zgadzam się w 100%. Ale jak ktoś zaczyna przygodę z MVVM to fajnie jest pisać samemu bez żadnych frameworków żeby zobaczyć jak to działa. Ale to tylko moje osobiste zdanie.

0

Dokładnie, zaczynałem pisać przy pomocy Caliburn.Micro, ale stwierdziłem, że lepiej najpierw nauczyć się podstaw bez uproszczeń. Program działa elegancko, oczywiście zamiast Buttonów mam ComboBox, więc wywołanie komendy jest w MainViewModel, w switchu który odbiera wybrany ComboBoxItem:

...
 public void MakeAction(string SelectedAction)
{
    switch (SelectedAction)
    {
      case "Ustawienia":
      PreferencesCommand.Execute(null);
      break;
...

1 użytkowników online, w tym zalogowanych: 0, gości: 1