2
votes

In MS VS 2015 Professional I develop C# WPF MVVM application using Catel as MVVM framework. My problem is I don't know how to realize switching among multiple views in one window using buttons. Below I briefly describe my application. The MainWindow has three buttons

<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 has a ViewModel. For brevity I don't show it here. In addition to MainWindow there are three views in my application: ConnectDisconnectView, CalibrationView and FieldSettingsView. For brevity I show here only one of them (FieldSettingsView) because all of others are created in the same manner on the base of 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>

Each of these views has a Model. I show here only one of these Models because all of them created in the same manner.

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

Each of these views has a ViewModel. I show here only one of these ViewModels because all of them created in the same manner.

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

Directly after my application loading, ConnectDisconnectView must be displayed. And then user can switch the views at will using the buttons on MainWindow toolbar. The switching among the Views must be in the following manner: if (for example) the current displayed view is "ConnectDisconnectView" and user presses "Field Settings" button then "ConnectDisconnectView" view must disappear from MainWindow and "FieldSettingsView" view must appear and must be displayed in MainWindow. And so on. That is when pressed appropriate button in MainWindow toolbar (for example "Flowmeter Calibration") the appropriate view (CalibrationView) must be displayed in MainWindow and other views must not be displayed. How can I realize this capability in my application? Your help will be appreciate highly.

P.S. Of course as you see the number and content of Views are reduced here for brevity and clarity. In real world the number of Views in my application is about 20 - 25 and they must contain complex graphics and table information.

2
Do yourself a favor and use Catel.Fody. Then you can write Catel properties as regular properties.Geert van Horrik
Will my switching of views task be solved using Catel.Foby?user3769902

2 Answers

5
votes

First I show you xaml code:

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

Then in c# code you need this:

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

When you start your application CurrentPage is to be loaded with data context ConnectViewModel(). And then with commands from buttons you can change date context for another view model.

1
votes

One way to solve this problem is using regions from Prism. Catel provides an extension for Prism so you can activate view models in specific regions.