Calling methods in a ViewModel from Xaml

Calling methods in a ViewModel from Xaml

Old forum URL: forums.lhotka.net/forums/t/8879.aspx


Jav posted on Tuesday, May 04, 2010

I've been chewing on the MVVMExperimentSL code for a couple of days now, and it has been quite helpful. I am now stuck trying to figure out how to call a method in a ViewModel from Xaml.

In MVVMExperimentSL I see two methods in DataListViewModel, Load() and ProcessItems2 being called from ListPage.xaml where I see this line at the top:
               <this:DataListViewModel x:Key="ListModel" />

I also see the DataContext of StackPanel being set:
               <StackPanel Grid.Column="0" Margin="10" DataContext="{Binding Source={StaticResource ListModel}}">

I then see the ListBox binding to the Model of DataListViewModel:
      <ListBox ItemsSource="{Binding Path=Model}"
               ItemTemplate="{StaticResource DataList}"
               SelectionMode="Multiple"
               Name="DataListBox"
               csla:InvokeMethod.TriggerEvent="SelectionChanged"
               csla:InvokeMethod.MethodName="ShowItem"
               csla:InvokeMethod.MethodParameter="{Binding RelativeSource={RelativeSource Self}, Path=SelectedItem}" />

Then there is this xaml for the button:
      <Button Content="Process items"
              Tag="{Binding ElementName=DataListBox}"
              csla:InvokeMethod.TriggerEvent="Click"
              csla:InvokeMethod.MethodName="ProcessItems2"
              csla:InvokeMethod.MethodParameter="{Binding ElementName=DataListBox, Path=SelectedItems}">

I am assuming that since the Binding in the StackPanel governs the actions of all controls placed therein, perhaps that should be enough.  But when I try to do this I keep getting errors.  The first few times the error complained about not finding a Method with "that name".  This must be because Method signature did not match the call. But now I am getting an error 4004

Here is my ViewModel:
                     <this:FrameViewModel x:Key="frameData" />

Here is the xaml in my button:
                    <RadioButton x:Name="btnPlan"
                                 Content="{Binding RootCategories[3].RootCategoryName}"
                                 Csla:InvokeMethod.TriggerEvent="Click"
                                 Csla:InvokeMethod.MethodName="btnPlanClick"
                                 Csla:InvokeMethod.MethodParameter="{Binding ElementName=btnPlan}"
                                 Height="16" />

Button content comes from the Model of the same ViewModel, FrameViewModel, and works just fine.  I have also tried with my Method without arguments, and by eliminating the MethodParameter line from the xaml.

Jav

Jav replied on Wednesday, May 05, 2010

I have found the answer to my problem, but have also discovered a dilemma posed by this MVVM thing.

Here's what's happening.  Much of the structure of my page is built on the fly based on info obtained from the database, i.e., the Model of my FrameViewModel.  The Button I am pushing is bound to the Model so it can get the Content.  Hence it knows nothing of the ViewModel.  When I set the DataContext of the button itself to:

DataContext

 

="{Binding Source={StaticResource frameData}}"

instead of the DataContext of the entire Grid where the button lies, i.e.:

DataContext

 

="{Binding Model, Source={StaticResource frameData}}"

then the Button Click works like a charm, but the button content disappears. With Code-Behind, the button can get the Content from one binding and the click response from another entity, the codebehind.  I suppose, I could expose all of the Model properties in the ViewModel, but that seems like a lot of work!  Would appreciate a suggestion for something a little more pleasant.

Jav

Jav replied on Wednesday, May 05, 2010

(Sorry, the one time I decided to click Post without previewing the message, it is almost unreadable.  Here is what I meant to say)

I have found the answer to my problem, but have also discovered a dilemma posed by this MVVM thing.

Here's what's happening.  Much of the structure of my page is built on the fly based on info obtained from the database, i.e., the Model of my FrameViewModel.  The Button I am pushing is bound to the Model so it can get the Content.  Hence it knows nothing of the ViewModel.  When I set the DataContext of the button itself to:
                      DataContext ="{Binding Source={StaticResource frameData}}"

instead of the DataContext of the entire Grid where the button lies, i.e.:
                      DataContext ="{Binding Model, Source={StaticResource frameData}}"

then the Button Click works like a charm, but the button content disappears. With Code-Behind, the button can get the Content from one binding and the click response from another entity, the codebehind.  I suppose, I could expose all of the Model properties in the ViewModel, but that seems like a lot of work!  Would appreciate a suggestion for something a little more pleasant.

Jav

Jav replied on Wednesday, May 05, 2010

Okay, continuing my education - there does appear to be a way.

I can set the DataContext of the overall container control to:
                                DataContext ="{Binding Source={StaticResource frameData}}"

This will set the datacontext to the ViewModel.  And then for binding the UI Controls I can use an expression that starts off with Model. followed by the actual property like:
                                Model.MySleepingBrain.LeftHemisphere

Sheesh! Can't believe I couldn't figure it out sooner!!

 

 

RockfordLhotka replied on Thursday, May 06, 2010

In 3.8 there are two (now three) controls in CSLA to help you:

In CSLA 4 I recommend the use of TriggerAction, because it is more powerful and works with the VS10 designer tools. I back-ported it to 3.8.3 because it is what I'm using in the MVVM video I'm creating.

Execute is gone in 4, because the Blend SDK has the exact same functionality - and removing it from CSLA means I can get rid of the System.Windows.Interactivity dependency (yea!).

InvokeMethod remains for backward compatibility, but it isn't as nice as TriggerAction, so I've stopped using it.

Jav replied on Thursday, May 06, 2010

Thanks Rocky.  It took some effort but I think I now have at least some comprehension of what's going on in this MVVM thing.  MVVMExperimentSL project has helped a lot.  There is a lot packed in that sample.  Subclassing ViewModel now seems somewhat clear, but I'm having difficulty visualizing situations where FrameworkElement would be the one to use other than the obvious one of when Model access is not needed.

I am using Csla 3.8.3, and in the intellisense I only see the InvokeMethod and NavigatorProvider.  InvokeMethod then offers a choice of TiggerEvent, Target, MethodName and MethodParameter.  I am back to using VS2008.  I loved using VS2010 but it kept giving me errors about WebService, even though the code would run just fine, so a few days ago I decided to postpone migration to 2010.

Jav

Copyright (c) Marimer LLC