Bind Dictionary to ItemsControl in C#/WPF - c#

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}"/>

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.

ContentControl.Content not binding correctly in MVVM

I have an MVVM app. I want a collection of buttons to be represented from the ViewModel and be dynamic.
Which means I want to populate the window with controls from the ViewModel.
I tried creating a content control and binding it's Content property to a Grid which I will put buttons in. The binding did not work, it remains empty.
I tried binding it to a simple string, still nothing. I should mention that other simple bindings do work, so that's why it's weird.
The creation of the UserControl:
<TabControl HorizontalAlignment="Left" Height="696" Margin="429,0,0,32" VerticalAlignment="Bottom" Width="552" ItemsSource="{Binding TabCollection}">
<TabControl.Resources>
</TabControl.Resources>
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<cattab:CategoryTab/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>`
The binding in the UserControl:
<ContentControl HorizontalAlignment="Left" Height="286" Margin="98,152,0,-396" VerticalAlignment="Top" Width="313">
<ContentControl Content="{Binding Favorites}" Margin="0,30,0,0" VerticalAlignment="Top"/>
</ContentControl>`
MainViewModel:
//**** Initilize TabCollection with fake data (temporary)
TabCollection.Add(new CategoryTabViewModel { Header = "בדיקה11" });
TabCollection.Add(new CategoryTabViewModel { Header = "בדיקה2" });
UserControl ViewModel:
public CategoryTabViewModel()
{
SearchText = "bbbaaaa";
Favorites.Add(new Button());
}
The binding of SearchText works, on Favorites it's not
Try to use an ItemsControl
Here's a good tutorial: http://www.wpf-tutorial.com/list-controls/itemscontrol/
<ItemsControl HorizontalAlignment="Left" Height="286" Margin="98,152,0,-396" VerticalAlignment="Top" Width="313" Content="{Binding Favorites}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<controlsToolkit:WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Use a WrapPanel to manage your layout: http://www.wpftutorial.net/WrapPanel.html
After reading your updates I can say that your code is not MVVM compliant because your ViewModel layer is aware of the View layer (you create Buttons in your ViewModel)
What you need to do:
XAML of your CategoryTab
<ItemsControl ItemsSource="{Binding Favorites, Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button/><!--Show whatever you want here-->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If you want to create new Button every time an object is added to Favorites you will need to make Favorites an ObservableCollection.

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>

How to bind properties of one ObservableCollection of ListView to properties of SelectedItem of another ListView?

So I have a few ListViews. The first is binded to ObservaleCollection<ComPort>. All properties of ComPort may take some predefined values. Other ListViews are responsible for that properties: they show all that possible (predefined) values and SelectedItem should be the current value of that property of ComPort from the first ObservaleCollection.
I can't attach images so here is an external picture, it would make the situation clean: http://i.stack.imgur.com/ZBRRx.png
<Window.Resources>
<ResourceDictionary x:Name="rd">
<l:ComPorts x:Key="vComPorts"/>
<l:SystemPorts x:Key="vSystemPorts"/>
<l:BaudRates x:Key="vBaudRate"/>
<l:Parities x:Key="vParities"/>
<l:DataBits x:Key="vDataBits"/>
<l:StopBits x:Key="vStopBits"/>
<l:Timeouts x:Key="vTimeouts"/>
<l:ComPort x:Key="vSelectedPort"/>
</ResourceDictionary>
</Window.Resources>
...
<ListView
Name="PortsList"
Grid.Row="1"
Grid.Column="0"
Margin="5"
VerticalAlignment="Stretch"
ItemsSource="{StaticResource vComPorts}"
DataContext="{StaticResource vComPorts}"
SelectedValuePath="PortName"
SelectedValue="{Binding ElementName=SystemPortsList, Path=SelectedItem.Value}"
SelectionChanged="PortsList_SelectionChanged"
MouseDoubleClick="PortsList_MouseDoubleClick">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox />
<TextBlock Margin="5,0,0,0" Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ListView
x:Name="SystemPortsList"
Margin="5"
VerticalAlignment="Stretch"
DataContext="{Binding Source={StaticResource vSelectedPort}}"
ItemsSource="{Binding Source={StaticResource vSystemPortsView}}"
SelectedItem="{Binding Source={StaticResource vSelectedPort}, Path=PortName}"
MouseEnter="SystemPortsList_Refresh"
MouseLeave="SystemPortsList_Refresh"
Grid.Row="1"
Grid.Column="1" SelectionChanged="SystemPortsList_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Name="tb" Margin="5,0,0,0" Text="{Binding Path=Name}" />
</StackPanel>
</ListView.ItemTemplate>
</ListView>
I've tried to make an instance of class ComPort for saving current value of selected item from the first ListView, but anyway I can't cope with it without help. How this task should be solved?
1) Instead of handling SelectionChanged on the PortsList ListView, bind your checkbox to the ListViewItemsPanel like so:
<CheckBox IsChecked={Binding IsSelected, RelativeSource=Parent/>
2) Add an x:Name to your first ListBox, say x:Name="ComPortLB";
3) Remove DataContext on SystemPortsList;
4) Fix SelectedItem on SystemPortsList like so:
SelectedValue="{Binding ElementName=ComPortLB, Path=SelectedValue.PortName}"
I haven't tested any of this code and I haven't done this kind of stuff for a while, so I apologize for errors, but it should get you closer. I've also had to make some assumptions about your classes since you don't provide enough information.

How to bind a ListBox item to a user control?

People use frequently something like:
<ListBox ItemsSource="{Binding ElementName=thisControl, Path=ListIndexes}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding Path=IndexName}"/>
<Label Content="{Binding Path=IndexValue}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
But I would like to use, instead of labels, a control, like this:
<ListBox ItemsSource="{Binding ElementName=thisControl, Path=ListIndexes}">
<ListBox.ItemTemplate>
<DataTemplate>
<local:Index Item="**{Binding}**"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My doubt is what to put into this Binding to include the whole item from the collection.
The syntax for this is:
<local:Index Item="{Binding}"/>
This will tell the data binding functions to bind the entire datacontext for each ListBox Item to the Item property in your Index control

Categories