How to add InputBindings to the Window after composition? - wpf

I am trying to master working with the MEF framework by implementing my own version of the well known Calculator example. The user interface is in WPF.
After composition the Viewmodel holds an ObservableCollection(Of IOperation) that is represented by a 'ListBox' of Buttons in the View. The text on each Button is a Char defined in IOperation as a property named Symbol. By binding the ListBox's SelectedItem to a property in the ViewModel I can just fire the Calculate method of the currently selected IOperation without knowing which Button was pressed. (Code illustrating this below.)
However, now I need to add InputBindings to the View , where each KeyBinding will be associated with the Symbol that is defined on the IOperation. It looks like that I cannot avoid implementing a Select Case(switch) statement to iterate through the Viewmodel's collection of IOperations to select the one on which the 'Calculate` method should be called.
Any ideas?
THE XAML:
<ListBox Grid.Column="1" Grid.Row="3" Name="OperationsList"
SelectedItem="{Binding ActiveOperation,Mode=TwoWay}"
ItemsSource="{Binding Operations}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid IsItemsHost="True"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Symbol}"
ToolTip="{Binding Description}"
Command="{Binding ElementName=OperationsList, Path=DataContext.ActivateOperation}"
Click="Button_Click_1"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
IOPERATION:
Public Interface IOperation
Function Calculate() As Double
Property Left As Double
Property Right As Double
Property Symbol As String
Property Description As String
End Interface
VIEWMODEL:
Private _activateOperation As Command
Public Property ActivateOperation As Command
Get
Return _activateOperation
End Get
Set(value As Command)
_activateOperation = value
OnPropertyChanged()
End Set
End Property
Public Property ActiveOperation As IOperation
Get
Return _compositor.ActiveOperation
End Get
Set(value As ICalculator)
_compositor.ActiveOperation = value
OnPropertyChanged()
End Set
End Property
Public ReadOnly Property Operations As ObservableCollection(Of IOperation)
Get
Return New ObservableCollection(Of ICalculator)(_compositor.Operations)
End Get
End Property
Private Sub DoActivateOperation(Optional parameter As Object = Nothing)
If Left.HasValue Then
MakeCalculation()
Else
Left = CDbl(Display)
ClearDisplay()
End If
End Sub

Code is a wee rough about the edges so adjust accordingly...
// in ViewModel, after composition
// map string symbols to input Keys (for brevity, using a Dictionary)
Dictionary<string, System.Windows.Input.Key> SymbolsToKeys = new Dictionary<string, Key>
{
{ "+", Key.Add },
// etc.
}
// compose the list (expose it via a public property)
// (ensure not to get confused: the KeyValuePair.Key is for the string symbol
// ... and the KeyValuePair.Value is for the Sys.Win.Input.Key value)
KeyBindings = (from symbolAndKey in SymbolsToKeys
join op in Operations on Equals(symbolAndKey.Key, op.Symbol)
select new KeyBinding(
new Command(op.Calculate)),
new KeyGesture(symbolAndKey.Value)
).ToList();
// in View, after Binding to ViewModel
var vm = DataContext as YourViewModel;
if(vm != null)
{
foreach(var keybinding in vm.KeyBindings){
this.InputBindings.Add(keybinding);
}
}

Related

WPF datagrid - Update property after combo item selected

I'm new to WPF and have a question about an issue I'm having with a combobox on a datagrid. When an item is selected from the combo, the bound property does not update like I expect.
Since I can't post images (this is the first time I've posted to SO), I'll attempt to explain. If I select "D00120" from the combo list, the combo will reflect the change but the grid property does not get changed.
Xaml:
<DataGridTemplateColumn Header="CPT Code" Width="75">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding PartBCombo,
RelativeSource={RelativeSource AncestorType=Window}}"
DisplayMemberPath="PartBLookup_CPTCode"
SelectedValuePath="PartBLookup_ProcedureDescription"
SelectedValue="{Binding PartBBilling_ProcedureName, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Property for combo items:
Partial Public Class PartBBilling_Combobox
Private _PartBLookup_CPTCode As String
Public Property PartBLookup_CPTCode As String
Get
Return _PartBLookup_CPTCode
End Get
Set(value As String)
_PartBLookup_CPTCode = value
End Set
End Property
Private _PartBLookup_ProcedureDescription As String
Public Property PartBLookup_ProcedureDescription As String
Get
Return _PartBLookup_ProcedureDescription
End Get
Set(value As String)
_PartBLookup_ProcedureDescription = value
End Set
End Property
End Class
Property for datagrid:
Private _PartBBilling_CPT As String
Public Property PartBBilling_CPT As String
Get
Return _PartBBilling_CPT
End Get
Set(value As String)
_PartBBilling_CPT = value
RaisePropertyChanged("PartBBilling_CPT")
End Set
End Property
Private _PartBBilling_ProcedureName As String
Public Property PartBBilling_ProcedureName As String
Get
Return _PartBBilling_ProcedureName
End Get
Set(value As String)
If _PartBBilling_ProcedureName <> value Then
_PartBBilling_ProcedureName = value
RaisePropertyChanged("PartBBilling_ProcedureName")
End If
End Set
End Property
When an item is selected, the UI displays correctly but the grid property does not get updated. Other than that, the combobox functions just fine.
Thanks for any help or advice.
First of i think to use something unique for your SelectedValuePath (used for identifying the correct selection)
DisplayMemberPath="PartBLookup_CPTCode"
SelectedValuePath="PartBLookup_CPTCode"
Also be careful and note that your SelectedValue will be bound to one of the values inside your PartBCombo collection whatever that type might be. So the Property you bind the SelectedValue to should be of that type.

databinding datagrid's comboboxes and textboxes to staticresource list

I've created a WPF vb.net form containing datagrid. Inside of datagrid I have two comboboxes and two textboxes, one combobox textbox pair for articles and another one for services. I should bind first combo to property of List(Of article) type and second to List(Of service) type where article is public class containing two public properties (articleId and articleName) and service is public class containing two public properties (serviseId and serviceName). Textboxes should display article and service names and comboboxes should display IDs. When combo selection is changed textbox text should change its value too.
List(Of article) and List(Of service) should be populated from database.
How could I do this, I know that solution is somewhere around me but can't catch it at all. There are two main problems, binding controls and populatin list from database.
If I need to post a part of code I'm gonna do this, just let me know.
Please help me to solve this situation,
thanks.
First of all - sorry, I am not very familiar with VB.Net, so my code is written in c#. Since I have no understanding of what you actually need, I can only offer you a following not really elegant solution.
For binding your ComboBox to the items from database you need to use a StaticResource, as I can see you did it, but maybe you defined it wrong, that's why it is not working for you. To get it work you need to have a class like this:
public class artiklsList : List<artikl>
{
public artiklsList()
{
this.Add(new artikl(1, "first")); //this is dummy items, you need to do a database stuff here
this.Add(new artikl(2, "second"));
this.Add(new artikl(3, "third"));
}
}
And a xaml like this:
<Window.Resources>
<my:artiklsList x:Key="source"></my:artiklsList>
</Window.Resources>
You also need update a cell with text when a ComboBox selection changed. It's not a simple task, because the easiest way to do this using Binding to the control using ElementName will not work in the DataGrid. What I did is kinda hacky anyway...
So, xaml is not very complicated:
<DataGrid Name="dgrStavke" AutoGenerateColumns="False" Height="160" Width="600" HorizontalAlignment="Left" Margin="5" Grid.Row="7" Grid.ColumnSpan="4" >
<DataGrid.Columns>
<DataGridTemplateColumn Header="Artikl ID">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox SelectedIndex="{Binding selectedIndexID, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Name="cmbArtikli" Width="120" DisplayMemberPath="artiklId" ItemsSource="{StaticResource source}">
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Naziv artikla" Binding="{Binding nazivArtikla}"/>
</DataGrid.Columns>
</DataGrid>
The binding of SelectedIndex and a horrible code-behind doing the trick with updating a text cell. For binding to work properly articl class have to implement INotifyPropertyChanged interface.
public class artikl: INotifyPropertyChanged
{
public artikl(int artid, string nazivart)
{
artiklId = artid;
nazivArtikla = nazivart;
}
public int artiklId{get;set;}
private string _nazv;
public string nazivArtikla
{
get { return _nazv; }
set { _nazv = value; NotifyPropertyChanged("nazivArtikla"); }
}
//Here I think you may have questions
private int _index;
public int selectedIndexID
{
get
{
//To get a SelectedIndex for ComboBox in current row we look in
//listArtikli defined in a MainWindow for a articli item with a current
//item's Id and take the index of this item
artikl art = MainWindow.listArtikli.Find(el => el.artiklId == this.artiklId);
return MainWindow.listArtikli.IndexOf(art);
}
set
{
//This property is binded with SelectedIndex property of ComboBox.
//When selected index changed, we look in listArtikli and take
//here properties of item with this index.
//This will change values of item binded to the current grid row
_index = value;
this.nazivArtikla = MainWindow.listArtikli[value].nazivArtikla;
this.artiklId = MainWindow.listArtikli[value].artiklId;
NotifyPropertyChanged("selectedIndexID");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And a window code-behind:
public partial class MainWindow : Window
{
public static artiklsList listArtikli = new artiklsList();
public static artiklsList gridsource = new artiklsList();
public MainWindow()
{
InitializeComponent();
dgrStavke.ItemsSource = gridsource;
}
}
You need gridsource aside from listArtikli to fill your DataGrid with values. Also because of all this code in selectedIndexID if you use only listArtikli its values corrupting. So listArtikli contains articli items just sorted in order which they were retrieved. And gridsource contains pairs artiklId-nazivArtikla as they presented in the DataGrid.
Hope it will help you just a little.
thanks for interesting. If you need more clarification please let me know. This is the code for one pair combobox-textbox, I'll easily apply this for another one:
XAML
...
<DataGrid Name="dgrStavke" AutoGenerateColumns="False" Height="160" Width="600" HorizontalAlignment="Left" Margin="5" Grid.Row="7" Grid.ColumnSpan="4">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Artikl ID">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Name="cmbArtikli" Width="120" ItemsSource="{Binding Source={StaticResource artcls}, Path=listArtikli}" DisplayMemberPath="artiklId"></ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Naziv artikla" Binding="{Binding nazivArtikla}"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
...
CODE
Imports System.Data
Imports System.Data.SqlClient
Imports System.Text
Namespace MW
Public Class artikl
Sub New(artid As Integer, nazivart As String)
' TODO: Complete member initialization
artiklId = artid
nazivArtikla = nazivart
End Sub
Public Property artiklId() As Integer
Public Property nazivArtikla() As String
End Class
Public Class frmDodavanjePaketa
Public Property listArtikli() As New List(Of artikl)
Private Sub popuniComboArtikli()
Dim sqlConn As SqlConnection = New SqlConnection(moduleGlobal.connString)
sqlConn.Open()
Dim strSql As New StringBuilder
strSql.Append("select a.artiklId, a.nazivArtikla ")
strSql.Append(" from artikli a ")
strSql.Append(" where isnull(a.aktivan, 0) = 1")
Dim sqlCom As SqlCommand = New SqlCommand(strSql.ToString, sqlConn)
Dim sqlDs As DataSet = New DataSet
Dim sqlDa As SqlDataAdapter = New SqlDataAdapter
sqlDa.SelectCommand = sqlCom
sqlDa.Fill(sqlDs)
For Each row As DataRow In sqlDs.Tables(0).Rows
Me.listArtikli.Add(New artikl(row.ItemArray(0).ToString, row.ItemArray(1).ToString))
Next
End Sub
End Class
End Namespace

Adding Items to a DataGrid (MVVM)

Goal
To add a list of a custom class object (DamagedItems) to a DataGrid using the Model, View, ViewModel (MVVM) way of doing things.
I want the user to be able to create entries of damaged parts (deemed improper during inspection of a machine).
What I have done
I have created:
A window: wDamagedItems.xaml in which it's DataContext is set to DamagedItemViewModel
A Model: DamagedItemModel.vb which implements INotifyPropertyChanged
A ViewModel: DamagedItemViewModel.vb where I set properties of classes such as my DamagedItemModel
An ObservableCollection: DamagedItemList.vb which inherits an ObservableCollection(Of DamagedItemModel)
Since my DataContext is set to the DamagedItemViewModel, here is how I setup the properties:
Public Class DamagedItemViewModel
Private _DamagedItem As DamagedItemModel
Private _Add As ICommand
Private _DamagedItems As DamagedItemList
Public Property DamagedItem As DamagedItemModel
Get
Return _DamagedItem
End Get
Set(value As DamagedItemModel)
_DamagedItem = value
End Set
End Property
Public Property DamagedItems As DamagedItemList
Get
Return _DamagedItems
End Get
Set(value As DamagedItemList)
_DamagedItems = value
End Set
End Property
Public Property Add As ICommand
Get
Return _Add
End Get
Set(value As ICommand)
_Add = value
End Set
End Property
Public Sub New()
DamagedItem = New DamagedItemModel("", "", "")
DamagedItems = New DamagedItemList
Add = New DamagedItemAddEntryCommand(Me)
End Sub
Public Function CanUpdate() As Boolean
If DamagedItem.Description = "" Then Return False
If DamagedItem.Initiales = "" Then Return False
Return True
End Function
Public Sub AddEntry()
DamagedItems.Add(DamagedItem) 'Items get added to the datagrid
DamagedItem = New DamagedItemModel 'Does not seem to clear textboxes
End Sub
End Class
Here is how my XAML is set up:
<DataGrid ItemsSource="{Binding Path=DamagedItems}" AutoGenerateColumns="True" HorizontalAlignment="Stretch" Margin="12,90,12,0" Name="DataGrid1" VerticalAlignment="Top" Height="229" / >
<TextBox Text="{Binding DamagedItem.Description, UpdateSourceTrigger=PropertyChanged}" Height="23" HorizontalAlignment="Left" Margin="88,24,0,0" VerticalAlignment="Top" Width="249" />
<TextBox Text="{Binding DamagedItem.Initiales, UpdateSourceTrigger=PropertyChanged}" Height="23" HorizontalAlignment="Left" Margin="88,58,0,0" VerticalAlignment="Top" Width="249" />
As you can see, my textboxes are bound to my Model (which is contained in my ViewModel, which is bound to that Window's DataContext). Whenever I click on my "Add" button, whatever is in the textbox gets added to the DataGrid, but the content in the text boxes stay there.
This step is fine, I write in what I want to add and click on "Add"
After clicking on "Add" i get the following results in the DataGrid, which is fine. The issue is my text boxes are still filled with data yet the Model was cleared (see code after DamagedItemViewModel AddEntry method).
Now when I try to add the following text:
Description: "Part is bent"
Initiales: "A.C"
I get the following result:
The first letter typed in the description gets inputted in the first entry of the DataGrid, then it erases the text in the description textbox. Only then can I keep typing what I want. The same thing occurs for the initiales text box.
Any ideas? If you wish to see more of my code, suggest which portion I should add.
Thank you in advance!
Yup, I remember running into this one. You have to implement iNotifyPropertyCHnaged. This is how the viewmodel class "notifies" the user interface that there has been a change to the underlying property of a binding:
look here:
http://msdn.microsoft.com/en-us/library/ms743695.aspx
You will have to implement this for every property you want reflected back to the view. SO what I do is have a base viewmodel class (ViewModelBase which exposes method RasiePropertyChanged) which implements iNotifyPropertyChanged and then my viewmodles inherit from it. Then I notify the property changed in the property set of the property:
ie:
Public Property Selection As job
Get
Return Me._Selection
End Get
Set(ByVal value As job)
If _Selection Is value Then
Return
End If
_PreviousJob = _Selection
_Selection = value
RaisePropertyChanged(SelectionPropertyName)
End Set
End Property
This seems frustrating at first but is needed to keep the decoupling that MVVM supports. Its easy to implement.

ListBox ItemTemplateSelector doesn't work

I am attempting to use an ItemTemplateSelector on a WPF ListBox and have looked at several examples online. Seemed simple enough but I cannot get it to work. I am hoping that someone can tell me where I've gone wrong:
Fist, I have an DataTemplateSelector class defined as follows:
Public Class DocketDataTemplateSelector
Inherits DataTemplateSelector
Public Overrides Function SelectTemplate(ByVal item As Object, ByVal container As DependencyObject) As DataTemplate
Return DataDocketHeaderTemplate
End Function
Private _DataDocketHeaderTemplate As DataTemplate
Public Property DataDocketHeaderTemplate() As DataTemplate
Get
Return _DataDocketHeaderTemplate
End Get
Set(ByVal value As DataTemplate)
_DataDocketHeaderTemplate = value
End Set
End Property
Private _DataDocketDataTemplate As DataTemplate
Public Property DataDocketDataTemplate() As DataTemplate
Get
Return _DataDocketDataTemplate
End Get
Set(ByVal value As DataTemplate)
_DataDocketDataTemplate = value
End Set
End Property
End Class
Very simple - just returns the DataDocketHeaderTemplate datatemplate for the time being until I can get it to work.
I then have my user control with the following as its resource definition:
<UserControl.Resources>
<DataTemplate x:Key="docketHeaderTemplate">
<TextBlock Text="Header Row Test" Background="Yellow"/>
</DataTemplate>
<DataTemplate x:Key="docketDataTemplate">
<TextBlock Text="Data Row Test" Background="Green"/>
</DataTemplate>
<local:DocketDataTemplateSelector DataDocketHeaderTemplate="{StaticResource docketHeaderTemplate}" DataDocketDataTemplate="{StaticResource docketDataTemplate}" x:Key="myDataTemplateSelector"/>
</UserControl.Resources>
The ListBox in the user control is simply defined like this:
<ListBox ItemsSource="{Binding TestData}" ItemTemplateSelector="{StaticResource myDataTemplateSelector}"/>
Then finally, my TestData list is defined in my bound viewmodel like so:
Private _listTestData As ObservableCollection(Of String) = Nothing
Public Property TestData As ObservableCollection(Of String)
Get
If _listTestData Is Nothing Then
_listTestData = New ObservableCollection(Of String)
_listTestData.Add("Row 1")
_listTestData.Add("Row 2")
_listTestData.Add("Row 3")
End If
Return _listTestData
End Get
Set(ByVal value As ObservableCollection(Of String))
_listTestData = value
NotifyPropertyChanged("TestData")
End Set
End Property
Now, I expect I would see a list of 3 rows in my listbox all saying 'Header Row Test' (since my datatemplateselector is always returning DataDocketHeaderTemplate). But instead I see my core data of
Row 1
Row 2
Row 3
This seems to indicate that my overriding datatemplateselector is not being hit (indeed if I set a breakpoint in DocketDataTemplateSelector, at no time do I see it being hit). Where am I going wrong with this?
Thanks
Sorry i can not post this as comment, i haven't got enough score.
I just tried your example code (my first VB project) and guess what, it works as expected: three times "Header Row Test" on yellow background. I've put the ListBox in a Grid in the UserControl, then put the UserControl in a Grid in a Window, then set the DataContext of the UserControl to a ViewModel object with your TestData property.
Something must be wrong that is not demonstrated by your example code, maybe you can provide more info.

ComboBox.SelectedValue not updating from binding source

Here's my binding source object:
Public Class MyListObject
Private _mylist As New ObservableCollection(Of String)
Private _selectedName As String
Public Sub New(ByVal nameList As List(Of String), ByVal defaultName As String)
For Each name In nameList
_mylist.Add(name)
Next
_selectedName = defaultName
End Sub
Public ReadOnly Property MyList() As ObservableCollection(Of String)
Get
Return _mylist
End Get
End Property
Public ReadOnly Property SelectedName() As String
Get
Return _selectedName
End Get
End Property
End Class
Here is my XAML:
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
xmlns:local="clr-namespace:WpfApplication1"
>
<Window.Resources>
<ObjectDataProvider x:Key="MyListObject" ObjectInstance="" />
</Window.Resources>
<Grid>
<ComboBox Height="23"
Margin="24,91,53,0"
Name="ComboBox1"
VerticalAlignment="Top"
SelectedValue="{Binding Path=SelectedName, Source={StaticResource MyListObject}, Mode=OneWay}"
ItemsSource="{Binding Path=MyList, Source={StaticResource MyListObject}, Mode=OneWay}"
/>
<Button Height="23"
HorizontalAlignment="Left"
Margin="47,0,0,87"
Name="btn_List1"
VerticalAlignment="Bottom"
Width="75">List 1</Button>
<Button Height="23"
Margin="0,0,75,87"
Name="btn_List2"
VerticalAlignment="Bottom"
HorizontalAlignment="Right"
Width="75">List 2</Button>
</Grid>
</Window>
Here's the code-behind:
Class Window1
Private obj1 As MyListObject
Private obj2 As MyListObject
Private odp As ObjectDataProvider
Public Sub New()
InitializeComponent()
Dim namelist1 As New List(Of String)
namelist1.Add("Joe")
namelist1.Add("Steve")
obj1 = New MyListObject(namelist1, "Steve")
.
Dim namelist2 As New List(Of String)
namelist2.Add("Bob")
namelist2.Add("Tim")
obj2 = New MyListObject(namelist2, "Tim")
odp = DirectCast(Me.FindResource("MyListObject"), ObjectDataProvider)
odp.ObjectInstance = obj1
End Sub
Private Sub btn_List1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles btn_List1.Click
odp.ObjectInstance = obj1
End Sub
Private Sub btn_List2_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles btn_List2.Click
odp.ObjectInstance = obj2
End Sub
End Class
When the Window first loads, the bindings hook up fine. The ComboBox contains the names "Joe" and "Steve" and "Steve" is selected by default. However, when I click a button to switch the ObjectInstance to obj2, the ComboBox ItemsSource gets populated correctly in the dropdown, but the SelectedValue is set to Nothing instead of being equal to obj2.SelectedName.
We had a similar issue last week. It has to do with how SelectedValue updates its internals. What we found was if you set SelectedValue it would not see the change we had to instead set SelectedItem which would properly update every thing. My conclusion is that SelectedValue is designed for get operations and not set. But this may just be a bug in the current version of 3.5sp1 .net
To stir up a 2 year old conversation:
Another possibility, if you're wanting to use strings, is to bind it to the Text property of the combobox.
<ComboBox Text="{Binding Test}">
<ComboBoxItem Content="A" />
<ComboBoxItem Content="B" />
<ComboBoxItem Content="C" />
</ComboBox>
That's bound to something like:
public class TestCode
{
private string _test;
public string Test
{
get { return _test; }
set
{
_test = value;
NotifyPropertyChanged(() => Test); // NotifyPropertyChanged("Test"); if not using Caliburn
}
}
}
The above code is Two-Way so if you set Test="B"; in code then the combobox will show 'B', and then if you select 'A' from the drop down then the bound property will reflect the change.
Use
UpdateSourceTrigger=PropertyChanged
in the binding
The type of the SelectedValuePath and the SelectedValue must be EXACTLY the same.
If for example the type of SelectedValuePath is Int16 and the type of the property that binds to SelectedValue is int it will not work.
I spend hours to find that, and that's why I am answering here after so much time the question was asked. Maybe another poor guy like me with the same problem can see it.
Problem:
The ComboBox class searches for the specified object by using the IndexOf method. This method uses the Equals method to determine equality.
Solution:
So, try to set SelectedIndex using SelectedValue via Converter like this:
C# code
//Converter
public class SelectedToIndexConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null && value is YourType)
{
YourType YourSelectedValue = (YourType) value;
YourSelectedValue = (YourType) cmbDowntimeDictionary.Tag;
YourType a = (from dd in Helper.YourType
where dd.YourTypePrimaryKey == YourSelectedValue.YourTypePrimaryKey
select dd).First();
int index = YourTypeCollection.IndexOf(a); //YourTypeCollection - Same as ItemsSource of ComboBox
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value!=null && value is int)
{
return YourTypeCollection[(int) value];
}
return null;
}
}
Xaml
<ComboBox
ItemsSource="{Binding Source={StaticResource YourDataProvider}}"
SelectedIndex="{Binding Path=YourValue, Mode=TwoWay, Converter={StaticResource SelectedToIndexConverter}, UpdateSourceTrigger=PropertyChanged}"/>
Good luck! :)
Ran into something similar, finally I just subscribed to the SelectionChanged event for the drop down and set my data property with it. Silly and wish it was not needed, but it worked.
Is it reasonable to set the SelectedValuePath="Content" in the combobox's xaml, and then use SelectedValue as the binding?
It appears that you have a list of strings and want the binding to just do string matching against the actual item content in the combobox, so if you tell it which property to use for the SelectedValue it should work; at least, that worked for me when I ran across this problem.
It seems like Content would be a sensible default for SelectedValue but perhaps it isn't?
Have you tried raising an event that signals SelectName has been updated, e.g., OnPropertyChanged("SelectedName")? That worked for me.
In my case I was binding to a list while I should be binding to a string.
What I was doing:
private ObservableCollection<string> _SelectedPartyType;
public ObservableCollection<string> SelectedPartyType { get { return
_SelectedPartyType; } set {
_SelectedPartyType = value; OnPropertyChanged("SelectedPartyType"); } }
What should be
private string _SelectedPartyType;
public string SelectedPartyType { get { return _SelectedPartyType; } set {
_SelectedPartyType = value; OnPropertyChanged("SelectedPartyType"); } }
The Binding Mode needs to be OneWayToSource or TwoWay since the source is what you want updated. Mode OneWay is Source to Target and therefore makes the Source ReadOnly which results in never updating the Source.
You know... I've been fighting with this issue for hours today, and you know what I found out? It was a DataType issue! The list that was populating the ComboBox was Int64, and I was trying to store the value in an Int32 field! No errors were being thrown, it just wasn't storing the values!
Just resolved this. Huh!!!
Either use [one of...] .SelectedValue | .SelectedItem | .SelectedText
Tip: Selected Value is preferred for ComboStyle.DropDownList while .SelectedText is for ComboStyle.DropDown.
-This should solve your problem. took me more than a week to resolve this small fyn. hah!!

Resources