Getting keys as Combobox selected items in Dictionary bind - c#

I have a Dictionary binded with ComboBox. For example let's say the Dictionary has data set like this:
{1,item1}
{2,item2}
Now when you select any of the option the ComboBox.SelectedItem should get only the integer key not the value.
Here is the code:
public static Dictionary<int, string> newDict { get; set; }
newDict = MTMInteraction.getPlanId();
txtPlanId.ItemsSource = newDict;
XAML code:
<ComboBox x:Name="txtPlanId" ItemsSource="{Binding newDict}" IsEditable="True" Margin="-2,0,79,3" Text="Enter ID" HorizontalAlignment="Center" VerticalAlignment="Bottom"/>

Instead of SelectedItem use SelectedValuePath/SelectedValue properties. Set SelectedValuePath against ComboBox to property that you want to get and bind SelectedValue to some property in view model
<ComboBox
x:Name="txtPlanId"
ItemsSource="{Binding newDict}"
...
SelectedValuePath="Key"
SelectedValue="{Binding SomeIntProperty}"/>
or in code txtPlanId.SelectedValue should give you Key part of your KeyValuePair<int,string>

DisplayMemberPath="Key"
More than you asked but it does answer the question (I think)
<Window x:Class="BindDictionary.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource self}}"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListBox ItemsSource="{Binding Path=DLBS, Mode=OneWay}"
DisplayMemberPath="Key"
SelectedValuePath="Key"
SelectedValue="{Binding Path=DLBSkey}"/>
</Grid>
</Window>
using System.ComponentModel;
namespace BindDictionary
{
public partial class MainWindow : Window , INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
private byte? dlBSkey = 1;
private Dictionary<byte, string> dlBS = new Dictionary<byte, string>() { { 1, "one" }, { 2, "two" }, { 5, "five" } };
public MainWindow()
{
InitializeComponent();
}
public Dictionary<byte, string> DLBS { get { return dlBS; } }
public byte? DLBSkey
{
get { return dlBSkey; }
set
{
if (dlBSkey == value) return;
dlBSkey = value;
NotifyPropertyChanged("DLBSkey");
}
}
}
}

Related

Binding does not populate values in combo box

I am able to populate List in the code behind but the values do not bind to the combobox in xaml
Here is the sample code
public IEnumerable<string> _AllProperties;
public IEnumerable<string> AllProperties
{
get { return _AllProperties; }
set
{
if (_AllProperties == value) return;
_AllProperties = value;
OnPropertyChanged("AllProperties");
}
}
public MainWindow()
{
InitializeComponent();
// Create a list
List<string> PropertyValues = new List<string>();
// Add a range of items
string[] values = { "BlackOilFluid",
"Boundary",
"Casing",
"CheckValve",
"Choke",
"Completion",
"CompletionConingPoint",
"CompletionModel",
"CompositionalFluid",
"Compressor",
"EngineKeywords",
"ESP",
"Expander",
"FileBasedFluid",
"Flowline",
"FluidComponent",
"GasLiftInjection",
"GenericBooster",
"GenericEquipment",
"GravelPack",
"HeatExchanger",
"Injector",
"IPRBackPressure",
"IPRDarcy",
"IPRFetkovitch",
"IPRForchheimer",
"IPRHorizontalPI",
"IPRHydraulicFracture",
"IPRJones",
"IPRPIModel",
"IPRPSSBabuOdeh",
"IPRSSJoshi",
"IPRVogel",
"Junction",
"Liner",
"MeasurementPoint",
"MFLFluid",
"MultiphaseBooster",
"MultiplierAdder",
"NetworkSim",
"NodalAnalysisOp",
"OneSubseaBooster",
"OpenHole",
"Packer",
"PCP",
"PTProfileSim",
"Pump",
"PVTFluid",
"RodPump",
"SinglephaseSeparator",
"Sink",
"SlidingSleeve",
"Source",
"Study",
"SubsurfaceSafetyValve",
"ThreePhaseSeparator",
"Tubing",
"TubingPlug",
"TwoPhaseSeparator",
"WaterTempVelocitySurvey",
"Well",
"WetGasCompressor" };
PropertyValues.AddRange(values);
_AllProperties = PropertyValues;
}
I expect the combo box to be populate with the above defined list of strings.
Here is the XAML code:
<ComboBox Grid.Column="2" x:Name="propvar" HorizontalAlignment="Center" VerticalAlignment="Center" Height="30" Width="100" Margin="5,5,5,5" IsTextSearchEnabled="True" FontSize="11"
ItemsSource="{Binding AllProperties}" SelectionChanged="Var_SelectionChanged"
SelectedItem="{Binding Propselected, NotifyOnValidationError=True, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" />
I do not quite understand why the values are not displayed in the combobox dropdown.
Try this:
Xaml:
<Window x:Class="TestChipDeleting.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"
mc:Ignorable="d"
Title="MainWindow"
Height="400"
Width="400">
<StackPanel VerticalAlignment="Center"
DataContext="{Binding}">
<ComboBox ItemsSource="{Binding Path=Properties}"
IsTextSearchEnabled="True"
FontSize="11"
Margin="8"
Height="30"
Width="200"
HorizontalAlignment="Center"
VerticalAlignment="Center"
SelectionChanged="Var_SelectionChanged"
SelectedItem="{Binding Propselected,
NotifyOnValidationError=True,
UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True,
ValidatesOnExceptions=True}"/>
</StackPanel>
</Window>
C#:
namespace Test
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
private void Var_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
}
}
public class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
BindItems();
}
private List<string> _properties;
public List<string> Properties
{
get { return _properties; }
set
{
if (value.Equals(_properties))
return;
_properties = value;
OnPropertyChanged(nameof(Properties));
}
}
private void BindItems()
{
string[] items =
{
"BlackOilFluid",
"Boundary",
"Casing",
"CheckValve",
"Choke",
"Completion",
"CompletionConingPoint",
"CompletionModel",
"CompositionalFluid",
"Compressor",
"EngineKeywords",
"ESP",
"Expander",
"FileBasedFluid",
"Flowline",
"FluidComponent",
"GasLiftInjection",
"GenericBooster",
"GenericEquipment",
"GravelPack",
"HeatExchanger",
"Injector",
"IPRBackPressure",
"IPRDarcy",
"IPRFetkovitch",
"IPRForchheimer",
"IPRHorizontalPI",
"IPRHydraulicFracture",
"IPRJones",
"IPRPIModel",
"IPRPSSBabuOdeh",
"IPRSSJoshi",
"IPRVogel",
"Junction",
"Liner",
"MeasurementPoint",
"MFLFluid",
"MultiphaseBooster",
"MultiplierAdder",
"NetworkSim",
"NodalAnalysisOp",
"OneSubseaBooster",
"OpenHole",
"Packer",
"PCP",
"PTProfileSim",
"Pump",
"PVTFluid",
"RodPump",
"SinglephaseSeparator",
"Sink",
"SlidingSleeve",
"Source",
"Study",
"SubsurfaceSafetyValve",
"ThreePhaseSeparator",
"Tubing",
"TubingPlug",
"TwoPhaseSeparator",
"WaterTempVelocitySurvey",
"Well",
"WetGasCompressor"
};
Properties = new List<string>(items);
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}

C# WPF - ComboBox of databound ObservableCollection of classes

Recently I started converting a proof of concept UWP app to a working WPF app.
What I want is to have two dropdowns (combobox) of "characters" I can choose, I want them databound to an ObservableCollection property, where the characters I selected is stored in a different Character property for player 1 then player 2.
I had databinding on dropdowns working in the UWP app, but I can't get it to work in the WPF app.
In the WPF app, the comboboxes stay empty and I can't select an option.
I tried following the answer to this question, but I think I'm missing something: Binding a WPF ComboBox to a custom list
Here is the code, kept minimal:
Character.cs
public class Character : INotifyPropertyChanged
{
private int _id;
public int Id
{
get
{
return _id;
}
set
{
_id = value;
}
}
private string _name;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public Character(int id, string name)
{
Id = id;
Name = name;
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
MainWindow.xaml
<Window x:Class="SmashWiiUOverlayManager.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:local="clr-namespace:SmashWiiUOverlayManager"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Grid.Row="0">
<ComboBox
Name="Player1CharacterDropdown"
ItemsSource="{Binding CharacterList, Mode=TwoWay}"
SelectedItem="{Binding Player1SelectedCharacter, Mode=TwoWay}"
SelectedValuePath="Name"
DisplayMemberPath="Name"
Width="144">
</ComboBox>
</StackPanel>
<StackPanel Grid.Column="0" Grid.Row="1" HorizontalAlignment="Right">
<ComboBox
Name="Player2CharacterDropdown"
ItemsSource="{Binding CharacterList, Mode=TwoWay}"
SelectedItem="{Binding Player2SelectedCharacter, Mode=TwoWay}"
SelectedValuePath="Character"
DisplayMemberPath="Name"
Width="144">
</ComboBox>
</StackPanel>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window, INotifyPropertyChanged
{
private ObservableCollection<Character> _characterList;
public ObservableCollection<Character> CharacterList
{
get
{
return _characterList;
}
set
{
_characterList = value;
}
}
private Character _player1SelectedCharacter;
public Character Player1SelectedCharacter
{
get
{
return _player1SelectedCharacter;
}
set
{
_player1SelectedCharacter = value;
}
}
private Character _player2SelectedCharacter;
public Character Player2SelectedCharacter
{
get
{
return _player2SelectedCharacter;
}
set
{
_player2SelectedCharacter = value;
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public MainWindow()
{
this.DataContext = this;
InitializeComponent();
CharacterList = new ObservableCollection<Character>
{
new Character(0, "Mario"),
new Character(1, "Luigi"),
new Character(2, "Wario"),
};
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
This code currently leaves the comboboxes empty.
When I use:
Player1CharacterDropdown.ItemsSource = new ObservableCollection<Character>
{
new Character(0, "Mario", ".\\images\\mario.png"),
new Character(1, "Luigi", ".\\images\\luigi.png"),
new Character(2, "Wario", ".\\images\\wario.png"),
};
... the combox gets filled, but it's databound to the property, which is what I would like.
What am I missing here?

MVVM ListView MultiBinding SelectedItems + SelectedItem (ListView) + SelectedItem (ComboBox) to TextBox.Text. Does not update properly

I am trying to MultiBind the SelectedItems (ListView) and SelectedItem or SelectedCategory in my ViewModel (ComboBox) to a readonly TextBox. The OutputConverter just checks if there is at least one item selected (ListView) and TypeData (ComboBox) selected before creating the text for the TextBox.
However, if I try it only with that the TextBox only updates when the ComboBox.SelectedItem changes not when SelectedItems inside ListView changes.
So I also included from my ViewModel SelectedEntry (ListView) (which is same as SelectedItem) as a Binding for the MultiBinding.
Now I get the following:
Explanation:
The Selection is always one step behind and uses the previous SelectedItems from the ListView to bind to the TextBox.Text. Even when I am selecting multiple entries via CTRL or Shift + Click. However, if the ComboBox.SelectedItem changes it updates the TextBox as intended.
How do I get the behaviour if the Selection changes inside the ListView the TextBox immediately updates its content accordingly (preferably I would like to use the SelectedEntries of my ViewModel and not the SelectedItems of the ListView and if possible in a MVVM compliant way)?
Edit:
I noticed when the Converter is called the SelectedEntry (implements
INotifyPropertyChanged) is updated but the SelectedEntries
(ObservableCollection and implements INotifyPropertyChanged) is not.
I have also included a SelectionChanged event that calls a command
and passes the SelectedItems as a parameter to the command. If that
might help but I would rather like to have a proper binding that
updates accordingly.
Code:
Model TypeData (ComboBox):
public class TypeData : INotifyPropertyChanged
{
public enum Type
{
NotSet = '0',
A = 'A',
B = 'B',
C = 'C'
}
private string name;
public string Name
{
get { return name; }
set
{
name = value;
//OnPropertyChanged("Name");
OnPropertyChanged(nameof(Name));
}
}
private Type category;
public Type Category
{
get { return category; }
set { category = value; }
}
public TypeData(string name)
{
Name = name;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public override string ToString()
{
return Name;
}
}
Model Entry (ListView):
public class Entry : INotifyPropertyChanged
{
private string title;
public string Title
{
get { return title; }
set
{
title = value;
OnPropertyChanged(nameof(Title));
}
}
private string author;
public string Author
{
get { return author; }
set
{
author = value;
OnPropertyChanged(nameof(Author));
}
}
private string year;
public string Year
{
get { return year; }
set
{
year = value;
OnPropertyChanged(nameof(Year));
}
}
public Entry(string title, string author, string year)
{
Title = title;
Author = author;
Year = year;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
ViewModel:
public class MainViewModel
{
public ObservableCollection<Entry> Entries { get; set; }
public Entry SelectedEntry { get; set; }
public ObservableCollection<Entry> SelectedEntries { get; set; }
public ObservableCollection<TypeData> Types { get; set; }
private TypeData selectedCategory;
public TypeData SelectedCategory { get; set; }
public RelayCommand<object> SelectionChangedCommand { get; set; }
public MainViewModel()
{
Entries = new ObservableCollection<Entry>
{
new Entry("Title1", "Author1", "Year1"),
new Entry("Title2", "Author2", "Year2"),
new Entry("Title3", "Author3", "Year3"),
new Entry("Title4", "Author4", "Year4"),
};
Types = new ObservableCollection<TypeData>
{
new TypeData("A"),
new TypeData("B"),
new TypeData("C"),
};
SelectionChangedCommand = new RelayCommand<object>(items =>
{
var selectedEntries = (items as ObservableCollection<object>).Cast<Entry>();
SelectedEntries = new ObservableCollection<Entry>(selectedEntries);
});
}
}
XAML:
<Window x:Class="MvvmMultiBinding.View.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:local="clr-namespace:MvvmMultiBinding"
xmlns:m="clr-namespace:MvvmMultiBinding.Model"
xmlns:vm="clr-namespace:MvvmMultiBinding.ViewModel"
xmlns:conv="clr-namespace:MvvmMultiBinding.View.Converter"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<vm:MainViewModel></vm:MainViewModel>
</Window.DataContext>
<Window.Resources>
<conv:OutputConverter x:Key="OutputConverter"/>
</Window.Resources>
<Grid>
<DockPanel>
<ListView Name="ListViewEntries" ItemsSource="{Binding Entries}" SelectedItem="{Binding SelectedEntry}" DockPanel.Dock="Top">
<ListView.View>
<GridView>
<GridViewColumn Header="Title" Width="250" DisplayMemberBinding="{Binding Title}" />
<GridViewColumn Header="Author" Width="150" DisplayMemberBinding="{Binding Author}" />
<GridViewColumn Header="Year" Width="50" DisplayMemberBinding="{Binding Year}" />
</GridView>
</ListView.View>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding DataContext.SelectionChangedCommand, ElementName=ListViewEntries}"
CommandParameter="{Binding SelectedItems, ElementName=ListViewEntries}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListView>
<ComboBox ItemsSource="{Binding Types}" SelectedItem="{Binding SelectedCategory}" MinWidth="200" DockPanel.Dock="Right"/>
<TextBox IsReadOnly="True" DockPanel.Dock="Left">
<TextBox.Text>
<MultiBinding Converter="{StaticResource OutputConverter}">
<Binding ElementName="ListViewEntries" Path="SelectedItems" Mode="OneWay"/>
<!--<Binding Path="SelectedEntries" Mode="OneWay"/>-->
<Binding Path="SelectedCategory" Mode="OneWay"/>
<!-- Without it converter is not called after selection changes -->
<Binding Path="SelectedEntry" Mode="OneWay"/>
</MultiBinding>
</TextBox.Text>
</TextBox>
</DockPanel>
</Grid>
OutputConverter:
public class OutputConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
System.Collections.IList items = (System.Collections.IList)values[0];
var entries = items.Cast<Entry>();
TypeData type = values[1] as TypeData;
List<Entry> selectedEntries = new List<Entry>();
foreach (var entry in entries)
{
selectedEntries.Add(entry);
}
StringBuilder sb = new StringBuilder();
// ComboBox and Selection must not be empty
if (type != null && selectedEntries.Count > 0)
{
foreach (var selectedEntry in selectedEntries)
{
sb.AppendFormat("{0} {1}\n\n", selectedEntry.Author, type);
}
}
return sb.ToString();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I would suggest giving your Entry Class an IsSelected property (Bound to the IsSelectedProperty of a ListViewItem). When the selection changes you can just iterate through your collection (bound to the ListView) and check if they are selected or not. Like this (excuse the MvvmLight, RelayCommand = ICommand, ViewModelBase extends ObservableObject):
ViewModel:
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
TestItemCollection = new ObservableCollection<TestItem>
{
new TestItem("Test1"),
new TestItem("Test2"),
new TestItem("Test3")
};
}
private TestItem m_selectedItemProperty;
public TestItem SelectedItemProperty
{
get
{
return m_selectedItemProperty;
}
set
{
m_selectedItemProperty = value;
RaisePropertyChanged("SelectedItemProperty");
}
}
public ObservableCollection<TestItem> TestItemCollection
{
get;
set;
}
public RelayCommand SelectionChanged
{
get { return new RelayCommand(OnSelectionChanged); }
}
private void OnSelectionChanged()
{
foreach (var item in TestItemCollection)
{
if (item.IsSelected)
Console.WriteLine("Name: " + item.Name);
}
}
}
I printed the names out here but you can also just add them to a string property and bind it to your text box or add the items to a collection (maybe bound to a ListBox or ItemsControl showing the selected entries).
TestItem:
public class TestItem : ObservableObject
{
public TestItem(string a_name)
{
m_name = a_name;
}
private string m_name;
public string Name
{
get
{
return m_name;
}
set
{
m_name = value;
RaisePropertyChanged("Name");
}
}
private bool m_isSelected;
public bool IsSelected
{
get
{
return m_isSelected;
}
set
{
m_isSelected = value;
RaisePropertyChanged("IsSelected");
}
}
}
View:
<Window x:Class="WpfAppTests.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:local="clr-namespace:WpfAppTests"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
mc:Ignorable="d"
xmlns:modelNoMvvmLight="clr-namespace:WpfAppTests"
xmlns:modelMvvmLight="clr-namespace:WpfAppTests.ViewModel"
Title="MainWindow" Height="350" Width="525" >
<Window.DataContext>
<modelMvvmLight:MainViewModel/>
</Window.DataContext>
<StackPanel>
<ListView Name="ListView" ItemsSource="{Binding TestItemCollection}" SelectedItem="{Binding SelectedItemProperty}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SelectionChanged}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem" >
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
</StackPanel>
</Window>

Not able to see the selected value in the combobox

I'm working on a C#/WPF application.
I'm invoking ProcessServiceResponse() method in MainViewModel on click of a button.
SelectedCountry property value is correctly getting set in this method.The country list combobox is also showing the list of countries.
But somehow, am not seeing a selected value(for e.g. SG) in the country dropdown list.
Any ideas as to what am I missing here please?
Let me know if you need any other details around the code.
Thanks.
Here's my code.
MainWindow View:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:MainViewModel="clr-namespace:MyTool.ViewModels"
xmlns:ViewModel="clr-namespace:MyTool.ViewModel.Bonds"
xmlns:View="clr-namespace:MyTool" x:Class="MyTool.MainWindow"
Title="{Binding DisplayName, Mode=OneWay}" ResizeMode="CanMinimize" WindowStartupLocation="CenterScreen" Height="600" Width="1100">
<Window.DataContext>
<MainViewModel:MainWindowViewModel/>
</Window.DataContext>
<ComboBox Margin="1,0" ItemsSource="{Binding MyViewModel.CountryList,UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="Description" SelectedValuePath="Code" SelectedItem="{Binding MyViewModel.SelectedCountry, Mode=TwoWay}" TabIndex="2" Grid.Row="7" Grid.Column="2" HorizontalAlignment="Left" Height="23" VerticalAlignment="Top" Width="180" />
MainViewModel:
public MainWindowViewModel()
{
MyAttributes = new MyViewModel();
}
public object MyAttributes
{
get { return m_myViewModel; }
set
{
m_myViewModel = value;
OnPropertyChanged("MyAttributes");
}
}
public void ProcessServiceResponse()
{
var destination = new MyViewModel();/
Type destinationType = destination.GetType();
PropertyInfo[] destinationTypePI = destinationType.GetProperties();
string propertyName = string.Empty;
object propertyValue = null;
foreach (var pinfo in sourcePI)
{
propertyName = pinfo.Name.Trim();
var matchingItem = destinationTypePI.ToList().Where(d => d.Name == propertyName);
if (matchingItem != null && matchingItem.Count() > 0)
{
propertyValue = pinfo.GetValue(serviceResponse.lst_DKSecurities[0]);
matchingItem.FirstOrDefault().SetValue(destination, propertyValue);
}
}
this.MyAttributes = destination;
}
MyViewModel:
namespace MyTool.ViewModels;
public class MyViewModel
{
public MyViewModel
{
this.CountryList = GetCountryList();
}
public string SelectedCountry
{
get
{
return m_selectedCountry;
}
set
{
m_selectedCountry = value;
}
}
}
After you fill the CountryList, assign to the SelectedCountry the list item you want to display:
CountryList = new ObservableCollection<string> {"A", "B", "C"};
SelectedCountry = CountryList[0];
<ComboBox ItemsSource="{Binding CountryList, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectedCountry, Mode=TwoWay}" />

WPF change datagrid combobox ItemsSource to value of another datagrid combox

I have a datagrid with 2 DataGridComboxClolumn elements and want to change the ItemsSource of the second when the value of the first is changed. How can I set up such a binding?
Note: the datagrid has it's own itemssource and the data is hierachical following:
ItemsSource of the datagrid (list op type Channel):
public class Root
{
public List<Channel> Ch { get; set; } // This is the ItemsSource of the datagrid itself
}
public class Channel
{
public Settings TheSettings { get; set; }
}
The thing I want to accomplish is that the TheSettings is set with selected value of the second ComboBox. This is easyly done by setting the ComboBox ItemsSource. Although I require that the ItemsSource of the second combox to be dynamic. E.g. it has to change to the source selected in the FIRST combobox. How can this be done?
Option 1
You can create your a DataGridTemplateColumn with two combobox. The first one can be populated with items from the main ViewModel and the second one will be bound to the items of the SelectedItem of the first.
<Window x:Class="WpfApplication1.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:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:WpfApplication1"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="525"
Height="350"
mc:Ignorable="d">
<Window.DataContext>
<local:Root/>
</Window.DataContext>
<Grid x:Name="LayoutRoot">
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Ch}" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ComboBox Width="100" ItemsSource="{Binding DataContext.ComboBox1Items, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" x:Name="ComboBox1"/>
<ComboBox Grid.Column="1" Width="100" ItemsSource="{Binding SelectedItem.Items, ElementName=ComboBox1}" SelectedItem="{Binding TheSettings}" IsEditable="True" IsReadOnly="True"/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
public class ComboBox1Item
{
public string Label { get; set; }
public List<string> Items { get; set; }
public override string ToString()
{
return this.Label;
}
}
public class Root
{
public List<Channel> Ch { get; set; }
public List<ComboBox1Item> ComboBox1Items { get; set; }
public Root()
{
this.Ch = new List<Channel>(){
new Channel(){ TheSettings = "Settings1"},
new Channel(){ TheSettings = "Settings2"},
};
this.ComboBox1Items = new List<ComboBox1Item>{
new ComboBox1Item(){ Label = "Item1",
Items = new List<string>(){ "Settings1", "Settings2"}
},
new ComboBox1Item(){ Label = "Item2",
Items = new List<string>(){ "Settings3", "Settings4"}
}
};
}
}
Option 2
Create an object to wrap your Channel objects, and put in it the logic to allow one combobox to drive the items of the other:
public class ChannelWrapper : INotifyPropertyChanged
{
#region INotifyPropertyChanged values
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
private object comboBox1SelectedItem;
public object ComboBox1SelectedItem
{
get { return this.comboBox1SelectedItem; }
set
{
if (this.comboBox1SelectedItem != value)
{
this.comboBox1SelectedItem = value;
this.OnPropertyChanged("ComboBox1SelectedItem");
// Put the logic to change the items available in the second combobox here
if (value == "Value1")
this.ComboBox2ItemsSource = new List<object>() { "Setting1", "Setting2" };
if (value == "Value2")
this.ComboBox2ItemsSource = new List<object>() { "Setting3", "Setting4" };
}
}
}
private List<object> comboBox2ItemsSource;
public List<object> ComboBox2ItemsSource
{
get { return this.comboBox2ItemsSource; }
set
{
if (this.comboBox2ItemsSource != value)
{
this.comboBox2ItemsSource = value;
this.OnPropertyChanged("ComboBox2ItemsSource");
}
}
}
public Channel Ch { get; set; }
}
Your Root class would then expose a collection of wrappers instead a collection of channels. Your DataGrid would have 2 ComboBoxColumns. The SelectedItem of the first would be bound to the property "ComboBox1SelectedItem" of the wrapper. The ItemsSource of the second would be bound to the property "ComboBox2ItemsSource" of the wrapper and the SelectedItem of the second column would be bound the setting of the Channel instance of the wrapper, with the path "Ch.TheSettting".

Categories

Resources