views:

28

answers:

1

I'm a bit of a .net newbie and I've been working on my first big silverlight project. So pardon the lack of lingo and the length of the question. But my problem is as follows.

The project is being built according to the MVVM pattern ( in this case I'm using LightMVVM ). Most of the views contain ListBoxes. These listboxes need to handle multiple different types of data each of which has it's own visual look. After some poking around I decoded tp try this implementation for datatemplate selection:

http://silverscratch.blogspot.com/2010/04/changing-data-templates-at-run-time.html

Some of my items, however, have sub controls that need to talk to the viewmodel. From what I've been reading Commands with element bindings is the best ways to handle this.

So, for example:

<Grid x:Name="NavMainLayoutRoot" DataContext="{Binding Source={StaticResource NavMainViewModelDataSource}}" Margin="15,0,0,0">
....
<ListBox x:Name="MenuListBox" HorizontalAlignment="Left" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="White" ItemsSource="{Binding Items}" ItemContainerStyle="{StaticResource MainNavigationButtonStyle}" Padding="0" VerticalAlignment="Top" >
        <ListBox.RenderTransform>
            <CompositeTransform/>
        </ListBox.RenderTransform>
        <ListBox.ItemTemplate>
            <DataTemplate>
                <xxxControls:SelectableContentControl TemplateName="{Binding Path=Type}" Content="{Binding Details}" IsTabStop="{Binding IsHitTestEnabled}">
                    <xxxControls:SelectableContentControl.Templates>
                        <DataTemplate>
                            <local:GenericItem />
                        </DataTemplate>
                        <DataTemplate x:Name="navbutton">
                            <local:MainNavItem />
                        </DataTemplate>

                    </xxxControls:SelectableContentControl.Templates>
                </xxxControls:SelectableContentControl>
            </DataTemplate>
        </ListBox.ItemTemplate>
....

And MainNavItem, simplified is:

<Grid x:Name="NavItemRoot" VerticalAlignment="Top" Margin="0,0,0,0">
    <Button Content="{Binding Label}" VerticalAlignment="Top" Style="{StaticResource MainNavItemButtonStyle}" HorizontalAlignment="Left" Margin="5,0" Command="{Binding DataContext.NavButtonClick, ElementName=NavMainLayoutRoot}"/>
</Grid>

The problem is that this didn't work. So for grins I went ahead and copy and pasted the code for the MainNavItem directly into the tag and like magic it started working.

Since I reuse a lot of these item templates all over the application, having them in nice contained external files is very nice and not something I want to give up.

(( Thinking about it, this example is not the best, suffice it to say that some of these data templates contain multiple controls and I can't just use selectedItem on the listbox to handle the selected events. ))

So any suggestions are welcome. What's the best practice here?

A: 

My first thought is that something in your MainNavItem user control is setting its DataContext to something else. If you don't set the DataContext it should automatically pick it up from the current item in your MenuListBox.

You can try creating a test value converter and putting a breakpoint in it to check what the data context is at runtime.

public class TestConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        Debug.WriteLine("TestConverter.Convert(value := {0}, targetType := {1}, parameter := {2}, culture := {3})",
            value, targetType, parameter, culture);
        return value; // put break point here to test data binding
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        Debug.WriteLine("TestConverter.ConvertBack(value := {0}, targetType := {1}, parameter := {2}, culture := {3})",
            value, targetType, parameter, culture);
        return value;
    }
}

And modify your MainNavItem to look like this in order to break in the TestConverter at runtime.

<UserControl.Resources>
    <ResourceDictionary>
        <TestConverter x:Key="TestConverter" />
    </ResourceDictionary>
</UserControl.Resources>
<Grid x:Name="NavItemRoot" DataContext="{Binding Converter={StaticResource TestConverter}}">
    <Button Content="{Binding Path=Label, Converter={StaticResource TestConverter}}" />
</Grid>

This will help you determine the issue with data binding.

I suspect that the problem with your command is that you're using element-to-element data binding to attempt to bind to an element that is outside of the user control you're currently within. This won't work. Instead, try setting up your NavMainViewModelDataSource static resource in App.xaml, then you can bind directly to it from your user control.

<Button Content="{Binding Label}" Command="{Binding Path=NavButtonClick, Source={StaticResource NavMainViewModelDataSource}}" />
Matt Casto
But it does work, but only if the usercontrol is defined in the same xaml as the element I'm trying to bind to, as soon as I externalize it into a another file it stops working.
As an aside. Using a converter as a way to test binding errors... thumbs up!