1

I have a FilterUserControl with FilterViewModel to be its DataContext. In FilterControl.xaml:

<Button x:Name="FilterButton">
  <Button.ContextMenu PlacementTarget="{x:Reference FilterButton}" ItemsSource="{Binding FilterConditions}" Style="{StaticResource ButtonContextMenu}">
                    <ContextMenu.ItemContainerStyle>
                        <Style TargetType="MenuItem">
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate>
                                        <MenuItem Command="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}, Path=DataContext.ChangeFilterCondition}" 
                                              CommandParameter="{Binding}">
                                         ...

I searched on Web and knew that

CommandParameter="{Binding}"

is the same as

CommandParameter="{Binding DataContext,RelativeSource={RelativeSource Self}}"

I originally thought that DataContext would be FilterViewModel But after debugging I found that DataContext was actually "each item of FilterConditions"

I finally got the evidence here ItemsSource vs DataContext in binding case

Now I would like to know in .xaml how do we identify what the DataContext is? What are the typical/common cases? Thanks.

Simon
  • 135
  • 1
  • 8

2 Answers2

3

Long story short: In an ItemsControl with an assigned ItemsSource you can be sure that each item has a different DataContext, this means ItemTemplate and ItemContainerStyle. Not the ItemsPanel.


DataContext is the root of binding path, and it remains the same throughout XAML hierarchy unless you change it.

You can change the DataContext explicitly or by changing the ItemsSource. Having an ItemsSource changes the DataContext of each element, so you don't have to take care of indexes.

This is not true when you assign to Items because it implicitly adds them to the ItemCollection and clears ItemsSource. Using Items is similar to when you add items to any other control. i.e. the contents of the DataContext in this case:

<ItemsControl>
    <Button Content="{Binding A}"/>
</ItemsControl>

is just like this case:

<StackPanel>
    <Button Content="{Binding A}"/>
</StackPanel>

or even:

<Button>
    <Button Content="{Binding A}"/>
</Button>

However using ItemsSource means that you're asking the ItemsControl to enumerate through the given collection, take each element, set their DataContext and render them. Therefore the DataContext is changed there.


RelativeSource Self resolves to the current XAML element, so these two are equal:

<... Prop="{Binding Path=Width, RelativeSource={RelativeSource Self}}"/>
<... Prop="{Binding Path=Width, ElementName=name}" x:Name="name"/>

DataContext is always the root object of the binding ({Binding} or {Binding Path=.}), so these three are equal:

<... Prop="{Binding Path=A}"/>
<... Prop="{Binding Path=DataContext.A, RelativeSource={RelativeSource Self}}"/>
<... Prop="{Binding Path=DataContext.A, ElementName=name}" x:Name="name"/>

Default Binding Path of all the objects in the object tree always resolves to the same object (unless they are changed). e.g. If grid.DataContext=A then A is the Binding root for all the objects inside grid object tree hierarchically.

Note that, you can either change DataContext in the code (preferably in the view's constructor), or you can "bind" the DataContext to have different scopes, so that this view:

<Grid DataContext="{Binding}"> // this is redundant and points to VM
    <Grid DataContext="{Binding Child1}">
         <Button Command="{Binding Action11}"/>
         <Button Command="{Binding Action12}"/>
    </Grid>
    <Grid DataContext="{Binding Child2}">
         <Button Command="{Binding Action21}"/>
         <Button Command="{Binding Action22}"/>
    </Grid>
    <ItemsControl ItemsSource="{Binding Collection}">
         <ItemsControl.ItemTemplate>
             <DataTemplate>
                 <Button
                       DataContext="{Binding}" // this is redundant and points to an item
                       Command="{Binding ElementAction}"/>
             </DataTemplate>
         </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

perfectly represents this VM:

 VM
     Child1
         Action11
         Action12
     Child2
         Action21
         Action22
     Collection
         Item1
              ElementAction
         Item2
              ElementAction
         Item3
              ElementAction
         ...

where Child1, Child2, and Collection are properties inside VM and so on

Bizhan
  • 16,157
  • 9
  • 63
  • 101
1

There's only one case for an itemscontrol ( or things inherit from itemscontrol ).

https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.itemscontrol?view=netcore-3.1

When you bind itemssource to a collection.

What happens is each item in that collection is presented. Datatemplating then gives an instance of whatever UI you specified in the template.

That row UI appears in your itemscontrol itemspanel.

The row UI has a datacontext of the item.

You can use a datatemplate selector or datatemplates associated with datatype so that you get different UI.

You can change the itemspanel that presents these so it's say a canvas instead of the default stackpanel.

But whatever you do, the datacontext of each will be one of those items in the collection you bound to itemssource.

Andy
  • 11,864
  • 2
  • 17
  • 20