如何在WPF MVVM Catel应用程序中切换多个视图?

时间:2016-01-22 08:48:41

标签: c# wpf mvvm catel

在MS VS 2015 Professional中,我使用Catel作为MVVM框架开发C#WPF MVVM应用程序。我的问题是我不知道如何使用按钮在一个窗口中实现多个视图之间的切换。下面我简要介绍一下我的申请。 MainWindow有三个按钮

<catel:Window x:Class="FlowmeterConfigurator.Views.MainWindow"
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
          xmlns:catel="http://catel.codeplex.com"
          ResizeMode="CanResize">

     <catel:StackGrid x:Name="LayoutRoot">
        <catel:StackGrid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto"/>
        </catel:StackGrid.RowDefinitions>

        <ToolBar>
            <Button Name="btnConnectDisconnect" Content="Connect/Disconnect"/>
            <Button Name="btnFieldSettings" Content="Field Settings"/>
            <Button Name="btnCalibration" Content="Flowmeter Calibration"/>
        </ToolBar>
    </catel:StackGrid>
</catel:Window>

应用程序MainWindow有一个ViewModel。为简洁起见,我不在这里展示。除MainWindow外,我的应用程序还有三个视图:ConnectDisconnectView,CalibrationView和FieldSettingsView。为简洁起见,我在这里只展示了其中一个(FieldSettingsView),因为所有其他的都是以同样的方式在catel:UserControl的基础上创建的。

<catel:UserControl x:Class="FlowmeterConfigurator.Views.FieldSettingsView"
               xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
               xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
               xmlns:catel="http://catel.codeplex.com">

    <catel:StackGrid>
        <catel:StackGrid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </catel:StackGrid.RowDefinitions>
        <catel:StackGrid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </catel:StackGrid.ColumnDefinitions>

        <Label Grid.Row="0" Grid.Column="0" Content="Flowmeter Serial Number"/>
        <TextBox Name="SerialNumber" Grid.Row="0" Grid.Column="1"/>
    </catel:StackGrid>

</catel:UserControl>

这些视图中的每一个都有一个模型。我在这里只展示了其中一个模型,因为它们都以相同的方式创建。

using Catel.Data;
namespace FlowmeterConfigurator.Models
{
    /// <summary>
    /// Field Settings Model.
    /// </summary>
    public class FieldSettingsModel : SavableModelBase<FieldSettingsModel>
    {
        /// <summary>
        /// Returns flowmeter serial number.
        /// </summary>
        public string SerialNumber
        {
            get { return GetValue<string>(SerialNumberProperty); }
            set { SetValue(SerialNumberProperty, value); }
        }

    /// <summary>
    /// Register SerialNumber property.
    /// </summary>
    public static readonly PropertyData SerialNumberProperty = RegisterProperty("SerialNumber", typeof(string), null);
    }
}

这些视图中的每一个都有一个ViewModel。我在这里只展示了其中一个ViewModel,因为它们都以相同的方式创建。

using Catel;
using Catel.Data;
using Catel.MVVM;
using FlowmeterConfigurator.Models;

namespace FlowmeterConfigurator.ViewModels
{
    /// <summary>
    /// Field settings ViewModel.
    /// </summary>
    public class FieldSettingsViewModel : ViewModelBase
    {
        /// <summary>
        /// Creates a FieldSettingsViewModel instance.
        /// </summary>
        /// <param name="fieldSettingsModel">Field settings Model.</param>
        public FieldSettingsViewModel(FieldSettingsModel fieldSettingsModel)
        {
            Argument.IsNotNull(() => fieldSettingsModel);
            FieldSettings = fieldSettingsModel;
        }

        /// <summary>
        /// Returns or sets Field Settings Model.
        /// </summary>
        [Model]
        public FieldSettingsModel FieldSettings
        {
            get { return GetValue<FieldSettingsModel>(FieldSettingsProperty); }
            set { SetValue(FieldSettingsProperty, value); }
        }

        /// <summary>
        /// Here I register FieldSettings property.
        /// </summary>
        public static readonly PropertyData FieldSettingsProperty = RegisterProperty("FieldSettings", typeof(FieldSettingsModel), null);

        /// <summary>
        /// Returns or sets flowmeter serial number.
        /// </summary>
        [ViewModelToModel("FieldSettings")]
        public string SerialNumber
        {
            get { return GetValue<string>(SerialNumberProperty); }
            set { SetValue(SerialNumberProperty, value); }
        }

        /// <summary>
        /// Here I register SerialNumber property.
        /// </summary>
        public static readonly PropertyData SerialNumberProperty = RegisterProperty("SerialNumber", typeof(string), null);
    }
}

在我的应用程序加载后,必须立即显示ConnectDisconnectView。然后,用户可以使用MainWindow工具栏上的按钮随意切换视图。视图之间的切换必须采用以下方式:如果(例如)当前显示的视图是“ConnectDisconnectView”并且用户按下“字段设置”按钮,则“ConnectDisconnectView”视图必须从MainWindow中消失,并且必须出现“FieldSettingsView”视图,必须在MainWindow中显示。等等。也就是说,当按下MainWindow工具栏中的相应按钮(例如“流量计校准”)时,必须在MainWindow中显示相应的视图(CalibrationView),并且不得显示其他视图。如何在我的应用程序中实现此功能?非常感谢您的帮助。

P.S。当然,正如您所看到的那样,为了简洁和清晰起见,视图的数量和内容都会减少。在现实世界中,我的应用程序中的视图数量大约为20 - 25,并且它们必须包含复杂的图形和表格信息。

2 个答案:

答案 0 :(得分:4)

首先我向您展示xaml代码:

<catel:Window.Resources>
    <catel:ViewModelToViewConverter x:Key="ViewModelToViewConverter" />
</catel:Window.Resources>

<catel:StackGrid x:Name="LayoutRoot">
    <ContentControl Content="{Binding CurrentPage, Converter={StaticResource ViewModelToViewConverter}}" />

    <ToolBar>
    <Button Name="btnConnectDisconnect" Command={Binding Connect} Content="Connect/Disconnect"/>
        <Button Name="btnFieldSettings" Command={Binding Field} Content="Field Settings"/>
        <Button Name="btnCalibration" Command={Binding Calibration} Content="Flowmeter Calibration"/>
    </ToolBar>
</catel:StackGrid>

然后在c#代码中你需要这个:

using Catel.Data;
using Catel.MVVM;
using System.Threading.Tasks;

public class MainWindowViewModel : ViewModelBase
{
    public MainWindowViewModel()
    {
        this.Connect = new Command(HandleConnectCommand);
        this.Field = new Command(HandleFieldCommand);
        this.Calibration = new Command(HandleCalibrationCommand);

        this.CurrentPage = new ConnectViewModel();
    }

    /// <summary>
    /// Gets or sets the CurrentPage value.
    /// </summary>
    public IViewModel CurrentPage
    {
        get { return GetValue<IViewModel>(CurrentPageProperty); }
        set { SetValue(CurrentPageProperty, value); }
    }

    /// <summary>
    /// Register the CurrentPage property so it is known in the class.
    /// </summary>
    public static readonly PropertyData CurrentPageProperty = RegisterProperty("CurrentPage", typeof(IViewModel), null);

    public Command Connect { get; private set; }

    public Command Field { get; private set; }

    public Command Calibration { get; private set; }

    protected override async Task InitializeAsync()
    {
        await base.InitializeAsync();
        // TODO: subscribe to events here
    }

    protected override async Task CloseAsync()
    {
        // TODO: unsubscribe from events here
        await base.CloseAsync();
    }

    private void HandleCalibrationCommand()
    {
        this.CurrentPage = new CalibrationViewModel();
    }

    private void HandleFieldCommand()
    {
        this.CurrentPage = new FieldViewModel();
    }

    private void HandleConnectCommand()
    {
        this.CurrentPage = new ConnectViewModel();
    }
}

当您启动应用程序时,将使用数据上下文ConnectViewModel()加载CurrentPage。然后使用按钮中的命令,您可以更改另一个视图模型的日期上下文。

答案 1 :(得分:1)

解决这个问题的一种方法是使用Prism的区域。 Catel provides an extension for Prism这样您就可以激活特定区域的视图模型。