How to connect a WPF Combobox to KeyValue-Dictionary - c#

I'm struggeling with a WPF Combobox.
My job is to let the user select an entry, that should be stored in a variable and additionally it is possible, that this variable is changed programatically, so the combobox should selct the corresponding item.
First my code:
public KeyValuePair<string, string> SelectedLanguage { get; set; } //the selected item
public Dictionary<string, string> Languages { get; set; } //the list of combobox items
private void loadLanguages(List<string> languages)
{
Languages = new Dictionary<string, string>();
foreach(string language in languages)
{
//set initial selected item
if(language=="de_DE") SelectedLanguage = new KeyValuePair<string, string>(language, getLanguageDescription(language));
Languages.Add(language, getLanguageDescription(language));
}
}
private string getLanguageDescription(string language)
{
switch (language)
{
case "de_DE_Match":
return "Deutsch";
case "fr_FR_Match":
return "Französisch (Frankreich)";
case "nl_NL_Match":
return "Niederländisch";
case "en_EN_Match":
return "Englisch (GB)";
default:
return "unbekannt";
}
}
And this is my WPF:
<ComboBox x:Name="cbLanguages"
ItemsSource ="{Binding Languages}"
DisplayMemberPath="Value"
SelectedValuePath="Key"
SelectedValue="{Binding SelectedLanguage}" />
<TextBox Text="{Binding SelectedLanguage.Value, Mode=OneWay}"/>
Now the List is shown as expected but there is no item selected, inside the textbox, the SelectedLanguage is shown correctly but not changed if selection of the combobox changes.
So how can I programmatically change the selected item and how can I show the selected item in the textbox? Shouldn't it be enough, if I change the SelectedLanguage?

ItemsSource ="{Binding Languages}"
ok, so you have a combo box that contains a collection of KeyValuePairs, since Languages is a dictionary.
DisplayMemberPath="Value"
This says that the text displayed for each item will be the Value of the KeyValuePair, (the language description)
SelectedValuePath="Key"
This says that when selecting items with SelectedValue, we should be comparing against the Key of the KeyValuePair. (the language code)
SelectedValue="{Binding SelectedLanguage}" />
This binds the selection to SelectedLanguage, which should be a language code (string) as we just specified. However, SelectedLanguage is not a string, but a KeyValuePair. Since your ItemsSource is already a collection of KeyValuePairs, you don't need to worry about SelectedValuePath and SelectedValue at all, you can just use SelectedItem.
<ComboBox x:Name="cbLanguages"
ItemsSource ="{Binding Languages}"
SelectedItem="{Binding SelectedLanguage}"
DisplayMemberPath="Value" />

Related

WPF, Listbox items as source to other Listbox

Let's say you have one Listbox in WPF with items such as 1,2,3,4,5 etc. How do you make another Listbox, right next to first one, that shows its items according to selection in the first Listbox? So if you select "item 2" in Listbox you'd get 2A, 2B,2C etc in Listbox2, if you select "item 3" you'd get 3A, 3B, 3C etc in Listbox3
Can't embed the picture yet but here's the example of what i need
There is an example of how to implement such cascading ComboBoxes according to the recommended MVVM design pattern available here: https://blog.magnusmontin.net/2013/06/17/cascading-comboboxes-in-wpf-using-mvvm/
You could bind the SelectedItem property of the first ListBox to a source property of your view model. In the setter of this one you then set another collection property that you bind the ItemsSource property of the second ListBox to, e.g.:
<ListBox ItemsSource="{Binding Numbers}" SelectedItem="{Binding SelectedNumber}" />
<ListBox ItemsSource="{Binding SubNumbers}" />
private object _selectedNumber;
public object SelectedNumber
{
get { return _selectedNumber; }
set
{
_selectedNumber = value;
NotifyPropertyChanged();
//set items
SubNumbers = new List<string> { "3A", "3B", "..." };
NotifyPropertyChanged("SubNumbers");
}
}
Make sure that your view model class implements the INotifyPropertyChanged interface and raises change notifications for this to work: https://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged(v=vs.110).aspx
Alternatively, if your model classes are defined in such a way that each item in the first ListBox has a collection property that returns its related items, you could bind the second ListBox directly to a property of the SelectedItem in the first one:
<ListBox x:Name="lb1" ItemsSource="{Binding Numbers}"/>
<ListBox x:Name="lb2" ItemsSource="{Binding SelectedItem.SubProperty, ElementName=lb1}" />

Bind multiple dropdowns from a single list

In MVC, I have a List<CityModel> which contains CityID, City, StateID, State, CountryID, Country. By using the below code I'm able to get the list of cites:
IEnumerable<SelectListItem> objCityList;
IEnumerable<SelectListItem> objStateList;
IEnumerable<SelectListItem> objCountryList;
using (CityModel objCityModel = new CityModel())
{
List<CityModel> cityList = objCityModel.getCityList();
objCityList = cityList.AsEnumerable().Select(m => new SelectListItem() {
Text = m.City,
Value = Convert.ToString(m.CityID)
});
}
How can I bind distinct State and Country from cityList?
From what I understand, you want to extract the State and Country lists from the City list. To do that, you can use something like this
objStateList = cityList.GroupBy(item => item.StateID, (key, items) => new SelectListItem
{
Text = items.First().State,
Value = Convert.ToString(key)
});
objCountryList = cityList.GroupBy(item => item.CountryID, (key, items) => new SelectListItem
{
Text = items.First().Country,
Value = Convert.ToString(key)
});
You just need to use the one list, but change the which property is displayed in the combobox. The selected item is of the same type as the objects on the list.
In your view model just expose the CityList property and then bind the comboboxes ItemsSource to that property.
You can do all this in XAML for instance, like this:
<ComboBox ItemsSource="{Binding CityList}"
DisplayMemberPath="City"
SelectedItem={Binding SelectedCity, Mode=TwoWay>
</ComboBox>
<ComboBox ItemsSource="{Binding CityList}"
DisplayMemberPath="County"
SelectedItem={Binding SelectedCounty, Mode=TwoWay>
</ComboBox>
<ComboBox ItemsSource="{Binding CityList}"
DisplayMemberPath="State"
SelectedItem={Binding SelectedState, Mode=TwoWay>
</ComboBox>

Set an item on a WPF Combobox when ItemSource is binded to an Enum

I am having trouble assigning the a combobox item by using an enum value that the combobox source is assigned to.
The XAML
<ComboBox HorizontalAlignment="Left"
x:Name="cmbName"
VerticalAlignment="Top"
Width="120" Margin="79,48,0,0">
<ComboBox.ItemsSource>
<CompositeCollection>
<ListBoxItem Content="Please Select"/>
<CollectionContainer Collection="{Binding Source={StaticResource Enum}}" />
</CompositeCollection>
</ComboBox.ItemsSource>
</ComboBox>
The C# that is trying to set the combobox to an item in the enum
// The problem, the assignment doesn't work.
cmbName.SelectedItem = Enum.value;
I can only set a item by using the combobox SelectedIndex
cmbName.SelectedIndex = 2;
But this is hardcoding the index so if the enum changes, so will the value.
So how can I set the combobox by the enum value?
Thanks
It's very hard to tell what your problem is because you haven't fully documented your scenario. As such, all that I can do is to show you how to do what you want. As I prefer to work with properties, I won't be using any Resources for this example, but I'm sure that you'll still be able to relate this solution to your problem.
So, first we have a test enum and some properties and some initialisation:
public enum TestEnum
{
None, One, Two, Three
}
private TestEnum enumInstance = TestEnum.None;
public TestEnum EnumInstance
{
get { return enumInstance; }
set { enumInstance = value; NotifyPropertyChanged("EnumInstance"); }
}
private ObservableCollection<TestEnum> enumCollection = new ObservableCollection<TestEnum>() { TestEnum.None, TestEnum.One, TestEnum.Two, TestEnum.Three };
public ObservableCollection<TestEnum> EnumCollection
{
get { return enumCollection; }
set { enumCollection = value; NotifyPropertyChanged("EnumCollection"); }
}
...
EnumCollection.Add(TestEnum.One);
EnumCollection.Add(TestEnum.Two);
EnumCollection.Add(TestEnum.Three);
EnumInstance = TestEnum.Three;
Then we have a ComboBox:
<ComboBox Name="ComboBox" ItemsSource="{Binding EnumCollection}"
SelectedItem="{Binding EnumInstance}" />
If you run the application, then at this point the selected ComboBoxItem should read Three. Because the ComboBox.SelectedItem is data bound to the EnumInstance property, setting...:
EnumInstance = TestEnum.Two;
... is roughly the same as:
ComboBox.SelectedItem = TestEnum.Two;
Both of these would select the Two value in the ComboBox. However, note this example:
EnumInstance = TestEnum.None;
Setting either the EnumInstance or the ComboBox.SelectedItem property to TestEnum.None would have no effect in the UI because there is no TestEnum.None value in the data bound collection.
I apologise that my answer was descriptive enough, however, the reason why I haven't set my enum as a property as Sheridan has described below is that I need an extra string value in my combo which you can see is "Please Select" and unfortunately, I cannot put this in the enum.
But Sheridan's method and logic is the way to go if you want to do this.
However, for my problem, I simply just used
ComboBox.SelectedValue = Enum.Value.ToString();
Thanks

Datagrid ComboBox binding two values

I have a combobox which is populated from a list of names which were obtained from selecting from an Observable collection. However, associated with those names is an ID also in that Observable collection. The goal is when the user selects a new name (Say changes "John" to "Jill") I will be able to obtain the ID, not just the name. The only way I can think of doing this is storing the ID also in the combobox somehow. But I don't know how to do that with binding.
<DataGridTemplateColumn Header="Name ">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox x:Name="namescombo" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=DataContext.Names}"
SelectedItem="{Binding Name, UpdateSourceTrigger=PropertyChanged}" FontSize="12" Background="White" FontFamily="Cambria" BorderBrush="White" BorderThickness="0"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
C#
ObservableCollection<Name> Names = new ObservableCollection<Name>();
Name twofields = new Name();
var NamesQuery =
from p in dataEntities.Names
select new { p.Name, p.Id };
foreach (var p in NamesQuery)
{
Names.Add(new Name
{
ID = p.Id,
Name = p.Name
});
}
Names = Names.Select(p => p.Name).Distinct().ToList();
A ComboBox contains properties for both the DisplayMemberPath and the SelectedValuePath, so you could use it to tell the ComboBox to identify items by the "Id" property, but display the "Name" property to the user.
For example,
<DataGridTemplateColumn Header="Name ">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=DataContext.Names}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
SelectedValue="{Binding SelectedId}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
I would recommend using SelectedValue over SelectedItem because WPF compares SelectedItem by .Equals() which defaults to comparing items by reference, and if your SelectedItem is not the exact same reference as the item in your ItemsSource, it won't get selected.
For example, SelectedItem = new Person(1, "Test"); would probably not set the selected item correctly, while SelectedItem = ItemsSource[0] would since it refers to an item that exists in the ItemsSource.
Also, it frequently makes more sense to store just the Id of the selected item on a row instead of the entire object :)
You can bind directly to Name object collection and set DisplayMemberPath to Name property so that strings are shown on GUI but in essence you have complete object binded to comboBox.
This way you can bind SelectedItem to Name object and can access Id and Name property.
<ComboBox ItemsSource="{Binding Names}" // Collection of name objects.
DisplayMemberPath="Name"
SelectedItem="{Binding SelectedNameObject}"/> // Object of type Name.

Binding a Dictionary to a Combobox in WPF displays incorrectly

Resolved - The theme was interfering with the display
This is my first experience with WPF so there may be an obvious answer to this.
I'm trying to display a Month selection combobox where the month names are displayed, and when a selection is made the integer value is captured.
XAML
<ComboBox Margin="5" IsEditable="False"
IsEnabled="{Binding IsCompanyFileUploadPeriodEnabled}"
ItemsSource="{Binding StartMonths}"
DisplayMemberPath="Key"
SelectedValuePath="Value"
SelectedValue="{Binding SelectedStartMonthID}"
Width="50"></ComboBox>
Edit:
The ViewModel extends the Galasoft MvvmLight ViewModelBase, which provides the RaisePropertyChanged method.
ViewModel
Dictionary<string, int> _startMonths;
public Dictionary<string, int> StartMonths
{
get
{
if (_startMonths == null)
{
_startMonths = new Dictionary<string, int>();
for (int i = 1; i < 13; i++)
{
_startMonths.Add(System.Globalization.DateTimeFormatInfo.CurrentInfo.GetMonthName(i),
i);
}
}
return _startMonths;
}
}
int _selectedStartMonthID;
public int SelectedStartMonthID
{
get
{
return _selectedStartMonthID;
}
set
{
_selectedStartMonthID = value;
RaisePropertyChanged(() => SelectedStartMonthID);
}
}
But for some reason when I run the app the combobox is displaying as
[January, 1]
[February, 2]
etc
Does anyone know why it might be ignoring the DisplayMemberPath instruction? The SelectedValuePath setting seems to be working fine when an element is selected.
ComboBox DisplayMemberPath binding is broken by Themes BureauBlue and WhistlerBlue
http://wpf.codeplex.com/workitem/10129
Take a look at StringFormat.
MSDN StringFormat

Categories

Resources