WPF Combobox is changing source via SelectedValue when ItemsSource is loaded - wpf

I have this combobox in my WPF window.
<ComboBox DisplayMemberPath="Description" SelectedValuePath="ID" ItemsSource="{Binding Source={StaticResource CvsPrinters}}" SelectedValue="{Binding CheckPrinterID}" />
My problem is that when loading the window, the SelectedValue binding is causing my source data to change to the first item in the ItemsSource, instead of setting the Combobox's SelectedValue to the appropriate item in the ItemsSource.
The CheckPrinterID is from a listview selection datacontext, and this problem only occurs to the item initially selected in that listview on load. When I select another item in the listbox, the combobox correctly selects the proper item and all is fine, but unfortunately my initial item has been updated and is now incorrect.

I guess you are trying to synchronize ListView and ComboBox through a common property. Try setting IsSynchronizedWithCurrentItem to True in ListView and make sure SelectedItem or SelectedIndex for ListView is set during load.

Try re-arranging ItemsSource before DisplayMemberPath.

If you have some flexibility in the DataContext object you could try changing the selected CheckPrinter property to be of the data object type instead of the ID and switch to using SelectedItem instead of SelectedValue (for some reason SelectedValue behaves differently, especially at initial load) and then extract the ID from that value in code.
If you can't use the CheckPrinter objects in your DataContext object for whatever reason, you could also go the opposite direction on the UI side by using a list of IDs as the ItemsSource, and again using SelectedItem. To get the list to show what you want in the ComboBoxItems you would then need to use an IValueConverter to pull out Description values based on IDs:
<ComboBox ItemsSource="{Binding Source={StaticResource CvsPrinterIds}}" SelectedItem="{Binding CheckPrinterID}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock >
<TextBlock.Text>
<Binding>
<Binding.Converter>
<local:MyDescriptionLookupConverter Printers="{StaticResource CvsPrinters}"/>
</Binding.Converter>
</Binding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
and a simple converter to do the ID-Description lookup (add some null and cast checks):
public class MyDescriptionLookupConverter : IValueConverter
{
public IEnumerable<Printer> Printers { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return Printers.First(p => p.Id == (int)value).Description;
}
...
}

Related

ObservableCollection Remove() not firing to Visibility binding

I have a strange issue with my WPF project. I have a ObservableCollection<T> bound to a ListBox. When I add and remove items, the binding works and the list displays the correct results.
The issue I have, is I'm also binding this same property to another XAML control, but it doesn't trigger the converter when I remove an item from the list. It works when I add items.
The relevant XAML is
<view:WelcomeView Visibility="{Binding Steps, Converter={StaticResource CollapseIfZero}}"/>
<ListBox ItemsSource="{Binding Steps}" />
And the converter is
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var col = value as ICollection;
return col.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
}
I have a break point in the converter. When a new item is added, the break point is hit. When an existing item is removed, the break point is not hit.
Does WPF do something magical with the ListBox which I'm not aware of (which has led to this unexpected behavior)?
ObservableCollection implements INotifyCollectionChanged and ListBox (and other ItemsControls) listens when collection was modified.
Steps property itself doesn't change, it is the same ObservableCollection.
WelcomeView.Visibility is bound to Steps, and doesn't update because property value didn't change, it keeps the same object reference.
try create binding to Steps.Count property (converter should be modified to use int value)
<view:WelcomeView Visibility="{Binding Steps.Count, Converter={StaticResource CollapseIfZeroCount}}"/>
or
there is bool HasItems property in ItemsControl. I would make a binding with ElementName and BooleanToVisibilityConverter
<view:WelcomeView "{Binding ElementName=Lst, Path=HasItems, Converter={StaticResource Bool2Visibility}}"/>
<ListBox Name="Lst" ItemsSource="{Binding Steps}" />

MVVM WPF - ComboBox two way binding inside ItemsControl

I am working on this problem for about a day now.
For some reason I am unable to TwoWay bind a value to a ComboBox if it is inside a ItemsControl. Outside works just fine.
I have an ObservableCollection of int? in my ViewModel:
private ObservableCollection<int?> _sorterExitsSettings = new ObservableCollection<int?>();
public ObservableCollection<int?> SorterExitsSettings
{
get { return _sorterExitsSettings; }
set
{
if (_sorterExitsSettings != value)
{
_sorterExitsSettings = value;
RaisePropertyChanged("SorterExitsSettings");
}
}
}
My XAML:
<ItemsControl ItemsSource="{Binding SorterExitsSettings}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=ItemsControl}, Path=DataContext.ScanRouter.Stores}"
SelectedValue="{Binding Path=., Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="name" SelectedValuePath="id" IsEditable="True" />
</DataTemplate>
</ItemsControl.ItemTemplate>
So the ComboBox is populated with a list of stores. It works fine so far.
The ObservableCollection SorterExitsSettings even has some values set which are shown in the displayed ComboBoxes. So setting the SelectedValue also works.
However when I change a selection, SorterExitsSettings wont change. While when I implement the ComboBoxes(100) without an ItemsControl it suddenly works fine.
<ComboBox ItemsSource="{Binding ScanRouter.Stores}" DisplayMemberPath="name" SelectedValuePath="id" IsEditable="True" SelectedValue="{Binding SorterExitsSettings[0], Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
Even better when I implement the ComboBoxes using the ItemsControl and also the example ComboBox shown above. When I change the single ComboBox's value it will change the value of the ComboBox inside the ItemsControl, but not the other way around.
Did somebody encounter this problem before?
My guess was that the ItemsControl doesn't like the fact that I am binding my selected value to an item in a list. However when I bind directly to a ViewModel property(Store) it also doesn't work.
I also tried using SelctedItem instead of SelectedValue and populate the ObservableCollection with Store objects instead of int?.
The problem is that you're binding your ComboBox's SelectedValue directly to the collection elements which are type int ?. This won't work, binding targets have to be properties. Try wrapping your int ? values in a class and expose the value as a property of that class with a getter and setter, i.e. something like this:
private ObservableCollection<Wrapper> _sorterExitsSettings = new ObservableCollection<Wrapper>();
... etc...
And:
public class Wrapper
{
public int? Value {get; set;}
}
And finally:
<ComboBox ... SelectedValue="{Binding Path=Value, Mode=TwoWay...
Post back here if you still have problems.

Displaying cultures in combo box and selecting

I'm trying to display cultures in the combox box and I want the user to receive the DisplayName of the culture when selected but I get culture code ie 'en', 'ar' etc
The itemsource of the combo box is
ItemsSource="{Binding Path=SupportedCultures, Mode=OneWay}" SelectedItem="{Binding SelectedItem.Language}" SelectedValue="DisplayName" DisplayMemberPath="DisplayName"
SupportedCultures property
public static List<CultureInfo> SupportedCultures
{
get
{
return _SupportedCultures;
}
}
How can I get DisplayName in my selectedItem's Language property which is of type string?
SelectedItem requires the same kind of object that your ItemsSource is bound to, so don't use that. If you want to select by the value of a property, then use SelectedValue, and to tell WPF which property to check for that value, use SelectedValuePath:
ItemsSource="{Binding SupportedCultures, Mode=OneWay}"
SelectedValue="{Binding SelectedItem.Language}"
SelectedValuePath="DisplayName"
DisplayMemberPath="DisplayName"
Your code is almost correct. Your List is static. So, to bind to it, you should use {x:Static} source:
ItemsSource="{Binding Source={x:Static yournamespace:YourClassName}, Path=SupportedCultures, Mode=OneWay}"
DisplayMemberPath="DisplayName"
Note that SelectedItem="{Binding SelectedItem.Language}" binds your selected CultureInfo to your ComboBox's DataContext. So, in this case a DataContext of your ComboBox should have object SelectedItem with CultureInfo Language {get;set;} property. I don't think that's what you are looking for?)

Binding templated item to Converter's Value property in DataTemplate

I'm using Silverlight 4.
I have a DataTemplate defined for a DataGrid which allows me to successfully display values to my liking. I have a Rating control inside of this DataTemplate that has a Converter on the Value property like so..
<DataTemplate>
<toolkit:Rating Value="{Binding Converter={StaticResource MyConverter}" ItemCount="5" />
</DataTemplate>
When I step through the code and get into the converter, I see that the value parameter isn't the item corresponding to the row being rendered by the template but my ViewModel that is the DataContext of the DataGrid itself!
Now, if I adjust this slightly like so,
<DataTemplate>
<toolkit:Rating Value="{Binding SomeProperty Converter={StaticResource MyConverter}" ItemCount="5" />
</DataTemplate>
The value passed to MyConverter is SomeProperty of the item rendered by the DataTemplate.
Does anyone know why this might be? How can I bind to the item the template refers to instead of the DataContext of the DataGrid?
Try "{Binding ., Converter={StaticResource MyConverter}"
I figured this out.
During the MeasureOverride stage of Silverlight's DataGrid, my converter is being invoked. It feels like a bug in the DataGrid's implementation of MeasureOverride to ignore the
<DataGrid ItemsSource="{Binding MySource}"></DataGrid>
binding expression with respect to a defined DataTemplate at this stage and use the DataContext of the DataGrid which will certainly cause a typical Converter to fail.
My band-aid solution for now is to add an if statement in my converter implementation to just make sure the type of value I get is what I expect so it passes MeasureOverride.
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is ExpectedType)
//do things
else
//return an instance of targetType
}
Can anyone confirm if this still happens in SL5?

Binding ComboBox Text property using a converter

I'm having troubles using a WPF ComboBox in the following scenario:
ViewModel
Provides a ObservableCollection<T>; This collection contains a list of items the user may select.
Provides a property of type T representing the selected item.
The user shall be able to select either an existing item from the items in the ObservableCollection<T> or add a new item by typing in the string representation.
I have a converter available able to convert a item of type T to string and vice versa.
View
My ComboBox bound to the collection and the selected item properties:
<ComboBox ItemsSource="{Binding Path=MyObservableCollection}"
SelectedItem="{Binding Path=MySelectedItem}"
IsReadOnly="False" IsEditable="True"/>
The data template used to display the items correctly:
<DataTemplate DataType="{x:Type T}">
<TextBlock Text="{Binding Converter={StaticResource ResourceKey=MyConverter}}"/>
</DataTemplate>
The Problem
Items in the drop down list of the ComboBox are displayed correctly using the convert. The selected item displayed in the TextBox of the ComboBox is not displayed correctly; Instead of using my converter, the ToString method is used.
Is it possible to specify a converter for the Text property? I tried using the following code:
<ComboBox ItemsSource="{Binding Path=MyObservableCollection}"
SelectedItem="{Binding Path=MySelectedItem}"
Text="{Binding Path=MySelectedItem, Converter={StaticResource ResourceKey=MyConverter}}"
IsReadOnly="False" IsEditable="True"/>
This solves the display problem but now I get the Type.FullName of T in the converters ConvertBack method - which can of course not be converted.
Summary
I want the user to be able to select an item from a collection, allowing him to add new items by entering the string representation. The items in the collection shall be converted between string and object representation using a converter. The conversion shall be done in both the drop down list and the text box of the ComboBox.
Edit
Here is the code in my converter - no magic there just straightforward conversion:
public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return MyConverter.Convert(value as T);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return MyConverter.Convert(value as string);
}
public static string Convert(T key)
{
// Conversion from T to string.
}
public static T Convert(string key)
{
// Conversion from string to T.
}
}
Ok, now I found something that does what I want:
<TextBox Text="{Binding Path=MySelectedItem, Converter={StaticResource ResourceKey=MyConverter}}"/>
<ListBox ItemsSource="{Binding Path=MyObservableCollection}"
SelectedItem="{Binding Path=MySelectedItem, Converter={StaticResource ResourceKey=MyConverter}}"/>
This does exactly what I want; I can select predefined values and the user may add values on his own. Is it possible to do this with a ComboBox?
In case someone facing same issue and don't want to cope with having a string property to bound to.
You can use the following
<ComboBox
ItemsSource="{Binding Path=MyObservableCollection}"
Text="{Binding MySelectedItem, Converter={StaticResource DisplayConverter}}"
SelectedValue="{Binding MySelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource DisplayConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
Notice the binding is done on the SelectedValue, not SelectedItem.
Then the display converter is added both to the Text & Itemtemplate properties.
On the Text property it will be used to convert the selected item display value.
On the ItemTemplate, to convert display values within the list box
I even use this snippet with Enum collection coming from ObjectDataProvider defined in the xaml. My enums have DisplayString attribute and combobox behave just fine to display the string value representation of the enums.
HTH
I now use a different approach for the problem:
My view model provides a observable collection and a string property. The collection is bound to the ItemsSource property of the ComboBox, the selected item to the string property.
<ComboBox
ItemsSource="{Binding Path=MyCollection}"
Text="{Binding Path=MyProperty, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
IsReadOnly="False" IsEditable="True"/>
UpdateSourceTrigger=LostFocus part is used to prevent unnecessary updates.

Resources