2014-10-08 35 views
5

Postanowiłem użyć biblioteki MVVM Light, aby pomóc w zaprojektowaniu interfejsu użytkownika. Po wielu badaniach i próbach i błędach, jeszcze nie znalazłem odpowiedzi, których szukam. Mam google i czytam każde pytanie StackOverflow, jakie mogę znaleźć, jednak mój problem wydaje się być unikalny na SO.Światło MVVM - elementy sterowania użytkownika jako widoki

Chciałbym zaprojektować interfejs użytkownika za pomocą jednego okna i zapełnić go różnymi widokami/UserControls. NIE chcę mieć wspólnego paska nawigacyjnego wśród kontrolek UserControls ani nie chcę, aby wiele okien było pop-upem. Każdy View/UserControl powinien łączyć się z własnym ViewModel, a MainWindow będzie wiązał się z MainViewModel.

Przykładowy scenariusz - MainWindow z 3 UserControls

1. MainWindow populates with first UserControl which has a listbox and 3 buttons, the first button is enabled. 
2. User clicks the first button. 
3. MainWindow populates with second UserControl. 

Albo dodatkowo

2. User selects choice from a listbox, button two and three become available. 
3. User clicks second/third button. 
4. MainWindow populates with second/third UserControl. 

Itp, itd

Być może moje podejście nie jest realistyczny, ale czuję, że to musi dać. Nie rozumiem, jak sprawić, by wszystkie te elementy działały koncepcyjnie. Nie ma mowy, że moje pragnienia są wyjątkowe. Jeśli uważasz, że to powielone pytanie, przekieruj. Twoje zdrowie.


Aby ułatwić zrozumienie, dodałem kilka klas poniżej. Najpierw mój app.xaml.

<Application x:Class="Bobcat_BETA.App" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:views="clr-namespace:Bobcat_BETA.UserControls" 
      xmlns:vm="clr-namespace:Bobcat_BETA.ViewModels" 
      StartupUri="MainWindow.xaml" 
      mc:Ignorable="d"> 
    <Application.Resources> 
     <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" /> 

     <DataTemplate DataType="{x:Type vm:SavedScenariosViewModel}"> 
      <views:SavedScenariosUserControl /> 
     </DataTemplate> 
     <DataTemplate DataType="{x:Type vm:ScenarioEditorViewModel}"> 
      <views:ScenarioEditorUserControl /> 
     </DataTemplate> 
     <DataTemplate DataType="{x:Type vm:SimulatorViewModel}"> 
      <views:SimulatorUserControl /> 
     </DataTemplate> 

    </Application.Resources> 
</Application> 

MainWindow.xaml

<Window x:Class="Bobcat_BETA.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Bobcat - Version:0.00" 
    DataContext="{Binding Main, Source={StaticResource Locator}}"> 
<Grid> 
    <ContentControl Content="{Binding CurrentView}"/> 
</Grid> 

ViewModelLocator.cs

namespace Bobcat_BETA.ViewModels 
{ 

    public class ViewModelLocator 
    { 

     private static MainViewModel _main; 

     public ViewModelLocator() 
     { 
      _main = new MainViewModel(); 
     } 

     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", 
      "CA1822:MarkMembersAsStatic", 
      Justification = "This non-static member is needed for data binding purposes.")] 
     public MainViewModel Main 
     { 
      get 
      { 
       return _main; 
      } 
     } 
    } 
} 

MainViewModel.cs

namespace Bobcat_BETA.ViewModels 
{ 
    public class MainViewModel : ViewModelBase 
    { 
     private ViewModelBase _currentViewModel; 

     readonly static SavedScenariosViewModel _savedScenarioViewModel = new SavedScenariosViewModel(); 
     readonly static ScenarioEditorViewModel _scenarioEditorViewModel = new ScenarioEditorViewModel(); 
     readonly static SimulatorViewModel _simulatorViewModel = new SimulatorViewModel(); 

     public ViewModelBase CurrentViewModel 
     { 
      get 
      { 
       return _currentViewModel; 
      } 
      set 
      { 
       if (_currentViewModel == value) 
        return; 
       _currentViewModel = value; 
       RaisePropertyChanged("CurrentViewModel"); 
      } 
     } 

     public MainViewModel() 
     { 
      CurrentViewModel = MainViewModel._savedScenarioViewModel; 
      SavedScenarioViewCommand = new RelayCommand(() => ExecuteSavedScenarioViewCommand()); 
      ScenarioEditorViewCommand = new RelayCommand(() => ExecuteScenarioEidtorViewCommand()); 
      SimulatorViewCommand = new RelayCommand(() => ExecuteSimulatorViewCommand()); 
     } 

     public ICommand SavedScenarioViewCommand { get; private set; } 
     public ICommand ScenarioEditorViewCommand { get; private set; } 
     public ICommand SimulatorViewCommand { get; private set; } 

     private void ExecuteSavedScenarioViewCommand() 
     { 
      CurrentViewModel = MainViewModel._savedScenarioViewModel; 
     } 

     private void ExecuteScenarioEidtorViewCommand() 
     { 
      CurrentViewModel = MainViewModel._scenarioEditorViewModel; 
     } 

     private void ExecuteSimulatorViewCommand() 
     { 
      CurrentViewModel = MainViewModel._simulatorViewModel; 
     } 
    } 
} 

SavedScenariosViewModel.cs

namespace Bobcat_BETA.ViewModels 
{ 
    public class SavedScenariosViewModel : ViewModelBase 
    { 

     public SavedScenariosViewModel() 
     { 
     } 

     ObservableCollection<ScenarioModel> _scenarioModels = new ObservableCollection<ScenarioModel>() 
     { 
      new ScenarioModel() {Name = "Scenario 0", ID = 000, Desc = "This will describe the Scenario Model."}, 
      new ScenarioModel() {Name = "Scenario 1", ID = 001, Desc = "This will describe the Scenario Model."}, 
      new ScenarioModel() {Name = "Scenario 2", ID = 002, Desc = "This will describe the Scenario Model."}, 
      new ScenarioModel() {Name = "Scenario 3", ID = 003, Desc = "This will describe the Scenario Model."}, 
      new ScenarioModel() {Name = "Scenario 4", ID = 004, Desc = "This will describe the Scenario Model."}, 
      new ScenarioModel() {Name = "Scenario 5", ID = 005, Desc = "This will describe the Scenario Model."}, 
      new ScenarioModel() {Name = "Scenario 6", ID = 006, Desc = "This will describe the Scenario Model."}, 
      new ScenarioModel() {Name = "Scenario 7", ID = 007, Desc = "This will describe the Scenario Model."}, 
      new ScenarioModel() {Name = "Scenario 8", ID = 008, Desc = "This will describe the Scenario Model."}, 
      new ScenarioModel() {Name = "Scenario 9", ID = 009, Desc = "This will describe the Scenario Model."} 
     }; 
     public ObservableCollection<ScenarioModel> ScenarioModels 
     { 
      get { return _scenarioModels; } 
     } 

    } 
} 

SavedScenariosUserControl.xaml

<UserControl x:Class="Bobcat_BETA.UserControls.SavedScenariosUserControl" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:vm="clr-namespace:Bobcat_BETA.ViewModels" 
      mc:Ignorable="d" 
      d:DesignHeight="300" d:DesignWidth="300"> 

    <UserControl.Resources> 
     <ResourceDictionary> 
      <ResourceDictionary.MergedDictionaries> 
       <ResourceDictionary Source="/Dictionaries/MasterDictionary.xaml" /> 
      </ResourceDictionary.MergedDictionaries> 
     </ResourceDictionary> 
    </UserControl.Resources> 

    <UserControl.Style> 
     <DynamicResource ResourceKey="GeneralUserControl"/> 
    </UserControl.Style> 

    <Grid> 
     <Label Content="Saved Scenario Selection" 
       Style="{StaticResource GeneralLabel}" HorizontalAlignment="Left" Margin="26,30,0,0" VerticalAlignment="Top" Height="62" Width="345"/> 
     <Label Content="Chose Flight Model:" 
       Style="{StaticResource GeneralLabel2}" 
       HorizontalAlignment="Left" Margin="27,111,0,0" VerticalAlignment="Top" Height="43" Width="345"/> 
     <ListBox Style="{StaticResource GeneralListBox}" 
       HorizontalAlignment="Left" Height="509" Margin="27,154,0,0" VerticalAlignment="Top" Width="345" 
       ItemsSource="{Binding ScenarioModels}"/> 

     <Button Content="New" 
       Style="{StaticResource TransitionButton}" 
       HorizontalAlignment="Left" Margin="948,601,0,0" VerticalAlignment="Top" MinHeight="62" MinWidth="150" IsEnabled="True" 
       Command="{Binding ScenarioEditorViewCommand}"/> 

     <Button Content="Edit" 
       Style="{StaticResource TransitionButton}" 
       HorizontalAlignment="Left" Margin="401,519,0,0" VerticalAlignment="Top" MinHeight="62" MinWidth="150" 
       Command="{Binding SaveScenariosViewCommand}"/> 
     <Button Content="Load" 
       Style="{StaticResource TransitionButton}" 
       HorizontalAlignment="Left" Margin="401,601,0,0" VerticalAlignment="Top" MinHeight="62" MinWidth="150" 
       Command="{Binding SimulatorViewCommand}"/> 

    </Grid> 
</UserControl> 

Jeśli coś jest niejasne, mogę dodać klas modeli, jak dobrze, ale zakładam, można wnioskować z tego, co się dzieje. Dzięki.

Odpowiedz

3

Twoje podejście jest bardzo prawdopodobne.Niektóre są niezbędne do zastosowania tej funkcjonalności . Musiałem użyć "MainViewModel", który zawierał wszystkie modele widoku. Te modele widoku zachowują się tak, że gdy kontekst danych zostanie przełączony na inny model widoku, odpowiednia kontrola użytkownika zostanie zmieniona na odpowiedni widok. Dobry przykład, na którym poszedłem, został odebrany przez Sheridana here. Zajrzyj do app.xaml z odpowiednimi szablonami danych, a przełączniki kontekstu danych zostaną obsłużone jak magia: D

Gdzie oderwałem się od przykładu Sheridana (ponieważ nie chciałem tworzyć oddzielnej klasy polecenia/obiektu przekaźnika), W rzeczywistości użyłem mvvm light (galasoft) do wysyłania wiadomości z moich viewmodeli do wiadomości z powrotem do "MainViewModel", aby zmienić kontekst danych. Dobrym przykładem użycia lekkich komunikatów MVVM jest here. Wyślij wiadomość z "podrzędnego" Zobacz model i zarejestruj go w "MainViewModel."

Powodzenia!

+0

Rzeczywiście mój app.xaml jest dokładnie taki, jak pierwszy przykład, o którym wspomniałeś. Zamiast wyprowadzać moje ViewModels z BaseViewModel, czerpię z ViewModelBase z MVVM Light. Jestem trochę zagubiony na Messengerze. Jutro opublikuję wszystkie moje zajęcia dla wszystkich, dla których przyjdą. – piofusco

+0

Okay! Trochę mnie straciłeś na tym, jak czerpiesz swoją ViewModelBase z MVVM Light. Ale opublikuj swój kod, a ja popatrzę. – Stunna

+0

Dodano kod powyżej. – piofusco

1

Nie możesz użyć interfejsu na przykład IChildLayout? Każdy ViewModel dziedziczą z tego interfejsu ...

public interface IChildLayout:INotifyPropertyChanged 
{ 
    public MainWindows_ViewModel Parent; 
} 

W swoim MainWindows ViewModel można mieć coś takiego ...

Właściwość IChildLayout, który zmienia się po kliknięciu przycisków ...

public class MainWindows_ViewModel:INotifyPropertyChanged 
{ 
    public MainWindows_ViewModel() 
    { 
     //Here i set the default ViewModel 
     this.Child=new First_ViewModel(){Parent=this}; 
    } 

    private IChildLayout _child; 
    public IChildLayout Child 
    { 
     get 
     { 
      return _child; 
     } 
     set 
     { 
      _child=value; 
      _child.Parent=this; 
      NotifyPropertyChanged("Child"); 
     } 
    } 
    #region INotifyPropertyChangedMembers... 
} 

Dla każdego układu można pobrać okno nadrzędne ViewModel (ważne jest, aby zmienić układ, edytując właściwość "Dziecko" z własnego programu ViewModel ...)

Umieszczasz kontrolkę UserControl w głównym oknie (w języku xaml), zawartość jest powiązana z twoją własnością Child, wtedy będzie odświeżana za każdym razem, gdy zmienisz swoją własność Child.

<Windows> 
    <Windows.DataContext> 
     <local:MainWindows_ViewModel/> 
    </Windows.DataContext> 
    <UserControl Content={Binding Child}> 
     <UserControl.Resources> 
      <DataTemplate DataType="{x:Type ViewModels:First_ViewModel}"> 
        <Controls:First_View DataContext="{Binding}"/> 
      </DataTemplate> 
      <DataTemplate DataType="{x:Type ViewModels:Second_ViewModel}"> 
        <Controls:Second_View DataContext="{Binding}" /> 
      </DataTemplate> 
     </UserControl.Resources> 
    </UserControl> 
</Windows> 

W tym przypadku, twój First_ViewModel może być: (W tym przykładzie używam pryzmat DelegateCommand powiązać działania przycisków ...

public class First_ViewModel:IChildLayout 
{ 
public MainWindows_ViewModel Parent {get;set;} 

public ICommand cmdBtn1click{get;set;} 
private Pass_to_second_ViewModel() 
{ 
    //Here i change the Parent Child Property, it will switch to Second_View.xaml... 
    this.Parent.Child=new Second_ViewModel(); 
} 

public First_ViewModel() 
{ 
    // Here i connect the button to the command with Prism... 
    this.cmdBtn1click=new DelegateCommand(()=>Pass_to_second_ViewModel()); 

} 

#region INotifyPropertyChangedMembers... 

}

Mam nadzieję, że to pomoże Zrobiłem to, aby zarządzać różnymi zakładkami w aplikacji WPF

+0

To wygląda logicznie, więc zamierzam zrobić wszystko, co w mojej mocy, aby to zadziałało. Wszelkie dodatkowe przykłady kodu byłyby mile widziane. Dzięki. – piofusco