views:

250

answers:

2

I have a custom user control that is based on a Grid control. I have a ViewModel that exposes this as a property. I would like the XAML on the view to bind to this. I am sure this must be easy but I am quite new to WPF. How is this achieved?

Many thanks in advance

(Edited to add more info)

Example of view without binding to ViewModel. Note that I have a custom grid that contains a number of custom stack panels which contains a number of custom headered content controls. These are determined during the ViewModel load.

<MyCustomGrid:CustomGrid>
   <MyCustomGrid:CustomStackPanel>
      <MyCustomGrid:CustomHeaderedContentControl/>
   </MyCustomGrid:CustomStackPanel>
   <MyCustomGrid:CustomStackPanel>
      <MyCustomGrid:CustomHeaderedContentControl/>
   </MyCustomGrid:CustomStackPanel>
</MyCustomGrid:CustomGrid>

ViewModel simply contains a List which contains a List etc. Note that the CustomGrid is a list as there can be more than one, but only one with a specific property will be bound.

A: 

I am still not sure exactly what you want to achieve, but I'll have a go anyway and maybe we can iterate to a solution from there.

Judging from your XAML above you have several custom controls. You mentioned the top level is based on Grid. Is Grid as in the control, or as the ItemPanelTemplate for an ItemsControl?

I get a somewhat feeling that you want to data bind your viewmodel's content, and for that you need to base your custom controls on ItemsControl. Correct me if I am wrong on this.

Something like this (as mentionded, this is assuming MyCustomGrid as an ItemsControl:

<UserControl Name="MyCustomGrid">
            <ItemsControl ItemsSource="{Binding Path=MyStackPanelsCollection}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Grid IsItemsHost="True" />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemContainerStyle>
                    <Style TargetType="{x:Type MyCustomGrid:MyCustomStackPanel}">
                        <Setter Property="Grid.Row" Value="{Binding Path=GridRow}" />
                        <Setter Property="Grid.Column" Value="{Binding Path=GridColumn}" />
                    </Style>
                </ItemsControl.ItemContainerStyle>
                <ItemsControl.ItemTemplate>
                    <DataTemplate DataType="{x:Type MyCustomGrid:MyCustomStackPanelViewModel}">
                        <ContentPresenter Content="{Binding Path=MySerializedObjectTurnedInToControl}" />
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </UserControl>

If that is not the case, as in you bind your CustomStackPanel etc directly to one named property in your viewmodel, it could look something like:

<MyCustomGrid:CustomGrid DataContext="{Binding Path=ViewModel}">
   <MyCustomGrid:CustomStackPanel DataContext="{Binding Path=MyFirstStackPanelViewModel}">

...

Sorry if I misunderstand still, I could very well be missing something obvious.

Tendlon
I think the problem I have is that the CustomStackPanel isn't known when creating the view in the XAML. The number of CustomStackPanels is only known after loading. The same with the contents of these stack panels. What I am not explaining very well is that the contents of these controls are essentially being loaded from a file. This file contains a serialised object model that represents what should be loaded. I kind of wanted to simply say <content binding={MyDesirialiasedObjectModelTurnedIntoControls}> if that makes sense?
Jon Archway
That makes things a little clearer indeed. I am still thinking about a solution based on ItemsControl and DataTemplates for your custom serialized objects. I'll edit my post with example.
Tendlon
A: 

Basic steps

  1. Set the DataContext of the User Control (or the control/window that contains your user control) to some model type instance, with a Data property that is a ObservableCollection<SomeDataType>.

  2. Bind the ListView's ItemSource to the Data property.

  3. Bind the GridViewColumn's DisplayMemberBinding property to properties of SomeDataType.

Thus you will have something like this:

<ListView SelectionMode="Single"
          HorizontalAlignment="Stretch"
          ItemsSource="{Binding Data}">
    <ListView.View>
        <GridView >
            <GridViewColumn Header="Name of Prop2"
                            DisplayMemberBinding="{Binding Path=Prop1}" />
            <GridViewColumn Header="Name of Prop2"
                            DisplayMemberBinding="{Binding Path=Prop2}" />
            <GridViewColumn Header="Name of Prop3"
                            DisplayMemberBinding="{Binding Path=Prop3}" />
        </GridView>
    </ListView.View>
</ListView>

You can bind to other types, and have control over formatting etc., but this is the simple case.

Richard
This isn't quite giving me what I want. The grid, the stack panels it should present and the content to show in those panels is instantiated in the viewmodel when the viewmodel is instantiated. I just want to present that in the view. Sorry if I am not explaining it very well.
Jon Archway
@JonArchway: the WPF controls will bind to properties on the control's `DataContext`. So you need to ensure you have set the `DataContext` to something with the appropriate properties, and those properties are populated. It would help if you included something of the structure you are trying to bind to (I suspect you are missing something obvious--once it has been pointed out that is).
Richard
OK, so if I do this in the codebehind I can add everything by doing something like SomeGridOnTheXamlView.Children.Add(viewModel.MyCustomGrid). I want to do something like this directly in the XAML? I am obviously being very stupid here, so feel free to point out my stupidness! :-)
Jon Archway
Right, this can be achieved by simply doing <ContentControl Content="{Binding Whatever"/>. Not sure if this is "best practice" though! Thoughts?
Jon Archway