Setting datacontext for a datatemplate item - wpf

I am trying to set the DataContext for my datatemplate item BuildStepsViewModel. The below code works perfectly fine.
<StackPanel Margin="10" Name="controlDisplay" Visibility="{Binding Path=Visibility}">
<ItemsControl HorizontalAlignment="Left" ItemsSource="{Binding Path=Elements}" Width="Auto">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type vms:BuildStepsViewModel}">
<local:BuildStepsSelWindow>
<local:BuildStepsSelWindow.DataContext>
<vms:BuildStepsViewModel/>
</local:BuildStepsSelWindow.DataContext>
</local:BuildStepsSelWindow>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
</StackPanel>
Now I am trying to reduce the verbose part in the datacontext set.
<StackPanel Margin="10" Name="controlDisplay" Visibility="{Binding Path=Visibility}">
<ItemsControl HorizontalAlignment="Left" ItemsSource="{Binding Path=Elements}" Width="Auto">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type vms:BuildStepsViewModel}">
<local:BuildStepsSelWindow DataContext="{Binding Path=BuildStepsViewModel}"/>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
</StackPanel>
But now the controls in the BuildStepsViewModel are not displaying correctly. Am I overlooking something?

In the first case the DataContext of is a new instance of vms:BuildStepsViewModel. This does not seem correct, as you want to bind the DataTemplate to an existing instance and not a new one.
In the second case the DataContext is bound to some BuildStepsViewModel property. The source is the old DataContext value. Unless the type BuildStepsViewModel has a property called BuildStepsViewModel, this binding will not work correctly. If I understand the case correctly, you can try omitting the data context, as it will be inherited and it will automatiacally become the correct BuildStepsViewModel instance:
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type vms:BuildStepsViewModel}">
<local:BuildStepsSelWindow />
</DataTemplate>
</ItemsControl.Resources>

These two snippets are not equivalent.
This
<local:BuildStepsSelWindow.DataContext>
<vms:BuildStepsViewModel/>
</local:BuildStepsSelWindow.DataContext>
essentially means
this.DataContext = new BuildStepsViewModel();
So you instantiate a new viewmodel for each view instance.
But this:
<local:BuildStepsSelWindow DataContext="{Binding Path=BuildStepsViewModel}"/>
essentially means:
this.DataContext = this.DataContext.BuildStepsViewModel;
See? That makes no sense. You're trying to access a property named BuildStepsViewModel on the object stored as DataContext. But since there is no data context (yet), the binding returns null, and the data context is not set.
Consider reading thoroughly about the data binding. Also, read about the XAML syntax.

Related

Binding to two dependency properties of the user control which is a ListViewItem

I've created an user control which is a ListViewItem. That control has 2 dependency properties I would like to bind to from my ViewModel. But how am I supposed to do that when I can only bind one thing (collection) to ListView ItemsSource property? How can I bind to ExpanderInfo dependency property? Is there any way to do that or maybe I have made a bad assumption and should have created user control in other way?
Here is my xaml code:
<ListView ItemsSource="{Binding Tasks}" SelectedItem="{Binding SelectedTask}" Height="200" ScrollViewer.VerticalScrollBarVisibility="Disabled">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<tc:TaskTile TaskInfo="{Binding}" ExpanderInfo="{Binding ExpanderStatus}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
Thank you for your help!
Replace <tc:TaskTile TaskInfo="{Binding}" ExpanderInfo="{Binding ExpanderStatus}"/> with
<tc:TaskTile TaskInfo="{Binding}" ExpanderInfo="{Binding DataContext.ExpanderStatus, RelativeSource={RelativeSource AncestorType=ListView, Mode=FindAncestor}}"/>

WPF Itemscontrol datatemplate property changing

I'm currently using the following itemscontrol and datatemplate:
<UserControl.Resources>
<DataTemplate x:Key="OrdersTemplate">
<dxlc:LayoutItem Label="CustomerReference" LabelPosition="Top" MaxWidth="300" HorizontalAlignment="Left" Width="300">
<dxe:TextEdit IsEnabled="True" Text="{Binding Path=CustomerReference}" />
</dxlc:LayoutItem>
</DataTemplate>
</UserControl.Resources>
<HeaderedContentControl Header="Steps">
<ItemsControl ItemsSource="{Binding Orders}" ItemTemplate="{StaticResource OrdersTemplate}"/>
</HeaderedContentControl>
The source is just a list with entities.
The problem is that the "CustomerReference" of every object from my source changes when I change it in the textbox. Whats missing here?
Greets
I think in your view model you have added the same object more than once. Instead of creating new object when ever you added to the collection. So when you want to add a object to collection create a new object and add it

Get instantiated UIElement of ContentTemplate from TabControl

I have a TabControl where the ContentTemplate is defined by a DataTemplate containing a ContentPresenter. The mapping UIElement class is defined by a DataTemplate for the specific ViewModel type. It works like that:
<UserControl.Resources>
<DataTemplate DataType="{x:Type ViewModels:DiagramVM}">
<Controls:Diagram DataContext="{Binding}" x:Name="diagram"/>
</DataTemplate>
</UserControl.Resources>
<TabControl ItemsSource="{Binding Path=Tabs, Mode=TwoWay}" SelectedIndex="{Binding Path=SelectedTabIndex}"
x:Name="AnalysisTabCtrl" Template="{DynamicResource ScrollableTabControlTemplate}">
<TabControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Path=Header}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Path=ViewModel}"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
My problem is that I need the instance of the instantiated UIElements. In this case the Diagram instances. How can I get them?
You can use the ItemsControl.ItemContainerGenerator to get a TabItem out of your TabControl, then you can use FindName on the TabItem.ContentTemplate to search for named instantiated controls. (Here you would probably need to name the content-presenter and then again search in its ContentTemplate)
I would not recommend doing anything like that, if you cannot manage without this you probably did not bind all the relevant properties to your items.

How to bind to parent DataTemplate from within an ItemsControl.ItemTemplate

I have a container type control which contains a number of items. The container control has a DataTemplate defined which also contains a ItemsControl with a DataTemplate for the item. The items however need to bind to something from the container control. A simplified example is given below:
<DataTemplate DataType="{x:Type ContainerType}">
<!-- Display of the container stuff-->
<ItemsControl ItemsSource="{Binding Items, Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type Item}">
<!-- Display of the item stuff -->
<ComboBox Text="Choose a container-level option..."
ItemsSource="{WHAT GOES HERE?}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
How to I bind something at the item level back up to the container level?
You can use a RelativeSource binding
<ComboBox ItemsSource="{Binding SomeCollection,
RelativeSource={RelativeSource
AncestorType={x:Type local:MyContainerControl}}}"/>
What you use for your binding path depends on where the collection is located. If it is located as a DependencyProperty on MyContainerControl, then the above binding works fine. If it is located in the DataContext of MyContainerControl, then you'll need to set the binding path to DataContext.SomeCollection
Maybe use TemplateBinding?
Something like:
{TemplateBinding YourPropertyInTheDataTemplateContext}
I've always been a big fan of ElementName. Basically you make sure you name the outer level control like : x:Name="MainWin" and then you can do something like this:
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ComboBox ItemsSource="{Binding ElementName=MainWin, Path=DataContext.SomeCollection}"/>

Access property of DataContext inside ItemTemplate

I have a really nasty problem with bindings. I know that there are other topics regarding binding itmes inside itemtemplate to datacontext of an object outside the template. However, this just won't work, i.e. the first textblock display 'Test' as desired whereas the same textbox inside the itemtemplate shows nothing.
<TextBlock Text="{Binding DataContext.Test, ElementName=myList}"/>
<ItemsControl x:Name="myList" ItemsSource="{Binding AllItems}"
Margin="0,0,0,0" VerticalAlignment="Top" HorizontalAlignment="Center">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel Orientation="Horizontal"
ItemHeight="170" ItemWidth="140"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image x:Name="{Binding KeyName}"
Source="{Binding ImagePath}"
Width="128"
Height="128">
</Image>
<TextBlock Text="{Binding DataContext.Test, ElementName=myList}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I would appreciate some help here folks as this is really a problem for me.
Inside the itemtemplate, the binding is initialized to the context of the current item in AllItems.
Update
Outside of the ItemTemplateyour bindings are relative to the DataContext of the page.**
Once inside an ItemTemplate then bindings are limited to the scope of the item specifically being evaluated at that time.
So, if we assume the following (based on the code in your question):
<ItemsControl x:Name="myList" ItemsSource="{Binding AllItems}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock x:Name="tb1"
Text="{Binding DataContext.Test, ElementName=myList}"/>
<TextBlock x:Name="tb2" Text="{Binding KeyName}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
tb1 cannot access the DataContext object directly.
tb2 cann access KeyName - assuming that whatever object AllItems is an IEnumerable of contains a property with that name.
As I understand it, inside an itemtemplate, the item past from the enumeration controls the binding source and this can't be overridden (by setting ElementName or otherwise).
If you need the value from Test in every object in your enumeration then you'll need to add it as a property of the object in the enumeration.
I'm sure someone more knowledgeable than me could explain why this is or give a better explanation but that's the gist of it.
** Assuming no other nesting of ItemsControls (or equivalent)

Resources