4

I'm new with WPF and I was just doing a simple menu using MVVM with bindings and commands but I think I'm doing something wrong. I just want to change all the Window Content importing a new UserControl I defined, everytime I press a Menu Button. That means I want to disappear the menu and show a new content (the UserControls I mentioned before).

Well I have the main window (ControlPanel):

<Window x:Class="OfficeMenu.Views.Menu.ControlPanel"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Control Panel" Height="800" Width="800">
<Grid>

    <ContentControl Content="{Binding contentWindow}"/>

</Grid>
</Window>

This one of the UserControls that provides a Menu of buttons to the Main Window when I run the project:

<UserControl x:Class="OfficeMenu.Views.ButtonsMenu"
         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" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
    <!-- One style for each *type* of control on the window -->
    <Style TargetType="Button">
        <Setter Property="Margin" Value="5"/>
    </Style>

</UserControl.Resources>
 <Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="1*" />
        <ColumnDefinition Width="1*" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="1*" />
        <RowDefinition Height="1*" />
    </Grid.RowDefinitions>
    <Button Command="{Binding OpenUsersCommand}">BUTTON 1</Button>
    <Button Grid.Column="1">BUTTON 2</Button>
    <Button Grid.Column="1" Grid.Row="2" >BUTTON 3</Button>
    <Button Grid.Row="1">BUTTON 4</Button>
</Grid>
</UserControl>

This is the ViewModel Im using for it:

class MenuViewModel : ViewModelBase
{
    RandomModel _model;  <!-- It does nothing important -->
    private UserControl _content;

    public ICommand OpenUsersCommand { get; private set; }

    public UserControl Content
    {
        get { return _content; }
        set {
            _content = value;
            NotifyPropertyChanged("contentWindow");
        }
    }

    public MenuViewModel(RandomModel model )
    {
        this._model= model;
        OpenUsersCommand = new RelayCommand(OpenUsers,null);
    }

    private void OpenUsers()
    {
        Content = new UsersPanel();  //This is the UserControl we need to load dinamically
    }
}

In App.xaml.cs we load the main window:

public partial class App : Application
{

    private void OnStartup(object sender, StartupEventArgs e)
    {
        ControlPanel view = new ControlPanel();
        view.ShowDialog();
    }
}

Now, the controlPanel.xaml.cs:

public ControlPanel()
    {
        InitializeComponent();

        ModelRandom model = new ModelRandom(); <!-- It does nothing yet -->
        MenuViewModel viewmodel = new MenuViewModel(model);
        Content = new BottonsMenu();  <!-- We load a default UserControl when we run the program -->
        this.DataContext = viewmodel;
    }
}

Thank you very much.

Ferran
  • 105
  • 1
  • 1
  • 8
  • In Your view model there is not property "contentWindow" which you have use in binding and how OpenUsers function bind to OpenUsersCommand – Firoz Sep 08 '14 at 10:22
  • Sorry I tryied to translate my code before post it and forgot to change this. But I still have the same problem. – Ferran Sep 08 '14 at 12:13
  • I use Prism, and (if I understand what you are trying to do) you want your main window to have two `Regions`: one button region and one 'main content' region. You then want to load a particular VM into that 'main content' region based on which command was fired by a button in the button region. Very simple, and nicely handled in Prism. For more information search for MVVM navigation topics. – Mashton Sep 08 '14 at 13:06

1 Answers1

3

I think the problem is that you are trying to change the Content property of ContentControl, not the property of your ViewModel. You have connected your ViewModel to the host ControlPanel, but also you need the separate view models for user controls that will be hosted there. I've added class for user control view model and changed host's view model property name for the clarity sake. Correct your code as follows.

//host view model
class MainModel : ViewModelBase
{
    private UserControl _content;

    public MainModel() { }

    internal void SetNewContent(UserControl _content)
    {
        ContentWindow = _content;
    }

    public UserControl ContentWindow
    {
        get { return _content; }
        set
        {
            _content = value;
            OnPropertyChanged("ContentWindow");
        }
    }
}

//user contol's view model
class MenuViewModel : ViewModelBase
{
    MainModel _mainModel;
    RandomModel _model; // <!-- It does nothing important -->

    public ICommand OpenUsersCommand { get; private set; }


    public MenuViewModel(MainModel mainModel, RandomModel model )
    {
        this._mainModel = mainModel;
        this._model = model;
        OpenUsersCommand = new RelayCommand(OpenUsers, CanOpenUsers);
    }

    private void OpenUsers(object _param)
    {
        UsersPanelViewModel upmodel = new UsersPanelViewModel(_mainModel, _model);
        UsersPanel up = new UsersPanel();
        up.DataContext = upmodel;
        _mainModel.SetNewContent(up);
    }

    private bool CanOpenUsers(object _param)
    {
        return true;
    }
}

    //main window function
    public ControlPanel()
    {
        InitializeComponent();

        //create main view model for host
        MainModel mainModel = new MainModel();

        RandomModel model = new RandomModel(); //<!-- It does nothing yet -->

        //create view model for user controls
        MenuViewModel viewmodel = new MenuViewModel(mainModel, model);
        ButtonsMenu bm = new ButtonsMenu(); // <!-- We load a default UserControl when we run the program -->
        bm.DataContext = viewmodel;

        //set host's property in our user control
        mainModel.ContentWindow = bm;
        this.DataContext = mainModel;
    }

main window XAML

<Window x:Class="WpfApplication1.ControlPanel"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ContentControl Content="{Binding ContentWindow}"/>
    </Grid>
</Window>

Hope it's quite understandable.

Michael Ro
  • 62
  • 2
  • 3
    using `UserControl` in `ViewModel` is not allowed in MVVM – Sumsuddin Shojib May 03 '18 at 11:23
  • @Ultraviolet Never think this way. You say that just because somebody told you. Every time think critically and think about what is best for you despite those "rules". Don't take other's opinions as yours. Which brings to my mind - someone can even tell you that in code behind should not be a single line of code. That is bullshit. In code behind you write code that is related to view. – Ladislav Ondris Jun 17 '18 at 19:08
  • 1
    There are reasons for the existence of `MVVM` word. However, I'm not prolonging the conversation here. Thanks – Sumsuddin Shojib Jun 18 '18 at 13:19
  • @SumsuddinShojib: to say *[it] isn't allowed in M-V-VM* is nonsensical. MVVM is an architecture which one may or may not follow at various levels of compliance. One may argue the value of placing a `View/UserControl` in a ViewModel, or whether the practice is good or bad; but one may not infer what is/is not allowed except for a compiler or language specification. – IAbstract Jan 08 '23 at 19:12