rlcrews
rlcrews

Reputation: 3562

Silverlight how to set the datacontext of a listbox item to the root viewmodel for the view that it is in

Within my project I have a Listbox which uses a datatemplate. Within this data template I have a button. When the listbox generates results the item source for this listbox is set to some property collection let's call it Results[0].

The issue I am having is that when I click the button to call a method from the view model the method cannot be found because the call is looking to the context of the listbox and not the root view. I'm using SimpleMVVM toolkit which users a Locator similar to the MVVMLight toolkit.

One approach I took was to explicitly set the data context on the button by declaring the viewmodel in the user control resources and staticlly setting it.

<UserControl.Resources>
    <formatter:HighlightConverter x:Key="FormatConverter" />
    <vml:SearchViewModel x:Key="vm" />
</UserControl.Resources>

and then the button contains

<HyperlinkButton HorizontalAlignment="Left"
    Click="Button_Click"
    Content="{Binding Type}"
    Style="{StaticResource ListBoxtTitleHyperlink}">
 <i:Interaction.Triggers>
     <i:EventTrigger EventName="Click">
        <ei:CallMethodAction MethodName="GetDetailID" TargetObject="{Binding Source={StaticResource vm}}" />
     </i:EventTrigger>
 </i:Interaction.Triggers>
</HyperlinkButton>

This works in the fact that I can now access the method but it instantiates a new view model vs allowing me to access the root viewmodel of the view. Ergo I loose any properties that I may have had in the previous view model so I cannot pass them to the method as parameters.

My implementation may be off here. So I am open to suggestions. In scenarios such as this what is the best approach to have a button within a listbox data template call a method from the view model and pass the method parameters that are derived from the selected listbox item?

To see the full code implementation you can download the sample project from SkyDrive Folder

Update I'm starting a bounty on this question as it has me stumped. Feel free to download the sample project for reference. For clarity the intent of this question is to learn how to accomplish the following 1. Select a row from listbox 2. The selectionchanged event will set a property to the text value displayed in the UI (RecordID two way binding using Inotify 3. Click on the button within the item template and call the method stored within the ViewModel using interaction triggers and displaying in a messagebox the RecordID property value.

Steps I and 2 are done. Where I am stuck is in understanding how to get the button which is part of the listbox item template to locate the root view model and call the method of that VM without instantiating a new ViewModel that would reset all previously stored properties.

Thanks in advance

Upvotes: 1

Views: 1149

Answers (2)

Scott Munro
Scott Munro

Reputation: 13596

Add the resource programmatically. The StaticResource binding might complain at design time but at runtime it should just work.

The UserControl has a Resources property which returns a reference to a ResourceDictionary. You can add the ViewModel to this and the effect will be the same as your Xaml example except that you can reuse the existing ViewModel.

Assuming that your MVVM framework has already populated the DataContext of the UserControl with the ViewModel then you could use C# code similar to the following to set up the resource.

this.Resources.Add("vm", this.DataContext);

If the DataContext is already set within the UserControl's constructor, then it could go there. Otherwise, you will need to find a hook that is called later in the UserControl's life cycle.

Edit: Having looked at your code. I would suggest the following modifications.

  1. Do not set either the DataContext or the "vm" StaticResource in XAML.
  2. Use the following code as the constructor of the TemplateView class.

Code:

public TemplateView()
{
    var templateViewModel = new TemplateViewModel();
    this.DataContext = templateViewModel;
    this.Resources.Add("vm", templateViewModel);
    InitializeComponent();
}

There are a couple of constraints here that led me to this solution. First is that the resource must be added prior to InitializeComponent. Second is that the templateViewModel must be available before the resource can be added.

Upvotes: 3

Barracoder
Barracoder

Reputation: 3764

I'm a WPF developer and don't know for sure if this would work in Silverlight but I'd normally change the binding on your target object to something like

 <ei:CallMethodAction MethodName="GetDetailID" TargetObject="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType=ListBox, Mode=FindAncestor}}"/> 

In effect, looking back up the tree until it finds the first ancestor ListBox, and then checking it's DataContext property which, if I read your question correctly, is your ViewModel.

Upvotes: 0

Related Questions