1

I am using framework Catel and I have a View with a TabControl which is being fed by an itemsource. TabContent was done with a Datatemplate and inside it has commands. One of those commands needs to open a new Window showing the content from the TabContent.

What am I trying to do? I am placing the command to open the the new Window in the model (Because from DataTemplate you are in the model context). Command is called correctly, however, I cannot refer to an ViewModel object from my Model.

I will write here a short version of my code to show better the problem.

My View is:

...
<TabControl Grid.Column="2" ItemsSource="{Binding Plots}" >
    ...
    <views:TabContent.Template>
        <DataTemplate>
            <Grid>
                ...
                    <DockPanel Grid.Column="0">
                        <ToolBarTray DockPanel.Dock="Left" Orientation="Vertical">
                            <ToolBar>
                                <Button Command="{Binding ShowAnotherWindow}">
                                    <Image Source="{StaticResource GalleryPropertyImage}" />
                                </Button>
                            </ToolBar>
                        </ToolBarTray>
                    </DockPanel>

                ...
            </Grid>
        </DataTemplate>
    </views:TabContent.Template>
</TabControl>
...

And inside my Model I have the command ShowAnotherWindow which is executed, but I cannot do something like:

CompletePlotViewModel viewModel = new CompletePlotViewModel(this);

What do you recommend me to do?

Saul Hidalgo
  • 55
  • 1
  • 7

3 Answers3

0

I would create an event on the model that the VM subscribes to. I usually have an OnPropertyChanged event (or something like that) that bubbles up.

So, the model might have something like:

public event PropertyChangedEventHandler PropertyChanged;

And the viewmodel would sign up like this:

/// <summary>
/// Set up property changed events. Call on initialisation
/// </summary>
private void SetupPropertyChanged()
{
  if (Model != null)
  {
    Model.PropertyChanged -= Model_PropertyChanged;
    Model.PropertyChanged += Model_PropertyChanged;
  }
}


public void Model_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
// do stuff here.
}

The viewmodel will have access to the model, so you can always manipulate from there. The advantage of this method is that the model does not need to know about it's parent, just have an event that the VM can sign up to.

Another example is here - https://stackoverflow.com/a/12002511/852806

NOTE: I agree with the other answers that logic and data should not be in the same later. My answer pertains more to a change on the data resulting in an effect in the VM, which can often legitimately be required.

NOTE 2: It looks like Catel might provide this sort of functionality out of the box - see propertychanged and propertychangedadvanced.

Community
  • 1
  • 1
JsAndDotNet
  • 16,260
  • 18
  • 100
  • 123
0

Your command ShowAnotherWindow definitely have to stay in the viewmodel.

In your UserControl add RelativeSource like this:

<Button Command="{Binding ShowAnotherWindow},RelativeSource={RelativeSource AncestorType={x:Type yourRootTagHere},Mode=FindAncestor}">

and replace yourRootTagHere with what you have (Window or UserControl).

Then, you'll be able to create the other viewmodel (CompletePlotViewModel) from the current viewmodel.

RazvanR
  • 412
  • 5
  • 14
0

Even though the binding context is the model in the data template, it's still very wrong to put commands inside a model. They should live in the view model.

What you can do is this:

<TabControl x:Name="tabControl" Grid.Column="2" ItemsSource="{Binding Plots}" >

Then you can use this binding:

<views:TabContent.Template>
    <DataTemplate>
        <Grid>
            ...
                <DockPanel Grid.Column="0">
                    <ToolBarTray DockPanel.Dock="Left" Orientation="Vertical">
                        <ToolBar>
                            <Button Command="{Binding ElementName=tabControl, Path=DataContext.ShowAnotherWindow}" CommandParameter="{Binding }">
                                <Image Source="{StaticResource GalleryPropertyImage}" />
                            </Button>
                        </ToolBar>
                    </ToolBarTray>
                </DockPanel>

            ...
        </Grid>
    </DataTemplate>
</views:TabContent.Template>

As you can see this now binds to the ShowAnotherWindow on the viewmodel. It passes the model as command parameter so you can use that as parameter in your command.

Geert van Horrik
  • 5,689
  • 1
  • 18
  • 32