Question

I've been working with the MVVM model for a week or so now and I think I have a handle on what should go where now. Note the "think" in that.

I have a single ViewModel that my view (MainWindow) binds to

_ViewModel = new MainViewModel();
this.DataContext = _ViewModel;

I have a few ICommands that do work within the ViewModel and subsequently the Model, which I'm fine with.

Now I initiate a few windows from my View (MainWindow) which I do in codebehind, as it's all purely view related stuff. I am trying to replicate the ICommand setup I have in the ViewModel in the View to simplify my life, or so I thought. I have the following commands set-up:

public ICommand comInitialiseWindows { get; private set; }

private bool _windowsactive = false;
public bool WindowsActive
{
    get { return _windowsactive; }
    set { SetProperty(ref _windowsactive, value); }
}
public bool comInitialiseWindows_CAN()
{
    return !_windowsactive;
}
private void comInitialiseWindows_DO()
{
    ... Code to do the window creation, etc.
}

I have this relay command in the MainWindow code:

comInitialiseWindows = new RelayCommand(() => comInitialiseWindows_DO(), comInitialiseWindows_CAN);

If I put this in the ViewModel it works a treat apart from the window creation stuff, but as it's View related I'm not surprised.

So the problem is the code doesn't run when I click the button. I'm guessing that the XAML is bound to the ViewModel, but I can't figure a way around this without setting the Binding for each button to the MainWindow in codebehind. I had assumed that the following would work, but it doesn't:

<Button x:Name="ribbutLayoutWindows"                                     
    Command="{Binding local:comInitialiseWindows}" 
    IsEnabled="{Binding local:comInitialiseWindows_CAN}"/>

I'm pretty sure I'm just not getting something somewhere. Or I'm trying to overcomplicate matters where a normal button click would have sufficed as it's View only.

Any suggestions?

Was it helpful?

Solution

There are two possibilities:

Through the ViewModel: You could expose a Property on your ViewModel:

class MainViewModel
{
    ICommand comInitialiseWindows  {get; set;}
}

And in your MainWindow:

MainViewModel vm = this.DataContext as MainViewModel;
vm.comInitialiseWindows  = new RelayCommand(() => comInitialiseWindows_DO(), comInitialiseWindows_CAN);

XAML:

<Button x:Name="ribbutLayoutWindows" Command="{Binding comInitialiseWindows}" />

Note: you don't need to bind the IsEnabled property. WPF will handle that for you and automatically call into the CanExecute-method of your ICommand.

Through a DependencyProperty

Declare this dependecyProperty in your code-behind:

public ICommand comInitialiseWindows
{
    get { return (ICommand)GetValue(MyPropertyProperty); }
    set { SetValue(MyPropertyProperty, value); }
}

public static readonly DependencyProperty comInitialiseWindowsProperty = 
    DependencyProperty.Register("comInitialiseWindows", typeof(ICommand), typeof(MainWindow), new PropertyMetadata(null));

Assign a value in the code-behind:

comInitialiseWindows = new RelayCommand(() => comInitialiseWindows_DO(), comInitialiseWindows_CAN);

After that, you need to break out of your data-context in the XAML. First of all, give your Page a name:

<Window x:Class="Web_Media_Seeker_WPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:local="clr-namespace:Web_Media_Seeker_WPF"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        x:Name="myWindow"
        Title="MainWindow" Height="350" Width="525">

And then declare your binding as follows:

<Button x:Name="ribbutLayoutWindows" Command="{Binding comInitialiseWindows, ElementName=myWindow}" />
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top