Passing application state between viewmodels in MVVM WPF application
Asked Answered
W

2

6

I need to write a small application to read a configuration file and generate some report with it. I was hoping to finally use MVVM but it's quite tricky to get started. Oh, I'm using Caliburn.Micro framework.

So this is what I have, a shell (primary view that hosts other views) that has a ribbon with 3 buttons on it:

1) Open file 2) Show settings 3) Show results

And two other views, SettingsView and ResultsView with buttons to generate and delete a report.

So I guess the view structure would be like this:

ShellView
   Ribbon
      OpenFileButton
      SettingsButton
      ResultsButton
   ContentControl (hosts SettingsView and ResultsView)

SettingsView
    CalculateResultsButton

ResultsView
    CancelResultsButton

The tricky part is this:

1. "Show settings" button is disabled until a file is opened (via Open file). 
2. "Show results" button is disabled until a report is calculated (via a 
    method in SettingsViewModel).
3. If a report is calculated, the CalculateResultsButton is disabled and
   CancelResultsButton is enabled and vice versa.

Please advise how could I achieve this ? I've no ideas what strategy should I go for. My non-MVVM-thinking-brain says that I should create a status variable and then somehow bind those buttons to that variable, but I guess that wont work in a MVVM world, right ? Any code example would be very very very appreciated!

Many thanks!

Weeden answered 4/4, 2012 at 17:4 Comment(0)
V
1

Since you're using CM you won't need any code-behind. You can delete the .xaml.cs files if you want.

This is a pretty basic example but it should give you an idea on how to control the state of the buttons. In this example, Open will be enabled and the other two are disabled. If you click on Open, Settings is enabled. The same happens with Results once Settings is clicked.

If you need a way to do global state the same concept can be applied by injecting a singleton, SharedViewModel, into the ViewModels and the CanXXX methods can check values in SharedViewModel. This is a SL demo of different things but one is injecting a singleton to share data, the same idea applies in wpf.

ShellView:

<Window x:Class="CMWPFGuardSample.ShellView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Grid Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0"
                    Orientation="Horizontal">
            <Button x:Name="Open"
                    Content="Open" />
            <Button x:Name="Settings"
                    Content="Settings" />
            <Button x:Name="Results"
                    Content="Results" />
        </StackPanel>
    </Grid>

</Window>

ShellViewModel:

 [Export(typeof (IShell))]
    public class ShellViewModel : PropertyChangedBase, IShell
    {
        private bool _isOpen;
        public bool IsOpen
        {
            get { return _isOpen; }
            set
            {
                _isOpen = value;
                NotifyOfPropertyChange(() => IsOpen);
                NotifyOfPropertyChange(() => CanSettings);
            }
        }

        private bool _isSettings;
        public bool IsSettings
        {
            get { return _isSettings; }
            set
            {
                _isSettings = value;
                NotifyOfPropertyChange(() => IsSettings);
                NotifyOfPropertyChange(() => CanResults);
            }
        }

        public bool IsResults { get; set; }

        public void Open()
        {
            IsOpen = true;
        }

        public bool CanSettings
        {
            get { return IsOpen; }
        }

        public void Settings()
        {
            IsSettings = true;
        }

        public bool CanResults
        {
            get { return IsSettings; }
        }

        public void Results()
        {
        }
    }
Vincevincelette answered 4/4, 2012 at 19:50 Comment(0)
M
0

MVVM and WPF Commands perfectly fits your "tricky part" requirements since have built in ICommand.CanExecute() method which allows enabling/disabling corresponding button based on custom logic.

To use this naice feature take a look first at the RoutedCommand Class and self explanatory example on MSDN How to: Enable a Command (see below code snippets).

And in general about MVVM, it is really SIMPLE! Just try it and you won't leave without it ;) In few words - you have to create for each EntityView.xaml corresponding EntityViewModel class and then just put instance of it in the View's DataContext either explicitly in code or using bindings:

var entityViewModel = new EntityViewModel();
var view = new EntityView();
view.DataContext = entityViewModel;

MVVM Command and Command.CanExecute bindings:

XAML:

<Window x:Class="WCSamples.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="CloseCommand"
    Name="RootWindow"
    >
  <Window.CommandBindings>
    <CommandBinding Command="ApplicationCommands.Close"
                    Executed="CloseCommandHandler"
                    CanExecute="CanExecuteHandler"
                    />
  </Window.CommandBindings>
  <StackPanel Name="MainStackPanel">
    <Button Command="ApplicationCommands.Close" 
            Content="Close File" />
  </StackPanel>
</Window>

C# code behind:

// Create ui elements.
StackPanel CloseCmdStackPanel = new StackPanel();
Button CloseCmdButton = new Button();
CloseCmdStackPanel.Children.Add(CloseCmdButton);

// Set Button's properties.
CloseCmdButton.Content = "Close File";
CloseCmdButton.Command = ApplicationCommands.Close;

// Create the CommandBinding.
CommandBinding CloseCommandBinding = new CommandBinding(
    ApplicationCommands.Close, CloseCommandHandler, CanExecuteHandler);

// Add the CommandBinding to the root Window.
RootWindow.CommandBindings.Add(CloseCommandBinding);
Moniliform answered 4/4, 2012 at 18:40 Comment(1)
Not ideal. Caliburn Micro avoids all the ICommand guff. caliburnmicro.codeplex.com/discussions/250844Mongrelize

© 2022 - 2024 — McMap. All rights reserved.