Neat Hack?: DataContext for Nested Views

Jan 4, 2010 at 6:23 PM

I have a bunch of Views (UserContorls) all bound to the same VM class (but different instances).

But there are a number of interface controls (sliders, text boxes, buttons, radioButtons) that are common to all these views.

I placed them in a UserControl so that now each view looks like:

UserControl /Grid / (ControlSubView, DisplaySubView)

The issue is that the DataContext is set statically at the UserControl Level

<UserControl.DataContext>
        <Binding Path="EventLens" Source="{StaticResource Locator}"/>
    </UserControl.DataContext>

Now I want tthe ControlSubView to also get that Context and share it. So that the Controls look like they are part of the View, but are just packaged in a UserControl.

I did something cute, on Load I grab my parents DataContext and set mine context to that:

 

public partial class ViewPanelControl : UserControl
    {
        public ViewPanelControl()
        {
            InitializeComponent();
            this.Loaded += setup;
        }

        private void setup(object sender, RoutedEventArgs e)
        {
            UserControl dad = ViewModelLocator.FindAncestor<UserControl>(this);
            this.DataContext = dad.DataContext;
        }
    }

 

As you can see, I extended the ViewModelLocator to have a method that would find my visual tree ancestor:

public static T FindAncestor<T>(DependencyObject dependencyObject)
            where T : class
        {
            DependencyObject target = dependencyObject;
            do
            {
                target = VisualTreeHelper.GetParent(target);
            }
            while (target != null && !(target is T));
            return target as T;
        }

Now I can use the new EventToCommand behavior and add events directly to the SubView.

     <StackPanel>
            <RadioButton x:Name="EventDataButton"
                         Content="Event Data"
                         Foreground="Yellow"
                         GroupName="DataLensContent"
                         Margin="0,5"
                         IsChecked="True">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="Checked">
                        <gala:EventToCommand
                    CommandParameterValue="event"
                    Command="{Binding ChangeDisplayData}"/>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </RadioButton>
            <RadioButton x:Name="ClusterDataButton"
                         Content="Cluster Data"
                         Foreground="Yellow"
                         GroupName="DataLensContent"
                         Margin="0,5">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="Checked">
                        <gala:EventToCommand
                            CommandParameterValue="cluster"
                            Command="{Binding ChangeDisplayData}"/>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </RadioButton>
        </StackPanel>

 

The only problem is that this is not BLENDABLE. I suspect the problem is that we need a way to extend the Locator so that FindAncestor is not a static method but a property that is computed.

Laurant, any ideas on how to do this?

 

 

So that I can say something like:

<UserControl.DataContext>
        <Binding Path="MyParentsVM" Source="{StaticResource Locator}"/>
    </UserControl.DataContext>

 

 

 

Jan 5, 2010 at 2:17 PM

My blunder.

Some experiments have shown that the DataContext is inherited by subUserControls. One does not to set it programmtically. The problem seems to be more with Blend, it cannot know what the subUserControl is embedded in, so I cannot display the objects in the DataContext (it thinks it is blank, until it is embedded).

 

I am still looking for some magic identifiers to use in the ViewModelLocator. For example, I think it would be clever if to have do something if

the Locator could keep a Dictionary of active VM models, and allow new dynamic views to either create a new VM or attach to the existing one.

 

<Binding Path="VMClass.MyID" Source={StaticResource Locator)" and have the Locator create a new VM of class VMCLass and save it in the VMDict["myID"] if someone needed to find that VM later, or create another view that shares that VM

then it would fetch the old one.

 

 

Coordinator
Jan 5, 2010 at 2:39 PM

Hi,

Sorry for not getting back to you sooner. As I told you on email, time is a bit tight in the moment.

Your findings are correct: The DataContext is inherited to the children UserControls. However, when you edit the UserControl itself, the DataContext is empty (because the UserControl is not contained in its parent, thus no inheritance.

One way to solve this issue is to use d:DataContext instead, which is applied only at design time. You can find more info about d:DataContext in Karl Shifflett's blog:

http://karlshifflett.wordpress.com/2009/10/28/ddesigninstance-ddesigndata-in-visual-studio-2010-beta2/

Hopefully this helps you to solve the issue at hand.

Laurent

Coordinator
Apr 18, 2010 at 6:57 PM

Hi,

As you are a previous user of the discussion tab on the MVVM Light Codeplex site, I would like to inform you that I decided to encourage the usage of StackOverflow for questions regarding the MVVM Light toolkit. Please tag your questions with the mvvm-light tag.

StackOverflow is an awesome site where tons of developers help others with their technical question.

http://stackoverflow.com/questions/tagged/mvvm-light

I will monitor this tag on the StackOverflow website and do my best to answer questions. The advantage of StackOverflow over the Codeplex discussion is the sheer number of qualified developers able to help you with your questions, the visibility of the question itself, and the whole StackOverflow infrastructure (reputation, up- or down-vote, comments, etc)

Thanks!

Laurent