MVVM Light - 用户控件作为视图

时间:2014-10-08 19:22:52

标签: c# xaml mvvm user-controls mvvm-light

我决定使用MVVM Light库来帮助设计UI。经过大量的研究和反复试验,我还没有找到我想要的答案。我已经用谷歌搜索并阅读了我能找到的每个StackOverflow问题,但是,我的问题似乎在SO上是独一无二的。

我希望设计一个带有单个窗口的UI,并使用不同的Views / UserControls填充它。我不想在UserControls中使用共享导航栏,也不想弹出多个窗口。每个View / UserControl都应绑定到自己的ViewModel,而MainWindow将绑定到MainViewModel。

示例场景 - 具有3个UserControls的MainWindow

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.

或者,另外

 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.

Etc等。

也许我的方法并不现实,但我觉得必须这样做。我不明白如何让所有这些作品在概念上发挥作用。我的欲望绝不是独一无二的。如果您认为这是重复的问题,请重定向。欢呼声。


为了让事情更容易理解,我在下面列了一些课程。首先,我的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>

如果有什么不清楚,我也可以添加Model类,但我假设你可以根据发生的事情做出推断。感谢。

2 个答案:

答案 0 :(得分:3)

所以你的方法很合理。您必须使用某些复杂的功能才能获得该功能。我不得不使用&#34; MainViewModel&#34;其中包含所有视图模型。这些视图模型的行为使得当数据上下文切换到不同的视图模型时,相应的用户控件将更改为适当的视图。 Sheridan here回答了我遵循的一个很好的例子。使用适当的数据模板连接到app.xaml,数据上下文切换将像魔术一样处理:D

我偏离Sheridan的例子(因为我不想创建一个单独的中继命令类/对象),我实际上使用mvvm light(galasoft)从我的视图模型发送消息给消息到#34; MainViewModel&#34;切换其数据上下文。可以找到使用MVVM光消息传递的一个很好的示例here。从&#34;孩子发送消息&#34;查看模型并将其注册到&#34; MainViewModel。&#34;

祝你好运!

答案 1 :(得分:1)

您是否可以使用接口来填充IChildLayout? 每个ViewModel都继承了这个接口...

public interface IChildLayout:INotifyPropertyChanged
{
    public MainWindows_ViewModel Parent;
}

在您的MainWindows ViewModel中,您可以拥有类似的内容......

属性IChildLayout,当您点击按钮时会发生变化...

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...
}

对于每个布局,您可以检索父窗口ViewModel(通过编辑&#34; Child&#34;属性来自它自己的ViewModel来切换布局非常重要...)

您将一个UserControl放入主窗口(在xaml中),内容被绑定到您的Child属性,然后每次更改您的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>

在这种情况下,您的First_ViewModel可以是:(在此示例中,我使用prism DelegateCommand绑定按钮操作...

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...

}

我希望这会对你有所帮助,我做了这样的事情来管理WPF应用程序中的不同Tab。