Access current item in a DataTemplate - c#

The following Control is bound to a List of Users:
<ItemsControl x:Name="Users">
<ItemsControl.ItemTemplate>
<DataTemplate>
How can I pass the user to a Converter in this case, not just properties of it?

I assume you're referring to an IValueConverter instance?
Just leave out the Path parameter of your Binding and it will pass in the current DataContext (in your case a User instance).
This sample assumes your IValueConverter is called MyValueConverter:
<ItemsControl x:Name="Users">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Converter={StaticResource MyValueConverter}}" />
</DataTemplate>
<ItemsControl.ItemTemplate>
</ItemsControl>

Related

How to nest ItemsControl with multiple ItemsSource?

I have a ConfigList object with a name and a Dictionary and I need to nest ItemsControls with different ItemsSource.
I tried to do it this way :
<ItemsControl x:Name="TestStep" Grid.Row="0" Grid.Column="0" ItemsSource="{Binding Path=ConfigList }" HorizontalAlignment="Center" VerticalAlignment="Center">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Ictrl.Nom}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl
ItemsSource="{Binding Path=Param}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Key}" />
<TextBlock Text=" : " />
<TextBlock Text="{Binding Path=Value}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ItemsControl>
When I start my application, I've got this error :
System.Windows.Markup.XamlParseException : '' Adding a value to the
collection of type 'System.Windows.Controls.ItemCollection' threw an
exception. ' line number '25' and line position '14'. '
Internal Exception
InvalidOperationException: Invalid operation when ItemsSource is in
use. Access and edit items with ItemsControl.ItemsSource.
Any idea what ​​the problem is?
You set an ItemsSource on the ItemsControl. The data template is used to create the controls that display the data. The created items are then put into the Items collection of the ItemsControl. If you add an element to the ItemsControl directly in XAML this will put them into the Items collection, too. Doing both is not allowed. You either specify an ItemsSource or add to Items directly. From the documentation:
Note that you use either the Items or the ItemsSource property to specify the collection that should be used to generate the content of your ItemsControl. When the ItemsSource property is set, the Items collection is made read-only and fixed-size.
However, in your case this is not the real issue, because your markup is wrong for what you want to achieve. If you you really intended to nest ItemsControls, you would simply change the data template for the outer ItemsControl to contain another ItemsControl that binds to a collection property within the outer data item. Since there is already a TextBox, you have to use a panel (e.g. StackPanel) to host multiple controls in the template.
<ItemsControl x:Name="TestStep" Grid.Row="0" Grid.Column="0" ItemsSource="{Binding Path=ConfigList }" HorizontalAlignment="Center" VerticalAlignment="Center">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Ictrl.Nom}" />
<ItemsControl ItemsSource="{Binding Path=Param}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Key}" />
<TextBlock Text=" : " />
<TextBlock Text="{Binding Path=Value}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If you want to have a hierarchical view of your data, using a TreeView might be a better fit.

Binding complex data to an ItemsControl

So basically I have an list of an object that contains another List of objects. Lets say I have an List of the object Class. And Class contains a list of Students. Every student has a property Name as a simple string.
So basically what I want is the following:
The user can select a class using a ComboBox.
<ComboBox ItemsSource="{Binding Path=Classes}" DisplayMemberPath="Name" />
That works.
After selecting an Item from that ComboBox, the user should see a list of every student in that class (remember the property Name in Students)
I have created a simple ItemsControl for that purpose.
<ItemsControl ItemsSource="{Binding Classes}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label Content="Name of the Student">
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
My question is: How do I get access to the students name in my label?
Your view model should have a SelectedClass property, which would be updated by binding it to the ComboBox's SelectedItem property:
<ComboBox ItemsSource="{Binding Classes}"
SelectedItem="{Binding SelectedClass}" .../>
You would then bind the ItemsControl to the Students collection of the selected Class like this:
<ItemsControl ItemsSource="{Binding SelectedClass.Students}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Note that the view model must implement the INotifyPropertyChanged interface and fire the PropertyChanged event when SelectedClass changes.
In a quick and dirty approach without a SelectedClass view model property, you could also directly access the ComboBox's SelectedItem like this:
<ComboBox x:Name="cbClasses" ItemsSource="{Binding Classes}" ... />
<ItemsControl ItemsSource="{Binding SelectedItem.Students, ElementName=cbClasses}">
...

Nested Dynamic Content Controls with MVVM

I am interested in creating a system of nested content controls to visually represent a user created network of nodes in an automation system.
Simply, I have nodes 'x' and they each contain modules 'y' which host channels 'z'.
So far I have set up in the ViewModel a system for instantiating all of this.
I have a List<x> where x is a model that contains a List<y> (and attributes: name, ID),
where y is a model that contains List<z>(and attributes: name, index) where z is a model for a channel (attributes: name, state, command).
I would now like to display these in my View.
The way I would like to do this is as follows, for each model x in List<x> there should be a Headered Content Control (or some other control) whose item-source is the List<y> in this model x. The Content Control should also display the 'name' attribute of x.
The datatemplate for each y under this Content Control should be a similar Content Control where the item-source is the List<z> in this model y. The Content Control should also display the 'name' attribute of y.
Finally, each model z under this content control should be displayed as a CheckBox that binds it's "ischecked" state to the 'state' attribute of the model, it's content to the 'name' attribute, and it's command to the 'command' attribute.
My question is; is there a way to do this in MVVM? And if so, how would I go about setting it up?
As usual, there are several ways to accomplish this task. It strongly depends on what kind of visual result you want to achieve.
You can display your data as a tree:
<TreeView ItemsSource="{Binding Nodes}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Node}"
ItemsSource="{Binding Modules}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Module}"
ItemsSource="{Binding Channels}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:Channel}">
<CheckBox Content="{Binding Name}"
IsChecked="{Binding State}"
Command="{Binding Command}" />
</DataTemplate>
</TreeView.Resources>
</TreeView>
In this example both appearance and relation to nested items for your classes are defined by HierarchicalDataTemplates. TreeView control is "smart" enough to know what to do with those.
More general solution would be something along these lines:
<ItemsControl ItemsSource="{Binding Nodes}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<HeaderedContentControl Header="{Binding Name}">
<ItemsControl ItemsSource="{Binding Modules}"
Margin="10,0,0,0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<HeaderedContentControl Header="{Binding Name}">
<ItemsControl ItemsSource="{Binding Channels}"
Margin="10,0,0,0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding State}"
Content="{Binding Name}"
Command="{Binding Command}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</HeaderedContentControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</HeaderedContentControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Here a template for each class is defined explicitly by ItemTemplate property on corresponding ItemsControl. I used Margin property for nested items to introduce some indentation.
NOTE
I've replaced x, y and z class names with Node, Module and Channel respectively for the sake of readability. Also, I'm using corresponding collection names - Nodes, Modules and Channels - which I believe should be self-explanatory.

Unable to bind collection to ComboBox in silverlight

I've a ObservableCollection, when I want to display this ObservableCollection with CheckBox I am simply binding the contents to CheckBox but when I want to display the same collection by using ComboBox I am unable to do that. Any suggestions?
XAML: Display collection using CheckBox --WORKS
<ItemsControl ItemsSource="{Binding Synonyms}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Margin="0,5,0,0" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<!--Display items in CheckBox-->
<CheckBox Content="{Binding Display}" Margin="10,0,0,0" /> // Display is the collection.
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Output:
XAML: Display collection using ComboBox --NOTHING OVER HERE
<ItemsControl ItemsSource="{Binding Synonyms}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Margin="0,5,0,0" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<!--Display items in ComboBox.-->
Approach - 1
<ComboBox>
<ComboBoxItem Content="{Binding Display}"/>
</ComboBox>
Approach - 2
<ComboBox ItemsSource="{Binding Path=Synonyms}" DisplayMemberPath="Display"/>
Approach - 3
<ComboBox >
<ComboBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Green" BorderThickness="1" Padding="5">
<TextBlock Text="{Binding Path=Display,StringFormat='Display: {0}'}" />
</Border>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Output:
I want to display items(one,two,three, etc.) inside oneComboBox with Select All option. I've tried several approaches but nothing. What am I missing here?
The checkbox is not designed to hold multiple items unlike the Combobox so implementing a system designed for the checkbox is not relevant for the combobox.
To solve your issue, remove the combobox from the ItemsControl and have it stand on its own:
<ComboBox ItemsSource="{Binding Synonyms}" DisplayMemberPath="Display"/>
Which tells the combobox to bind its ItemsSource to the data context which is unspecified, which is fine, so it then gets it's parent's data context. That process works its way up to each parent until it finds a bound data context (most likely the page's datacontext to a VM instance).
Assuming that the data context is valid at some point in the visual tree, it will bind to that instance and look for a property named Synonyms. From the Synonyms property it will use that as a list to display items.
To show (display) text in the combobox (instead defaulting to the item's ToString()) the combobox will show the string from the item's property Display.
Giving a list of items in one drop down.
The short answer, is you should use a ComboBox as the root element, not ItemsControl. CompboBox is just a specialized version of ItemsControl.
<ComboBox ItemsSource="{Binding Synonyms}" DisplayMemberPath="Display"/>
The longer answer.
ComboBox derives from ItemsControl, so you get all the features the base class, plus additional features.
ItemsControl (and its derived classes) provides a way of repeating a set of data in the UI. The DataTemplate is where you specify what UI you want for each "row" of data in the Synonyms source.
What you are doing is asking Silverlight to create a separate ComboBox for each underlying data row.
You can still use a DataTemplate within the ComboBox. Like this.
<ComboBox ItemsSource="{Binding Synonyms}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<!--Display items in CheckBox-->
<TextBox Text="{Binding Display}"
Margin="10,0,0,0"
FontWeight="Bold" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Bind Dictionary to ItemsControl in C#/WPF

I'm trying to bind KeyValuePair Elements from a Dictionary to a ItemsControl.
My Dictionary has 15 Elements and the following code shows me 15 TextBoxes:
<WrapPanel Name="PersonsWrapPanel" Grid.Row="0">
<ItemsControl ItemsSource="{Binding Persons}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" Width="auto">
</WrapPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Value.Text}"></TextBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</WrapPanel>
Unfortunately without any TextBox content (which would be Key or Value).
Any ideas?
Perhaps try binding directly to the values of the dictionary:
ItemsSource="{Binding Persons.Values}"
If I am understanding your XAML properly, each object in the dictionary has a field called "Text" to which you are trying to bind. If so and you make the above changes, you will need to change your DataTemplate as well:
<TextBox Text="{Binding Text}" />
See this article for more info. HTH.
I solved it by using this line:
<TextBox Text="{Binding Value, Mode=OneWay}"></TextBox>
The code on http://www.dev102.com/2008/03/07/binding-a-wpf-control-to-a-dictionary/ doesn't seem to work.
Let us say you have a Dictionary called RowValues, with both the [key, value] defined as [string, string].
Now to bind to the Value Pair of this dictionary, you can do the following:
<ItemsControl ItemsSource="{Binding RowValues.Values}" >
To display the text (Value), you can bind as:
<TextBlock Text="{Binding}"/>

Categories