How to bind Dictionary object to checkedlistbox using WPF - wpf

I have a generic dictionary collection Dictionary. I need to bind the displaymember path key to the content of the checkbox and checkbox Ischecked property to the value member of the Dictionary
private Dictionary<string, bool> _columnHeaderList;
public Dictionary<string, bool> ColumnHeaderList
{
get { return _columnHeaderList; }
set { _columnHeaderList = value; RaisePropertyChanged("ColumnHeaderList"); }
}
private Dictionary<string, bool> GetColumnList()
{
Dictionary<string, bool> dictColumns = new Dictionary<string, bool>();
Array columns = Enum.GetValues(typeof(ColumnHeaders));
int arrayIndex=0;
for(int i=0;i<columns.Length;i++)
{
dictColumns.Add(columns.GetValue(arrayIndex).ToString(), true);
}
return dictColumns;
}
My XAML looks like
<ListBox Grid.Column="0" Grid.Row="1" Height="200"
ItemsSource="{Binding ColumnHeaderList}"
VerticalAlignment="Top">
<ListBox.ItemTemplate>
<HierarchicalDataTemplate>
<CheckBox Content="{Binding key}" IsChecked="{Binding Path=Value}"></CheckBox>
</HierarchicalDataTemplate>
</ListBox.ItemTemplate>
</ListBox>

You will need to use OneWay binding if you bind to Dictionary because the KeyValuePair has read-only properties.
<CheckBox Content="{Binding Key, Mode=OneWay}" IsChecked="{Binding Path=Value, Mode=OneWay}" Width="100" /></CheckBox>
make sure you have set the DataContext. Note this will not update the dictionary values when the user presses on the checkboxes.

Since Value property is readonly and OneWay binding will not allow you track the changes if user checks or unchecks the checkboxes. It is recommended to bind them with an array new class ListItem:
class ListItem
{
public string Text { get; set; }
public bool IsChecked { get; set; }
}
private ListItem[] GetColumnList()
{
return Enum.GetValues(typeof(ColumnHeaders))
.Select(h => new ListItem{ Text = h.ToString(),IsChecked = true})
.ToArray();
}

Yeah its possible and it should work too, although you need to bind with Value with Binding Mode as OneWay since the dictionary value can't be set since its readOnly. If you wish to change the value you can hook the Command(if following MVVVM) or can handle in code behind on Checked event.
Also Binding for Key is not correct, replace your keywith Key. Your final xaml should be like this -
<ListBox Grid.Column="0" Grid.Row="1" Height="200"
ItemsSource="{Binding ColumnHeaderList}"
VerticalAlignment="Top">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Key}"
IsChecked="{Binding Path=Value, Mode=OneWay}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Note i have changed the HierarchicalDataTemplate with DataTemplate since i could not see any hierarchy in the template.

Related

WPF Bindings of a Custom User Control with a ComboBox containing a DataTemplate

I am trying to create a Custom User Control with a ListView in it that contains a Data Template looking like this:
<ComboBox ItemsSource="{Binding ItemsSource, ElementName=root}"
SelectedItem="{Binding SelectedItem, ElementName=root, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<!-- This is what I tried:
Working but not what I want <TextBox Text="{Binding Name}"/>
Returns the List only the word "FallBack" <TextBox Text="{Binding ItemText, ElementName=root}" />
Returns the LIst empty <TextBox Text="{Binding ItemText}" />
-->
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
In the code behind I have created the necessary dependency properties for this case (so I assume) the only relevant one is regarding the Item Text and it looks like that:
#region ItemText
public string ItemText
{
get { return (string)GetValue(ItemTextProperty); }
set { SetValue(ItemTextProperty, value); }
}
// Using a DependencyProperty as the backing store for ItemText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ItemTextProperty =
DependencyProperty.Register("ItemText", typeof(string), typeof(CardComboBox), new PropertyMetadata("FallBack"));
#endregion
What I would like to do is to add the User Control like that
<local:CardComboBox ItemsSource="{Binding Persons}" SelectedItem="{Binding SelectedPerson}" ItemText="{Binding Name}" IsEnabled="True" />
Option one:
<TextBox Text="{Binding Name}"/>
works fine because the PropertyName of the Class Person is Name, but I obviously do not want to HardCode it. I want to bind it to whichever property I like.
Option 2:
<TextBox Text="{Binding ItemText, ElementName=root}" />
Gives me the list of 7 items (as per list) but just displays the word "Fallback" because of the DependencyPropertyMetadata.
Option 3:
<TextBox Text="{Binding ItemText}" />
Gives me the list but with no text at all.
I also tried to work with relativeSource but only with similar results.
#region Person
Person _selectedPerson;
public Person SelectedPerson
{
get => _selectedPerson;
set
{
if (value != _selectedPerson)
{
_selectedPerson = value;
OnPropertyChanged("SelectedPerson");
}
}
}
ObservableCollection<Person> _persons;
public ObservableCollection<Person> Persons
{
get => _persons;
set
{
if (value != _persons)
{
_persons = value;
OnPropertyChanged("Persons");
}
}
}
public void populatePersons()
{
Persons = new ObservableCollection<Person>();
Persons.Add(new Person("Carl"));
Persons.Add(new Person("Max"));
Persons.Add(new Person("May"));
Persons.Add(new Person("Jen"));
Persons.Add(new Person("Charly"));
Persons.Add(new Person("Nora"));
Persons.Add(new Person("Yvonne"));
}
#endregion
I have added the List I am binding to. The Method Populate Persons is called in the constructor of the ViewModel.
<TextBox Text="{Binding ItemText, RelativeSource={RelativeSource AncestorType=local:CardComboBox}}" /> will bind the TextBox to the current value of the ItemText dependency property of the parent CardComboBox if that's what you want.
However, if you want to display the value of the Name property of each Person byt you also want to be able specify the name of the property of Person to bind to using your ItemText property, you will have to create the binding programmtically.
The closest solution working so far is to expose the ItemContentTemplate Dependency Property and then to bind it to a static resource (e.g. from App.xaml)
The XAML of the UserControl looks like this:
<StackPanel Style="{StaticResource CardStackPanel}" Orientation="{Binding Orientation, ElementName=root}" >
<Label x:Name="Label" Content="{Binding TitleText, ElementName=root}"/>
<ComboBox ItemsSource ="{Binding ItemsSource, RelativeSource={RelativeSource AncestorType=UserControl}}"
ItemTemplate="{Binding ItemContentTemplate, RelativeSource={RelativeSource AncestorType=UserControl}}"
SelectedItem="{Binding SelectedItem, RelativeSource={RelativeSource AncestorType=UserControl}, Mode=TwoWay}" />
</StackPanel>
the Code Behind for the ComboBox Part:
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(CardComboBox));
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem", typeof(object), typeof(CardComboBox));
public object SelectedItem
{
get { return GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
public DataTemplate ItemContentTemplate
{
get { return (DataTemplate)GetValue(ItemContentTemplateProperty); }
set { SetValue(ItemContentTemplateProperty, value); }
}
public static readonly DependencyProperty ItemContentTemplateProperty =
DependencyProperty.Register("ItemContentTemplate", typeof(DataTemplate), typeof(CardComboBox));
The Static Ressource in App.xaml (example):
<DataTemplate x:Key="FirstNamesTemplate">
<Label Content="{Binding FirstName}"/>
</DataTemplate>
The Implementation of a ComboBoxCard looks now like this:
<local:CardComboBox ItemsSource="{Binding PersonModels}" SelectedItem="{Binding SelectedPersonModel, Mode=TwoWay}" ItemContentTemplate="{StaticResource FirstNamesTemplate}" TitleText="With StaticResource" IsEnabled="False"/>
This pattern allows to implement ComboBoxes or ListViews in Custom UserControls .

How to bind object data from Combobox to ListBox

This is the custom object:
public class FileItem
{
public string Name { get; set; }
public string Path { get; set; }
}
Collection:
public ObservableCollection<FileItem> collection { get; set; }
Combobox:
<ComboBox
Name="cbCollection"
ItemsSource="{Binding interfaces}"/>
ListBox:
<ListBox
Name="lbCollection "
ItemsSource="{Binding collection}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
So my Combobox is populated with my object collection and i want to see all its properties in my ListBox.
Currently:
I can see only the property Name.
i can see all the objects from my Combobox and not only the selected one.
If you want to see more than just the name property, you'll need to extend the data template to include the additional properties.
If you want to see the selected item's properties, then you should bind to the combo box's SelectedItem property. Actually I don't think you want a ListBox as there is only one selected item.
This should get you started:
<ContentControl Content="{Binding ElementName=cbCollection, Path=SelectedItem}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type local:FileItem}">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>

Binding to a viewmodel property in a DataTemplate

I'm fairly new to XAML but enjoying learning it. The thing I'm really struggling with is binding a property to an element in a DataTemplate.
I have created a simple WPF example to, (hopefully,) explain my problem.
I this example I am trying to bind the Visibility property of a CheckBox in a DataTemplate to a Property in my viewmodel. (Using this scenario purely for learning/demo.)
I have a simple DataModel named Item, but is of little relevance in this example.
class Item : INotifyPropertyChanged
{
// Fields...
private bool _IsRequired;
private string _ItemName;
And a fairly simple View Model named ItemViewModel.
class ItemViewModel : INotifyPropertyChanged
{
private ObservableCollection<Item> _Items;
private bool _IsCheckBoxChecked;
private bool _IsCheckBoxVisible;
public ObservableCollection<Item> Items
{
get { return _Items; }
set { _Items = value; }
}
public bool IsCheckBoxChecked
{
get { return _IsCheckBoxChecked; }
set
{
if (_IsCheckBoxChecked == value)
return;
_IsCheckBoxChecked = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("IsCheckBoxChecked"));
PropertyChanged(this, new PropertyChangedEventArgs("IsCheckBoxVisible"));
}
}
}
public bool IsCheckBoxVisible
{
get { return !_IsCheckBoxChecked; }
set
{
if (_IsCheckBoxVisible == value)
return;
_IsCheckBoxVisible = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("IsCheckBoxVisible"));
}
(Constructors and INotifyPropertyChanged implementation omitted for brevity.)
Controls laid out in MainPage.xaml as follows.
<Window.Resources>
<local:VisibilityConverter x:Key="VisibilityConverter"/>
</Window.Resources>
<Window.DataContext>
<local:ItemViewModel/>
</Window.DataContext>
<Grid>
<StackPanel>
<CheckBox x:Name="checkBox" Content="Hide CheckBoxes" FontSize="14" IsChecked="{Binding IsCheckBoxChecked, Mode=TwoWay}" />
<ListView ItemsSource="{Binding Items}" HorizontalContentAlignment="Stretch" >
<ListView.ItemTemplate >
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding ItemName}"/>
<CheckBox Grid.Column="1" Visibility="{Binding IsCheckBoxVisible, Converter={StaticResource VisibilityConverter}}" >
<CheckBox.DataContext>
<local:ItemViewModel/>
</CheckBox.DataContext>
</CheckBox>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel Orientation="Horizontal" Margin="4,4,0,0">
<TextBlock Text="IsCheckBoxVisible:"/>
<TextBlock Text="{Binding IsCheckBoxVisible}" Margin="4,0,0,0" FontWeight="Bold" />
</StackPanel >
<Button Content="Button" Visibility="{Binding IsCheckBoxVisible, Converter={StaticResource VisibilityConverter}}" Margin="4,4,4,4"/>
</StackPanel>
</Grid>
The 'Hide CheckBoxes' checkbox is bound to IsCheckBoxChecked and is used to update IsCheckBoxVisible. I've also added a couple of extra controls below the DataTemplate to prove, (to myself,) the everything works.)
I have also implemented Jeff Wilcox's value converter. (Thank you.) http://www.jeff.wilcox.name/2008/07/visibility-type-converter/
When I run the app, checking and unchecking the 'Hide Checkbox', controls outside the DataTemplate function as expected but, alas, the Checkbox inside the data template remains unchanged.
I have had success with:
IsVisible="{Binding IsChecked, Converter={StaticResource VisibilityConverter}, ElementName=checkBox}"
But I'm not just trying mimic another control but make decisions based on a value.
I would REALLY appreciate any help or advice you can offer.
Thank you.
When you are in a DataTemplate, your DataContext is the data templated object, in this case an Item. Thus, the DataContext of the CheckBox in the DataTemplate is an Item, not your ItemViewModel. You can see this by your <TextBlock Text="{Binding ItemName}"/>, which binds to a property on the Item class. The Binding to IsCheckBoxVisible is trying to find a property called IsCheckBoxVisible on Item.
There are a couple of ways around this, but by far the easiest is to do this:
On your Window (in the xaml), give it and x:Name. Eg:
<Window [...blah blah...]
x:Name="MyWindow">
Change your binding to look like this:
<CheckBox Grid.Column="1"
Visibility="{Binding DataContext.IsCheckBoxVisible, ElementName=MyWindow, Converter={StaticResource VisibilityConverter}}">
We're using the Window as the source for the Binding, then looking at its DataContext property (which should be your ItemViewModel, and then pulling off the IsCheckBoxVisible property.
Another option, if you want something fancier, is to use a proxy object to reference your DataContext. See this article on DataContextProxy.

How to make ListBox editable when bound to a List<string>?

Edit: The basic problem is binding a List to ListBox(or any other control). So I am editing the question.
I bound a list of string to a ListBox as below. However when I change the contents of the textbox it is not changing the string in the source list.Why?
public partial class MainWindow : Window
{
List<string> _nameList = null;
public List<string> NameList
{
get
{
if (_nameList == null)
{
_nameList = new List<string>();
}
return _nameList;
}
set
{
_nameList = value;
}
}
public MainWindow()
{
NameList.Add("test1");
NameList.Add("test2");
InitializeComponent();
}
And the XAML
<ListBox Grid.Row="0" Grid.Column="0" DataContext="{Binding ElementName=main}" ItemsSource="{Binding NameList}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding .,Mode=OneWayToSource , UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The DataContext of each ListBoxItem is the string itself, so the path of your binding is empty (.). TwoWay and OneWayToSource bindings require a path, since you can't just replace the current DataContext. So you need to wrap your string in an object that exposes the string as a property:
public class StringItem
{
public string Value { get; set; }
}
Expose the strings as a list of StringItem:
public partial class MainWindow : Window
{
List<StringItem> _nameList = null;
public List<StringItem> NameList
{
get
{
if (_nameList == null)
{
_nameList = new List<StringItem>();
}
return _nameList;
}
set
{
_nameList = value;
}
}
public MainWindow()
{
NameList.Add(new StringItem { Value = "test1" });
NameList.Add(new StringItem { Value = "test2" });
InitializeComponent();
}
And bind to the Value property:
<ListBox Grid.Row="0" Grid.Column="0" DataContext="{Binding ElementName=main}" ItemsSource="{Binding NameList}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Note that StringItem will also need to implement INotifyPropertyChanged so that bindings are automatically updated. You should also expose the list as an ObservableCollection<T> rather than a List<T>
May be it helsp?
<ListBox Name="lsbList">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=Value}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
you can create a DataGridTemplateColumn.CellEditingTemplate with an itemscontrol and textboxes to edit your items
If I didn't misunderstand your question, it is pretty easy to implement. Look:
<ComboBox Text="My Comment 5 with addition." IsEditable="True" Height="25" Width="200">
<ComboBoxItem>My comment1</ComboBoxItem>
<ComboBoxItem>My comment2</ComboBoxItem>
</ComboBox>

How to get TextBox inside DataTemplate in a ListBox to notify the ViewModel on value change

What I need to find is when a textbox's value is changing or the dropdown's value changes inside my datatemplate item, I need to be notified in my ViewModel.cs.
So basically as a user edits a textbox inside the listbox, the viewmodel will be notified as the values are changing.
The reason is I need to go through all my Entries and update something as items inside the listbox's datatemplate change.
Any suggetion?
I have the following in my XAML.
<ListBox x:Name="EntriesListBox"
ItemsSource="{Binding Path=Entries}"
Grid.Row="1">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ComboBox x:Name="EntriesPropertyName"
Width="215"
Margin="0,0,5,0"
SelectedItem="{Binding Path=Property, Mode=TwoWay}"
ItemsSource="{Binding Source={StaticResource DataContextProxy},Path=DataSource.EntityTypeProperties}" />
<TextBox x:Name="EntriesPropertyValue"
Width="215"
Margin="0,0,5,0"
Text="{Binding Path=Value, Mode=TwoWay, BindsDirectlyToSource=True}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The following is in my VM (ViewModel.cs)
public ObservableCollection<Entry> Entries { get; set; }
The following is in my business object (Entry.cs)
public class Entry
{
public PropertyItem Property { get; set; }
public string Value { get; set; }
}
On your binding, set the UpdateSourceTrigger... Also implement INotifyPropertyChanged
Provided that you have setup your view model class properly (by implementing INotifyPropertyChanged), following is what you may want to do:
<TextBox x:Name="EntriesPropertyValue"
Width="215"
Margin="0,0,5,0"
Text="{Binding Path=Value, Mode=TwoWay, BindsDirectlyToSource=True, UpdateSourceTrigger=PropertyChanged}" />
This seems to work. Any reason not to do it this way?
private void EntriesPropertyValue_TextChanged(object sender, TextChangedEventArgs e)
{
(sender as TextBox).GetBindingExpression(TextBox.TextProperty).UpdateSource();
this.ViewModel.UpdateFinalQuery();
}

Resources