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

本文关键字:视图 应用程序 WPF MVVM Catel | 更新日期: 2023-09-27 18:31:22

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

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

Application 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);
    }
}

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

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"并且用户按下"字段设置"按钮,则"ConnectDisconnectView"视图必须从主窗口中消失,并且必须显示"字段设置视图"视图,并且必须在主窗口中显示。等等。也就是说,当按下主窗口工具栏中的相应按钮(例如"流量计校准")时,必须在主窗口中显示相应的视图(校准视图),并且不得显示其他视图。如何在我的应用程序中实现此功能?您的帮助将不胜感激。

附言当然,如您所见,为了简洁明了,此处减少了视图的数量和内容。在现实世界中,我的应用程序中的视图数约为 20 - 25,它们必须包含复杂的图形和表格信息。

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

首先,我向您展示 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() 加载。然后,使用按钮中的命令,您可以更改另一个视图模型的日期上下文。

解决此问题的一种方法是使用棱镜中的区域。Catel 为 Prism 提供了一个扩展,因此您可以在特定区域中激活视图模型。