Enum getting cast to string when bound with custom converter - wpf

Problem Overview
I have a custom IValueConverter called EnumDisplayConverter. It's supposed to take an Enum value and return the name so it can be displayed. Somehow, even though this converter is being used on a binding between properties of an Enum type, the converter is being passed a value of String.Empty. This of course causes an error as String is not an Enum, not to mention it's just really unexpected.
Code to Reproduce
The following code can be used to reproduce the error. The steps to reproduce and an explanation of what the code is meant to do come after.
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:VBTest"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<DockPanel>
<ListBox Name="LB_Foos" DockPanel.Dock="Left" ItemsSource="{Binding FooOptions}" SelectionChanged="ListBox_SelectionChanged"/>
<ComboBox ItemsSource="{x:Static local:MainWindow.SelectableThings}" SelectedItem="{Binding OpenFoo.SelectableChosenThing}" VerticalAlignment="Center" HorizontalAlignment="Center" Width="100">
<ComboBox.ItemTemplate>
<DataTemplate>
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Content" Value="{Binding Converter={x:Static local:EnumDisplayConverter.Instance}}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding}" Value="-1">
<Setter Property="Content" Value="None"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DockPanel>
</Window>
Imports System.Collections.ObjectModel
Imports System.Globalization
Class MainWindow
Shared Sub New()
Dim Things = (From v As Thing In [Enum].GetValues(GetType(Thing))).ToList
Things.Insert(0, -1)
SelectableThings = New ReadOnlyCollection(Of Thing)(Things)
End Sub
Public Shared ReadOnly Property SelectableThings As IReadOnlyList(Of Thing)
Public ReadOnly Property FooOptions As New ReadOnlyCollection(Of Integer)({1, 2, 3, 4})
'This is a placeholder method meant to set OpenFoo to a new instance of Foo when a selection is made.
'In the actual application, this is done with data binding and involves async database calls.
Private Sub ListBox_SelectionChanged(sender As Object, e As SelectionChangedEventArgs)
OpenFoo = Nothing
Select Case LB_Foos.SelectedItem
Case 1
OpenFoo = New Foo With {.ChosenThing = Nothing}
Case 2
OpenFoo = New Foo With {.ChosenThing = Thing.A}
Case 3
OpenFoo = New Foo With {.ChosenThing = Thing.B}
Case 4
OpenFoo = New Foo With {.ChosenThing = Thing.C}
End Select
End Sub
Public Property OpenFoo As Foo
Get
Return GetValue(OpenFooProperty)
End Get
Set(ByVal value As Foo)
SetValue(OpenFooProperty, value)
End Set
End Property
Public Shared ReadOnly OpenFooProperty As DependencyProperty =
DependencyProperty.Register("OpenFoo",
GetType(Foo), GetType(MainWindow))
End Class
Public Enum Thing
A
B
C
End Enum
Public Class Foo
Public Property ChosenThing As Thing?
Public Property SelectableChosenThing As Thing
Get
Return If(_ChosenThing, -1)
End Get
Set(value As Thing)
Dim v As Thing? = If(value = -1, New Thing?, value)
ChosenThing = v
End Set
End Property
End Class
Public Class EnumDisplayConverter
Implements IValueConverter
Public Shared ReadOnly Property Instance As New EnumDisplayConverter
Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
If value Is Nothing Then Return Nothing
Return [Enum].GetName(value.GetType, value)
End Function
Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
Return Binding.DoNothing
End Function
End Class
Steps to Reproduce
Run MainWindow
Select any item from the ListBox on the left
Select a different item from the ListBox
Observe unhanded exception
Code Explanation
In case it's not clear what the code is supposed to do, I'll explain a bit.
Foo represents a data object that is being edited by the user via MainWindow. Every Foo has the option of a Thing. Not having a Thing is also an option, which is why ChosenThing is a Thing? (i.e. Nullable(Of Thing)).
Null data items don't work in a ComboBox, since Null means "there is no selection". To get around this, I add a value of -1 to my list of selectable Thing values. In Foo.SelectableChosenThing, I check for -1 and convert it to Null for the actual value of Foo.ChosenThing. This lets me bind to the ComboBox correctly.
Problem Details
The error only seems to occur when OpenFoo is set to Nothing before being given a new value. If I take out the line OpenFoo = Nothing, everything works. However, in the real application I want to set OpenFoo to Nothing while the selection is being loaded- besides, it doesn't explain why this is happening in the first place.
Why is EnumDisplayConverter being passed a value of type String, when the properties involved are of the expected type Thing?

Turns out, after some investigation, that the problem comes from the ComboBox control. When the selected item of the ComboBox is null, it replaces the display value of null with String.Empty. Here's a snippet from the .NET Reference Source for ComboBox.UpdateSelectionBoxItem:
...
// display a null item by an empty string
if (item == null)
{
item = String.Empty;
itemTemplate = ContentPresenter.StringContentTemplate;
}
SelectionBoxItem = item;
SelectionBoxItemTemplate = itemTemplate;
...
This happens before any DataTemplates are considered, so by the time my DataTemplate get's called, it's being given a value of String.Empty to display instead of null.
The solution is to either
Add a DataTrigger to the ContentControl's Style which checks for String.Empty and doesn't use the converter in such a case.
Modify EnumDisplayConverter to check for non-Enum values and return DependencyProperty.UnsetValue.

Related

How to listen to INotifyDataErrorInfo using a CollectionView

I have the following scenario:
XAML:
<ListView Name="lsv_edit_selectNode" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="4"
Grid.RowSpan="17" IsSynchronizedWithCurrentItem="True" SelectionMode="Single"
ItemsSource="{Binding Path=Nodes.CollectionView, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}">
Where Nodes is a custom ObservableCollection that contains a ListCollectionView:
Public Class FilterableObservableCollection(Of T)
Inherits ObservableCollection(Of T)
Implements INotifyPropertyChanged, INotifyCollectionChanged
Public Property CollectionView As ListCollectionView
Get
Return _collectionView
End Get
Protected Set(value As ListCollectionView)
If value IsNot _collectionView Then
_collectionView = value
NotifyPropertyChanged()
End If
End Set
End Property
'etc.
T in this case is a Node object, with many properties including the one I'm interested in (call it NodeResults):
Public Class Node
Inherits ObservableValidatableModelBase
Public Property NodeResults as NodeResults
Set
SetAndNotify(_nodeResults, Value) ' INotifyPropertyChanged
AddHandler _nodeResults.ErrorsChanged, AddressOf BubbleErrorsChanged ' INotifyDataErrorInfo?
End Set
End Property
' etc.
and NodeResults:
Public Class NodeResults
Inherits ObservableValidatableModelBase
' many properties here, each validated using custom Data Annotations. This works, when I bind a text box directly to here, for example.
ObservableValidatableModelBaseImplements INotifyDataErrorInfo, and stores its errors in a collection called errors:
Private errors As New Dictionary(Of String, List(Of String))()
Public ReadOnly Property HasErrors As Boolean Implements INotifyDataErrorInfo.HasErrors
Get
Return errors.Any(Function(e) e.Value IsNot Nothing AndAlso e.Value.Count > 0)
End Get
End Property
Public Function GetErrors(propertyName As String) As IEnumerable Implements INotifyDataErrorInfo.GetErrors
Try
If Not String.IsNullOrEmpty(propertyName) Then
If If(errors?.Keys?.Contains(propertyName), False) _
AndAlso If(errors(propertyName)?.Count > 0, False) Then ' if there are any errors, defaulting to false if null
Return errors(propertyName)?.ToList() ' or Nothing if there are none.
Else
Return Nothing
End If
Else
Return errors.SelectMany(Function(e) e.Value.ToList())
End If
Catch ex As Exception
Return New List(Of String)({"Error getting errors for validation: " & ex.Message})
End Try
End Function
Public Event ErrorsChanged As EventHandler(Of DataErrorsChangedEventArgs) Implements INotifyDataErrorInfo.ErrorsChanged
Public Sub NotifyErrorsChanged(propertyName As String)
ErrorsChangedEvent?.Invoke(Me, New DataErrorsChangedEventArgs(propertyName))
End Sub
Public Sub BubbleErrorsChanged(sender As Object, e As DataErrorsChangedEventArgs)
If TypeOf (sender) Is ObservableValidatableModelBase Then
errors = DirectCast(sender, ObservableValidatableModelBase).errors
End If
NotifyErrorsChanged(String.Empty)
End Sub
What I want to happen is for the individual Nodes in the CollectionView to notify the ListView, so that it highlights the individual entries that are invalid (i.e. have a NodeResults that is invalid) on the screen.
My first instinct is that Node somehow needs to subscribe to and bubble the NodeResults' ErrorsChanged event, hence the BubbleErrorsChanged method on the ObservableValidatableModelBase class - but that doesn't seem to work.
Another possibility is - does ListView even have a default template for displaying validation exceptions? If not, should something like this work? (it doesn't...)
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Background" Value="{Binding Path=(Validation.Errors).CurrentItem, Converter={StaticResource ValidationExceptionToColourConverter}}"/>
</Style>
</ListView.ItemContainerStyle>
Where ValidationExceptionToColourConverter simply returns Brushes.Red or Brushes.White depending on whether the error is Nothing or not.
Note: Binding a text box directly to Nodes.NodeResults.SomeProperty works fine, giving the results I'm expecting.
I needed the following:
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Background" Value="{Binding Path=HasErrors, Mode=OneWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource BooleanToBrushConverter}}"/>
</Style>
</ListView.ItemContainerStyle>

Set visibility of Context Menu Items in WPF depending on other menu items

I have a tree view control with hierarchical data. It has a context menu with four options: Expand, Expand All, Collapse, Collapse All. I am currently using the following class to show / hide the context menu items:
Public Class clsTreeContextMenuVisibilityConverter
Implements IValueConverter
Public Function Convert(InValue As Object, InTargetType As Type, InParameter As Object, InCulture As Globalization.CultureInfo) As Object Implements IValueConverter.Convert
Dim node As TreeNode = Nothing
If InValue Is Nothing Then
Return Binding.DoNothing
End If
node = DirectCast(InValue, TreeNode)
If InValue.[GetType]() <> GetType([Boolean]) Then
If node.HasChildren AndAlso node.ParentNode Is Nothing Then
If node.IsExpanded Then
Return Visibility.Collapsed
End If
Return Visibility.Visible
End If
End If
Return Binding.DoNothing
End Function
Public Function ConvertBack(InValue As Object, InTargetType As Type, InParameter As Object, InCulture As Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
Throw New NotImplementedException()
End Function
End Class
XAML:
<Style x:Key="ExpandMenuItemStyle"
TargetType="MenuItem">
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Visibility" Value="{Binding Converter={StaticResource VisConverter}}" />
</Style>
<MenuItem Header="Expand" Style="{StaticResource ExpandMenuItemStyle}" />
VisConverter is x:Key of the converter class. My question is, if a Node is expanded, I should see Collapse and vice versa. Also, if it is a root parent level node, then I should see Expand All. So do I have to write separate converters for all four cases or is there an intelligent way to do this?
Please let me know if more information is required.
You probably want two converters or two pairs of converters. One for the expanded state and one for returning whether the node is a parent or child. If you go for just two converters you will need a parameter to determine whether the converter should return Visibility.Visible or Visibility.Collapsed.
With four converters you won't need the parameter.

How to add InputBindings to the Window after composition?

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);
}
}

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