How to use multiple dictionaries in a listbox DataTemplate? - c#

I am wondering if it is possible to use two dictionaries in a listbox data template. I want to use the value of Names in a textbox, and the value of EnabledNames for a check box. Is this possible? If so, how would I go about doing it?
Some example data would be:
Dictionary<string, string> Names = new Dictionary<string, string>()
{
{ "4EG25","John" },
{"923OT", "Joe" }
};
Dictionary<string, bool> EnabledNames = new Dictionary<string, bool>()
{
{ "4EG25",false },
{"923OT", true}
};
And how I want to use in a way like:
<ListBox x:Name="listBox" HorizontalAlignment="Left" Height="359" VerticalAlignment="Top" Width="673" Margin="0,0,-0.333,-0.333" ItemsSource="{Binding Path=Names, Mode=OneWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Path=EnabledNames[ItemsSource.Key].Value}" />
<TextBlock Text="{Binding Path=ItemsSource.Value}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Just create a class that contains both values and use it as the ItemsSource
class Name
{
public string Value { get; set; }
public bool Enabled { get; set; }
}
public IEnumerable<Name> TheNames
{
get { return Names.Select(n => new Name {Value = n.Value, Enabled = EnabledNames[n.Key]}); }
}
<ListBox ItemsSource="{Binding Path=TheNames, Mode=OneWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Path=Enabled}" />
<TextBlock Text="{Binding Value}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

This is very hard, so I'd recommend you to create specific class as DataContext for your ListBox.
What you could try in your case:
WPF binding can be done only to properties (your dictionaries are now Fields, so your bindings won't work. Your ViewModel could be something like this:
public class ViewModel
{
public ViewModel()
{
Names = new Dictionary<string, string>()
{
{ "4EG25","John" },
{"923OT", "Joe" }
};
EnabledNames = new Dictionary<string, bool>()
{
{ "4EG25",false },
{"923OT", true}
};
}
public Dictionary<string, string> Names { get; set; }
public Dictionary<string, bool> EnabledNames { get; set; }
}
In your xaml when you set DataTemplate, its DataContext is set to single entry of your ItemsSource. In your case this is KeyValuePair. Also you could use MultiBinding to bind to your EnabledNames dictionary and use converter to convert your Key to bool Value from EnabledNames:
<Window x:Class="Test31641637.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Test31641637"
Title="MainWindow" Height="350" Width="525"
Name="mw">
<Window.Resources>
<ResourceDictionary>
<local:MultiDictionaryConverter x:Key="multiDictionaryConverter" />
</ResourceDictionary>
</Window.Resources>
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<ListBox x:Name="listBox" ItemsSource="{Binding Names}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox >
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource multiDictionaryConverter}" ConverterParameter="">
<Binding Path="Key" Mode="OneWay"/>
<Binding ElementName="mw" Path="DataContext.EnabledNames"/>
</MultiBinding>
</CheckBox.IsChecked>
</CheckBox>
<TextBlock Text="{Binding Path=Value}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Window>
And IMultiValueConverter:
public class MultiDictionaryConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (values.Length == 2 && values[0] is string && values[1] is Dictionary<string, bool>)
{/*
((Dictionary<string, bool>)values[1])[values[0] as string] =
!((Dictionary<string, bool>)values[1])[values[0] as string];*/
return ((Dictionary<string, bool>)values[1])[values[0] as string];
}
return false;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
I suppose this is the easiest way to do it (but it is not easy) and it won't work, because when you click on CheckBox in your ListBox, ConvertBack method will be called, but it is impossible to convert back boolean value. So, again, the easiest way is to create specific class that represents single line of your ListBox:
public class Person
{
public string ID { get; set; }
public string Name { get; set; }
public bool IsEnabled { get; set; }
}

Related

How to bind an ObservableCollection to a TreeView in a hierarchical way?

I know this may be a duplicate but no solution works for me. So I have a class Technik which has these properties:
public class Technik
{
public bool checkedTe { get; set; }
public int TechnikID { get; set; }
public string anlagengruppe { get; set; }
public string techniktyp { get; set; }
public string anlage { get; set; }
public string bemerkung { get; set; }
}
Now I have a DataTable with 216 rows and each row is getting into a Technik object that is added into my ObservableCollection<Technik> like:
foreach (DataRow dr in dtTechnik.Rows)
{
Technik technik = new Technik();
technik.checkedTe = (bool)dr.ItemArray[0];
technik.TechnikID = (int)dr.ItemArray[1];
technik.anlagengruppe = (string)dr.ItemArray[2];
technik.techniktyp = (string)dr.ItemArray[3];
technik.anlage = (string)dr.ItemArray[4];
technik.bemerkung = (string)dr.ItemArray[5];
TechnikCollection.Add(technik);
}
I want to bind my ObservableCollection like:
* anlagengruppe
* techniktyp
*anlage
* TechnikID
Right now I'm getting nowhere, so maybe you guys out there can help me.
Actual my tree view looks like this:
<TreeView x:Name="treeView" HorizontalAlignment="Left" Height="850" Margin="10,0,0,0" VerticalAlignment="Top" Width="464"
ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Visible" ItemsSource="{Binding TechnicTable}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding TechnicTable}">
<TextBlock Text="{Binding Path=anlagengruppe}" />
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding techniktyp}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Edit:
Maybe some of you think that my tree view ItemsSource is not the correct collection, this is the right one, there is some more code where I change collections.
I am skeptical of this design. Why do you feel it's useful and helpful to the user to present a single object and its property values as if that object had some hierarchical structure to it?
If all you're trying to do is impose some visual structure on the user interface, that's easily done without using TreeView. For example:
class TableItem
{
public string Property1 { get; set; }
public string Property2 { get; set; }
public string Property3 { get; set; }
public TableItem() { }
public TableItem(string property1, string property2, string property3)
{
Property1 = property1;
Property2 = property2;
Property3 = property3;
}
}
class ViewModel
{
public ObservableCollection<TableItem> TableItems { get; } = new ObservableCollection<TableItem>();
}
<Window x:Class="TestSO46300831HiearchicalObservable.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:l="clr-namespace:TestSO46300831HiearchicalObservable"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<l:ViewModel>
<l:ViewModel.TableItems>
<l:TableItem Property1="Item #1, property #1"
Property2="Item #1, property #2"
Property3="Item #1, property #3"/>
<l:TableItem Property1="Item #2, property #1"
Property2="Item #2, property #2"
Property3="Item #2, property #3"/>
<l:TableItem Property1="Item #3, property #1"
Property2="Item #3, property #2"
Property3="Item #3, property #3"/>
</l:ViewModel.TableItems>
</l:ViewModel>
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type l:TableItem}">
<StackPanel>
<TextBlock Text="{Binding Property1}"/>
<TextBlock Text="{Binding Property1}" Margin="10,0,0,0"/>
<TextBlock Text="{Binding Property1}" Margin="20,0,0,0"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ListBox ItemsSource="{Binding TableItems}"/>
</StackPanel>
</Window>
That said, if you must use TreeView and you want for the view to update as the collection is modified, it seems to me you can accomplish that by using an intermediate collection that implements INotifyCollectionChanged (easily done simply by inheriting ObservableCollection<T> and tracking the original collection. The intermediate collection is needed, so that items can be converted from the original single-object item to a hierarchical item type that can be used with the TreeView class. For example:
class HierarchicalTableItem
{
public string Text { get; }
public IReadOnlyList<HierarchicalTableItem> Items { get; }
public HierarchicalTableItem(string text, HierarchicalTableItem child = null)
{
Text = text;
Items = child != null ? new[] { child } : null;
}
}
class ViewModel
{
public ICommand AddCommand { get; }
public ICommand InsertCommand { get; }
public ICommand RemoveCommand { get; }
public int Index { get; set; }
public ObservableCollection<TableItem> TableItems { get; } = new ObservableCollection<TableItem>();
public ViewModel()
{
AddCommand = new DelegateCommand(() => TableItems.Add(_CreateTableItem()));
InsertCommand = new DelegateCommand(() => TableItems.Insert(Index, _CreateTableItem()));
RemoveCommand = new DelegateCommand(() => TableItems.RemoveAt(Index));
}
private int _itemNumber;
private TableItem _CreateTableItem()
{
_itemNumber = (_itemNumber < TableItems.Count ? TableItems.Count : _itemNumber) + 1;
return new TableItem(
$"Item #{_itemNumber}, property #1",
$"Item #{_itemNumber}, property #2",
$"Item #{_itemNumber}, property #3");
}
}
class ConvertingObservableCollection<T> : ObservableCollection<object>
{
private readonly IValueConverter _converter;
private readonly ObservableCollection<T> _collection;
public ConvertingObservableCollection(IValueConverter converter, ObservableCollection<T> collection)
{
_converter = converter;
_collection = collection;
_ResetItems();
_collection.CollectionChanged += _OnCollectionChanged;
}
private void _OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
_AddItems(e);
break;
case NotifyCollectionChangedAction.Move:
_RemoveItems(e);
_AddItems(e);
break;
case NotifyCollectionChangedAction.Remove:
_RemoveItems(e);
break;
case NotifyCollectionChangedAction.Replace:
_ReplaceItems(e);
break;
case NotifyCollectionChangedAction.Reset:
_ResetItems();
break;
}
}
private void _ReplaceItems(NotifyCollectionChangedEventArgs e)
{
for (int i = 0; i < e.NewItems.Count; i++)
{
this[i] = _Convert(e.NewItems[i]);
}
}
private void _AddItems(NotifyCollectionChangedEventArgs e)
{
for (int i = 0; i < e.NewItems.Count; i++)
{
Insert(i + e.NewStartingIndex, _Convert(e.NewItems[i]));
}
}
private void _RemoveItems(NotifyCollectionChangedEventArgs e)
{
for (int i = e.OldItems.Count - 1; i >= 0; i--)
{
RemoveAt(i + e.OldStartingIndex);
}
}
private void _ResetItems()
{
Clear();
foreach (T t in _collection)
{
Add(_Convert(t));
}
}
private object _Convert(object value)
{
return _converter.Convert(value, typeof(T), null, null);
}
}
class TableItemHierarchicalConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
TableItem tableItem = value as TableItem;
if (tableItem == null)
{
return Binding.DoNothing;
}
return new HierarchicalTableItem(tableItem.Property1,
new HierarchicalTableItem(tableItem.Property2,
new HierarchicalTableItem(tableItem.Property3)));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
class ConvertingCollectionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
IValueConverter converter = parameter as IValueConverter;
if (converter == null || value == null ||
value.GetType().GetGenericTypeDefinition() != typeof(ObservableCollection<>))
{
return Binding.DoNothing;
}
Type resultType = typeof(ConvertingObservableCollection<>).MakeGenericType(value.GetType().GenericTypeArguments);
return Activator.CreateInstance(resultType, converter, value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
<Window x:Class="TestSO46300831HiearchicalObservable.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:l="clr-namespace:TestSO46300831HiearchicalObservable"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<l:ViewModel>
<l:ViewModel.TableItems>
<l:TableItem Property1="Item #1, property #1"
Property2="Item #1, property #2"
Property3="Item #1, property #3"/>
<l:TableItem Property1="Item #2, property #1"
Property2="Item #2, property #2"
Property3="Item #2, property #3"/>
<l:TableItem Property1="Item #3, property #1"
Property2="Item #3, property #2"
Property3="Item #3, property #3"/>
</l:ViewModel.TableItems>
</l:ViewModel>
</Window.DataContext>
<Window.Resources>
<l:ConvertingCollectionConverter x:Key="convertingCollectionConverter1"/>
<l:TableItemHierarchicalConverter x:Key="tableItemConverter1"/>
</Window.Resources>
<ScrollViewer>
<StackPanel>
<UniformGrid Columns="4">
<Button Content="Add" Command="{Binding AddCommand}"/>
<Button Content="Insert" Command="{Binding InsertCommand}"/>
<Button Content="Remove" Command="{Binding RemoveCommand}"/>
<TextBox Text="{Binding Index}"/>
</UniformGrid>
<TreeView ItemsSource="{Binding TableItems,
Converter={StaticResource convertingCollectionConverter1},
ConverterParameter={StaticResource tableItemConverter1}}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Items}">
<TextBlock Text="{Binding Text}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</StackPanel>
</ScrollViewer>
</Window>
This alternative relies on three key classes:
ConvertingObservableCollection<T> — This does the work of watching the original collection and presenting converted items according to the current state of that original collection.
ConvertingCollectionConverter — This converts the original collection to the ConvertingObservableCollection<T> object for the purpose of binding to the TreeView.
TableItemHierarchicalConverter — This converts the individual original item objects into a hierarchy of objects suitable for display in the TreeView.
Of course, there is also the simple container class HierarchicalTableItem which is used to represent the hierarchy for each table item.
Ultimately, the key to remember is that for a TreeView, you must be presenting items that have a recursive nature, such that a single HierarchicalDataTemplate element can be used to define how to present each level of the tree. This means that a single data item must have some property that can be used for the ItemsSource of the template, which is itself some type of collection of the same type of data item.

Not able to see binded items to listbox

I've created a ListBox with this structure:
<ListBox VerticalAlignment="Stretch"
Background="AliceBlue"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ItemsSource="{Binding EventInfo}">
how you can see I binded the EventInfo property that I valorize behind code. This property have the OnPropertyChange(); implementation as my other properties, and the value setted is got correctly. Anyway, I'm not able to display the binded source:
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=League}" />
<TextBlock Text="test" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
now the property League value isn't displayed also the value test. I really don't understand why. The League property exist, and also I've no error in xaml.
What I did wrong?
UPDATE:
public Models.EventInfo EventInfo
{
get { return _eventInfo; }
set
{
_eventInfo = value;
OnPropertyChanged();
}
}
and in the Model
public class EventInfo
{
public string League { get; set; }
public string Date { get; set; }
public string GameWeek { get; set; }
public string GameStart { get; set; }
public string FirstTime { get; set; }
public string SecondTime { get; set; }
public string Stadium { get; set; }
public List<MatchArbiter> Arbiter { get; set; }
}
Try this. You need to populate ItemsSource with a collection, not a single item. Instead of your existing EventInfo property, you need a collection property. I'm going to rename it to EventInfoItems to keep confusion to a minimum.
private ObservableCollection<Models.EventInfo> _eventInfoItems =
new ObservableCollection<Models.EventInfo>();
public ObservableCollection<Models.EventInfo> EventInfoItems
{
get { _eventInfoItems; }
set
{
_eventInfoItems = value;
OnPropertyChanged();
}
}
Now, somewhere, you're going to have to add some items to that collection if you want anything to appear in the list. You could create a few test items in your viewmodel constructor, just for the time being. Like this:
EventInfoItems.Add(new EventInfo { League = "NBA" });
EventInfoItems.Add(new EventInfo { League = "Premier League" });
EventInfoItems.Add(new EventInfo { League = "Serie A" });
XAML
<ListBox
VerticalAlignment="Stretch"
Background="AliceBlue"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ItemsSource="{Binding EventInfoItems}"
>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=League}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
Update
Turns out OP may have only one item. If that's the case, a ListBox is unnecessary. A ContentControl is the right control when you've got only one item and you want to display it with a DataTemplate. This XAML will use the original version of the EventInfo property:
public Models.EventInfo EventInfo
{
get { return _eventInfo; }
set
{
_eventInfo = value;
OnPropertyChanged();
}
}
XAML:
<ContentControl
VerticalAlignment="Stretch"
Background="AliceBlue"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
Content="{Binding EventInfo}"
>
<ContentControl.ContentTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=League}" />
</StackPanel>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>

Binding ObservableCollection<double> to ListBox not updating source [duplicate]

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>

XAML binding list enum

class EnumToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return loai.ToDisplaytring();
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
<ListView ItemsSource="{Binding ListEnum" Margin="0,51,0,0">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=.,Converter={StaticResource EnumToStringConverter}}" HorizontalAlignment="Center" FontSize="18"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I am a new guy to XAML.
I just want to display enum in listview.
But it has some problem of binding itself with binding:
{Binding Path=.,Converter={StaticResource EnumToStringConverter}}
This is simpler:
<ListView x:Name="ListViewInstance" ItemsSource="{Binding ListEnum}" Margin="0,51,0,0">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" HorizontalAlignment="Center" FontSize="18"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
That's the binding that gets the items, it automatically makes the item.ToString()
and to show all the values in the DataContext, for instance:
public List<Devices> ListEnum { get { return typeof(Devices).GetEnumValues().Cast<Devices>().ToList(); } }
In case you need a converter do the following:
public class EnumToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return what you need to convert from value
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
and then in XAML
<Window.Resources>
<local:EnumToStringConverter x:Key="EnumToStringConverter"/>
</Window.Resources>
<ListView x:Name="ListViewInstance" ItemsSource="{Binding ListEnum}" Margin="0,51,0,0">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding, Converter={StaticResource EnumToStringConverter}}" HorizontalAlignment="Center" FontSize="18"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
You can use the enum with attributes and the create a list for listbox using extension methods.You can refer the below code.
<ListBox Width="200" Height="25" ItemsSource="{Binding ComboSource}"
DisplayMemberPath="Value"
SelectedValuePath="Key"/>
public class MainViewModel
{
public List<KeyValuePair<RentStatus, string>> ComboSource { get; set; }
public MainViewModel()
{
ComboSource = new List<KeyValuePair<RentStatus, string>>();
RentStatus re=RentStatus.Active;
ComboSource = re.GetValuesForComboBox<RentStatus>();
}
}
public enum RentStatus
{
[Description("Preparation description")]
Preparation,
[Description("Active description")]
Active,
[Description("Rented to people")]
Rented
}
public static class ExtensionMethods
{
public static List<KeyValuePair<T, string>> GetValuesForComboBox<T>(this Enum theEnum)
{
List<KeyValuePair<T, string>> _comboBoxItemSource = null;
if (_comboBoxItemSource == null)
{
_comboBoxItemSource = new List<KeyValuePair<T, string>>();
foreach (T level in Enum.GetValues(typeof(T)))
{
string Description = string.Empty;
FieldInfo fieldInfo = level.GetType().GetField(level.ToString());
DescriptionAttribute[] attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0)
{
Description = attributes.FirstOrDefault().Description;
}
KeyValuePair<T, string> TypeKeyValue = new KeyValuePair<T, string>(level, Description);
_comboBoxItemSource.Add(TypeKeyValue);
}
}
return _comboBoxItemSource;
}
}

Combobox Empty Item

I am trying to bind a combobox with an empty item. For this I am using the CompositeCollection as I have read in many of the questions.
This is working currently. However using a composite collection messes up the grouping that I had. Either I get the blank in the combobox or I get the grouping. I want both be available.
Here is the sample code that I have been playing with:
Xaml
<Window.Resources>
<local:CourseConverter x:Key="CourseConverter" />
<DataTemplate DataType="{x:Type local:Course}">
<TextBlock Text="{Binding Path=CourseName}" />
</DataTemplate>
<CollectionViewSource x:Key="CoursesCVS" Source="{Binding Courses}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Major" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="0.2*"/>
<RowDefinition Height="Auto "/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Text="Select Course" Grid.Row="1" Margin="15,5,0,5" Width="200" />
<ComboBox Grid.Row="2" Width="200" Margin="15,5,0,5"
ItemsSource="{Binding Source={StaticResource CoursesCVS}}"
SelectedItem="{Binding SelectedCourse, Converter={StaticResource CourseConverter}}">
<ComboBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ComboBox.GroupStyle>
<!--<ComboBox.ItemsSource>
<CompositeCollection>
<ComboBoxItem Content="" />
<CollectionContainer Collection="{Binding Source={StaticResource CoursesCVS}}" />
</CompositeCollection>
</ComboBox.ItemsSource>-->
</ComboBox>
<TextBlock Text="{Binding SelectedCourse.CourseName}" Grid.Row="3" Margin="15,5,0,5" Width="200" />
</Grid>
viewmodel and supporting classes:
public class Course
{
public int CourseID { get; set; }
public string CourseName { get; set; }
public string Major { get; set; }
}
public class MainWindowViewModel : INotifyPropertyChanged
{
public MainWindowViewModel()
{
_courses = new List<Course>();
_courses.Add(new Course { CourseID = 1, CourseName = "Math 107", Major = "Math" });
_courses.Add(new Course { CourseID = 1, CourseName = "Biology 110", Major = "Biology" });
_courses.Add(new Course { CourseID = 1, CourseName = "Chemistry 123", Major = "Chemistry" });
_courses.Add(new Course { CourseID = 1, CourseName = "Spanish 112", Major = "Biology" });
_courses.Add(new Course { CourseID = 1, CourseName = "Molecular 114", Major = "Biology" });
}
private List<Course> _courses;
public List<Course> Courses
{
get { return _courses; }
set { _courses = value; OnPropertyChanged(); }
}
private Course _selectedCourse;
public Course SelectedCourse
{
get { return _selectedCourse; }
set { _selectedCourse = value; OnPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName]string propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class CourseConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is Course)
return value;
else
return null;
}
}
Is there something that I am missing to make it work with and without grouping. For removing grouping, I remove inline ItemSource declaration and use the commented code. This shows the blank.
In WPF, we work with data items, not UI elements. We declare DataTemplates to define what our data items should look like in any UI container. Using this method, we let the Framework take care of displaying the UI and we concentrate on the data. So, to display an empty UI element in WPF, you just have to add an empty data item into your collection and let the DataTemplate do its work:
_courses.Add(new Course());
This would be rendered simply as an empty item as it has no data to be displayed in any data bound controls. So try declaring a DataTemplate in the ComboBox.ItemTemplate property, or even just setting the DisplayMemberPath to the relevant Course property if you only want to display one value.

Categories

Resources