I have a combobox that is bind to the following list:
private List<string> strList;
public List<string> StrList
{
get { return strList; }
set
{
strList = value;
OnPropertyChanged("StrList");
}
}
The selected item is bind to the next object:
private string str;
public string Str
{
get { return str; }
set
{
if (str != value)
{
str = value;
OnPropertyChanged("Str");
}
}
}
Following the combobox:
<ComboBox ItemsSource="{Binding StrList}"
SelectedItem="{Binding Str,UpdateSourceTrigger=LostFocus}"
Height="50" Width="200"/>
I want that the binding happens only on lost focus, and when changing the values with the keys of the keyboard.
Therefore UpdateSourceTrigger=LostFocus.
My question is how to do that binding happened also by changing the values from the keyboard?
I created a behavior and in it I renewed the binding in a case of pressing the keys:
public class KeysChangedBehavior : Behavior<ComboBox>
{
protected override void OnAttached()
{
this.AssociatedObject.AddHandler(ComboBox.KeyDownEvent,
new RoutedEventHandler(this.OnKeysChanged));
this.AssociatedObject.AddHandler(ComboBox.KeyUpEvent,
new RoutedEventHandler(this.OnKeysChanged));
}
protected void OnKeysChanged(object sender, RoutedEventArgs e)
{
BindingExpression _binding = ((ComboBox)sender).GetBindingExpression(ComboBox.SelectedItemProperty);
if (_binding != null)
_binding.UpdateSource();
}
}
Here the combobox:
<ComboBox ItemsSource="{Binding StrList}" SelectedItem="{Binding Str,UpdateSourceTrigger=LostFocus}" Height="50" Width="200">
<i:Interaction.Behaviors>
<KeysChangedBehavior/>
</i:Interaction.Behaviors>
</ComboBox>
Related
I have a ListBox with a simple DataTemplate, a CheckBox, and a TextBox.
If the user checks a CheckBox I want to get this changed item, like the property SelectedItem of the ListBox.
How can I get the element from List2, which has changed?
MyListItem:
public class MyListItem2 : ReactiveObject
{
private string _name;
public string Name
{
get { return _name; }
set
{
this.RaiseAndSetIfChanged(ref _name, value, "Name");
}
}
private bool _isMarked;
public bool IsMarked
{
get { return _isMarked; }
set
{
this.RaiseAndSetIfChanged(ref _isMarked, value, "IsMarked");
}
}
}
View:
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DataTemplate.Views.MainWindow"
xmlns:viewsmodels="clr-namespace:DataTemplate.ViewModels;assembly=DataTemplate"
xmlns:dt="clr-namespace:DataTemplate;assembly=DataTemplate"
Title="DataTemplate" Width="700">
<Window.DataContext>
<viewsmodels:MainWindowViewModel />
</Window.DataContext>
<Grid ColumnDefinitions="250">
<ListBox Grid.Column="1" Items="{Binding List2}">
<ListBox.ItemTemplate>
<DataTemplate DataType="dt:MyListItem2">
<Grid ColumnDefinitions="50*,50*">
<CheckBox Grid.Column="0" Content="Mark" IsChecked="{Binding IsMarked}"/>
<TextBox Grid.Column="1" Text="{Binding Name}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
ViewModel:
public class MainWindowViewModel : ReactiveObject
{
public ObservableCollection<MyListItem2> List2 { get; set; }
public MainWindowViewModel()
{
List2 = new ObservableCollection<MyListItem2>();
Random rand = new Random();
for (int i = 0; i < rand.Next(1, 20); i++)
{
MyListItem2 mli = new MyListItem2();
mli.Name = "ListItem" + i;
mli.IsMarked = false;
mli.PropertyChanged += ItemChanged;
List2.Add(mli);
}
}
private void ItemChanged(object sender, PropertyChangedEventArgs e)
{
var item = sender as MyListItem2;
Console.WriteLine(string.Format("changed: {0} {1}", item.Name, item.IsMarked));
}
}
I can see two ways:
Since you are using MVVM, implement the INotifyPropertyChanged interface on the MyListItem2 class (Microsoft Reference on INotifyPropertyChanged implementation). Raise the property change event when the IsMarked value is set/changed, then wire into the PropertyChanged event handler of the item to determine when it is changed. . OR
If you have codebehidn, add a "Checked" and/or "Unchecked" event handler on the checkbox itself from the XAML. Shown below.
CheckBox Grid.Column="0" Content="Mark" IsChecked="{Binding IsMarked}"/>
Checked="IsMarked_Checked"
Codebehind
public void IsMarked_Checked(object sender, RoutedEventArgs e)
{
var ck = sender As Checkbox;
if (ck == null)
{
return;
}
// do whatever you need to here using the datacontext of the Checkbox
}
If you want to know when a check box is checked/unchecked by the user you will need to trigger on the event from the checkbox.
Use something like this:
private void MyCheckBox_Checked(object sender, RoutedEventArgs e)
{
//check IsChecked of MyCheckBox here
}
Try setting binding Mode:
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
I have a simple ComboBox which looks like following:
<ComboBox ItemsSource="{DynamicResource ItemsCompColl}"
TextSearch.TextPath="ItemName"
SelectedValue="{Binding ItemId, UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True}" SelectedValuePath="ItemId"
Grid.IsSharedSizeScope="True">
........................
</ComboBox>
It works well. Now, I am using the ItemId property which is bound in SelectedValue to check if user has selected an appropriate item from comboBox or not.
Issues:
When user selects a value from ComboBox, the ItemId property is set to the Id of the Selected Item in ComboBox. After that if user goes to next Control and returns to ComboBox and enters some garbage value to ComboBox, the ItemId of ComboBox does not change, I mean it's not reset to "0". So, my validation fails and user succeeds in entering the garbage values.
OK, so you want to Set SelectedValue to 0 when there is any validation error in the editable TextBox of theComboBox. You need to check the validation result of the Text and then reset your SelectedValue to 0 if the validation fails.
Here is an working example for you:
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<ComboBox ItemsSource="{Binding ComboboxItems}"
IsEditable="True" DisplayMemberPath="ItemName"
Text="{Binding SelectedName, ValidatesOnDataErrors=True}"
SelectedValue="{Binding SelectedID, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="ItemId"
Grid.IsSharedSizeScope="True">
</ComboBox>
<TextBox Text="{Binding SelectedID,UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</Grid>
</Window>
C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
MyViewModel mvm;
public MainWindow()
{
InitializeComponent();
mvm = new MyViewModel()
{
ComboboxItems = new ObservableCollection<ComboItem>()
{
new ComboItem{ItemName="item1",ItemId=1},
new ComboItem{ItemName="item2",ItemId=2},
new ComboItem{ItemName="item3",ItemId=3}
},
};
this.DataContext = mvm;
}
}
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
protected void RaisePropertyChanged(String propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
}
public class ComboItem : ObservableObject
{
private string _itemname;
private int _itemid;
public string ItemName
{
get
{
return _itemname;
}
set
{
_itemname = value;
RaisePropertyChanged("ItemName");
}
}
public int ItemId
{
get { return _itemid; }
set
{
_itemid = value;
RaisePropertyChanged("ItemId");
}
}
}
public class MyViewModel : ObservableObject, IDataErrorInfo
{
private int _selectedid;
private string _selectedname;
public ObservableCollection<ComboItem> ComboboxItems
{
get;
set;
}
public int SelectedID
{
get { return _selectedid; }
set
{
if (_selectedid != value)
{
_selectedid = value;
RaisePropertyChanged("SelectedID");
}
}
}
public string SelectedName
{
get { return _selectedname; }
set
{
if (_selectedname != value)
{
_selectedname = value;
RaisePropertyChanged("SelectedName");
}
}
}
public string Error
{
get { return this[SelectedName]; }
}
public string this[string columnName]
{
get {
switch (columnName)
{
case "SelectedName":
{
if (SelectedName!=null && ComboboxItems.Count(x => x.ItemName == SelectedName) == 0)
{
//reset selected value to 0
this.SelectedID = 0;
return "Invalid selection";
}
break;
}
}
return null;
}
}
}
}
Result:
When user enter valid text (e.g. item1), the Textbox below shows the correct ItemId of the SelectedValue, and when user enter invalid text, the selected value will be reset to 0.
P.S: When garbage is entered in ComboBox, it will always display validation error indicator (red border as shown above), and if you data-bind SelectedItem to a property, it will be null. So you shouldn't care about the SelectedValue if there is an error, that's why I was saying I can't reproduce the error in the comments.
You really got me with this, i've never realized this problem existed. I've found a solution that works it you don't care to clear the combo on focus. There are probably better ways, but none i can think about. Maybe someone out there has another solution.
First of all, add a reference to Windows.System.Interactivity in your proyect, and add this to your XAML:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Then, add this code to the combobox:
<ComboBox ItemsSource="{DynamicResource ItemsCompColl}"
TextSearch.TextPath="ItemName" x:Name="cbItems"
SelectedValue="{Binding ItemId, UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True}" SelectedValuePath="ItemId"
Grid.IsSharedSizeScope="True">
<i:Interaction.Triggers>
<i:EventTrigger EventName="GotMouseCapture">
<i:InvokeCommandAction Command="{Binding ClearCombo}"
CommandParameter="{Binding ElementName=cbItems}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
Finally, let's create the command in ouw View Model:
RelayCommand<System.Windows.Controls.ComboBox> _clearCombo;
public ICommand ClearCombo
{
get
{
if (_clearCombo == null)
{
_clearCombo = new RelayCommand<System.Windows.Controls.ComboBox>(this.ClearComboCommandExecuted,
param => this.ClearComboCommandCanExecute());
}
return _clearCombo;
}
}
private bool ClearComboCommandCanExecute()
{
return true;
}
private void ClearComboCommandExecuted(System.Windows.Controls.ComboBox cb)
{
cb.Text = "";
}
Hope this helps with your problem.
Edit
Ok, after #XAMlMAX comment, I think he is right and this may be done in Code Behind easily and probably it's better in MVVM pattern. Simply add a event handler to the combobox to capture GotMouseCapture:
<ComboBox ItemsSource="{DynamicResource ItemsCompColl}"
TextSearch.TextPath="ItemName" x:Name="cbItems"
SelectedValue="{Binding ItemId, UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True}" SelectedValuePath="ItemId"
Grid.IsSharedSizeScope="True"
GotMouseCapture="cbItems_GotMouseCapture" >
And then in code behind of the View:
private void cbItems_GotMouseCapture(object sender, MouseEventArgs e)
{
((ComboBox)sender).Text = "";
}
Edit 2
Well, one final, ugly idea to solve it. I don't like it at all, but maybe it solves your problem.
First of all, you must subscribe to the TextBoxBase.TextChanged event:
<ComboBox ItemsSource="{DynamicResource ItemsCompColl}"
TextSearch.TextPath="ItemName" x:Name="cbItems"
SelectedValue="{Binding ItemId, UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True}" SelectedValuePath="ItemId"
Grid.IsSharedSizeScope="True"
TextBoxBase.TextChanged="cbItems_TextChanged" >
Then in code behind add this code:
private void cbItems_TextChanged(object sender, TextChangedEventArgs e)
{
string text = ((ComboBox)sender).Text;
((YourViewModel)this.DataContext).ItemId= text;
}
This way, you make sure any time the ComboBox changes its text, you get notified about it. It's really horrible code, but i've runned out of ideas...
Is it possible to refactor the currentDevices into a collection?
Basically, I have three comboboxes in which the selectedvalue is bound to currentDevices. then the currentDevices are taken from a settings file.
View
<ComboBox ItemsSource="{Binding availableDevices}"
SelectedValue="{Binding currentDevice1}">
</ComboBox>
<ComboBox ItemsSource="{Binding availableDevices}"
SelectedValue="{Binding currentDevice2}">
</ComboBox>
<ComboBox ItemsSource="{Binding availableDevices}"
SelectedValue="{Binding currentDevice3}">
</ComboBox>
ViewModel
public string currentDevice1 {
get
{
return SampleSettings.Default.Device1;
}
set
{
SampleSettings.Default.Device1 = value;
}
}
public string currentDevice2
{
get
{
return SampleSettings.Default.Device2;
}
set
{
SampleSettings.Default.Device2 = value;
}
}
public string currentDevice3
{
get
{
return SampleSettings.Default.Device3;
}
set
{
SampleSettings.Default.Device3 = value;
}
}
I'd write a DeviceOptionViewModel, and give the main viewmodel an ObservableCollection.
DeviceOptionViewModel.cs
public class DeviceOptionViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _currentDevice;
public String CurrentDevice {
get { return _currentDevice; }
set {
_currentDevice = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(CurrentDevice));
}
}
// Parent event assigns this to his own availableDevices
// when he creates this.
public IEnumerable AvailableDevices { get; set; }
}
Main VM:
public ObservableCollection<DeviceOptionViewModel>
CurrentDevices { get; private set; }
= new ObservableCollection<DeviceOptionViewModel>();
XAML:
<ItemsControl
ItemsSource="{Binding CurrentDevices}"
>
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- DataContext here is DeviceOptionViewModel. We gave it its
own reference to AvailableDevices to simplify binding. -->
<ComboBox
ItemsSource="{Binding AvailableDevices}"
SelectedValue="{Binding CurrentDevice}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Back to main viewmodel:
protected void PopulateCurrentDevices(IEnumerable<String> stringsFromWherever)
{
CurrentDevices.Clear();
foreach (var device in stringsFromWherever)
{
var dovm = new DeviceOptionViewModel() {
CurrentDevice = device,
AvailableDevices = this.availableDevices
};
dovm.PropertyChanged += DeviceOptionViewModel_PropertyChangedHandler;
CurrentDevices.Add(dovm);
}
}
protected void DeviceOptionViewModel_PropertyChangedHandler(object sender,
PropertyChangedEventArgs e)
{
var dopt = sender as DeviceOptionViewModel;
if (e.PropertyName == nameof(DeviceOptionViewModel.CurrentDevice))
{
// Do stuff
}
}
So you populate and repopulate CurrentDevices in your viewmodel as needed, and the UI will magically appear if all the notifications are done correctly.
If you create a new ObservableCollection and assign that to the CurrentDevices property, you'll need to raise PropertyChanged(nameof(CurrentDevices)) on the main viewmodel. I made the setter private to avoid having to implement that detail. If it's not a huge collection, may as well just Clear() and Add() on the same old instance.
I have a combo box that I type my Database Server names in it. I just want it to remeber what I have typed so far so next time it adds it to a list of items in it so I don't have to type the same Database Server name again each time I run the app.
As for saving the names I have typed in the combox, I am fine with saving them in a file, like a text file, Json, XML, whatever.
I don't know how to do the binding? and when to load the file? Can you help me with an example?
<ComboBox x:Name="serverTxt" Height="23" VerticalAlignment="Top" Text="{Binding Path=ServerNames}"/>
Here's some code that came from this answer, with a little updating and added the storage/retrieval. It should get you started at least. Note that this solution requires a second element on your window (I added a second combobox here) because it triggers on LostFocus, otherwise it will update for each character as you type.
Set up your xaml like this:
<ComboBox x:Name="comboBox" HorizontalAlignment="Left" Margin="149,43,0,0" VerticalAlignment="Top" Width="120" IsEditable="True"
ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}" Text="{Binding NewItem, UpdateSourceTrigger=LostFocus}"/>
<ComboBox x:Name="comboBox1" HorizontalAlignment="Left" Margin="349,122,0,0" VerticalAlignment="Top" Width="120"/>
Then your main window:
public partial class MainWindow : Window
{
private string _selectedItem;
private ObservableCollection<string> ServerNames;
private string fileLocation = #"C:\Temp\ServerNames.txt";
public MainWindow()
{
ServerNames = new ObservableCollection<string>();
if (File.Exists(fileLocation))
{
var list = File.ReadAllLines(fileLocation).ToList();
list.ForEach(ServerNames.Add);
}
DataContext = this;
InitializeComponent();
}
public IEnumerable Items => ServerNames;
public string SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
OnPropertyChanged("SelectedItem");
}
}
public string NewItem
{
set
{
if (SelectedItem != null)
{
return;
}
if (!string.IsNullOrEmpty(value))
{
ServerNames.Add(value);
SelectedItem = value;
}
}
}
protected void OnPropertyChanged(string propertyName)
{
var handler = this.PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
private void Window_Closing(object sender, CancelEventArgs e)
{
if (!File.Exists(fileLocation))
{
File.Create(fileLocation);
}
File.WriteAllLines(fileLocation, ServerNames);
}
}
Im trying to make a ComboBox that have checkboxes as items and based on what is checked display different things when the combobox is "closed".
The look Im trying achieve can be seen in the image.
Ideally I dont want the user to be able to select the text in the top ( in the image).
Is there a simple solution to this? I've seen solutions where one can display more information when all the items are shown by using DataTriggers to hide different nested controls, but that is not really what Im looking for.
Any ideas?
/Erik
Here is a way to achieve most of what you want using a ComboBox, except that the text can still be selected (using custom text only works when IsEditable is true). It is not editable though because of IsReadOnly="true".
View
<ComboBox
IsEditable="True"
IsReadOnly="True"
ItemsSource="{Binding Items}"
Text="{Binding Text}">
<ComboBox.ItemTemplate>
<DataTemplate
DataType="{x:Type local:Item}">
<CheckBox
Content="{Binding Name}"
IsChecked="{Binding IsChecked}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Viewmodel
// ObservableObject is a custom base class that implements INotifyPropertyChanged
internal class MainWindowVM : ObservableObject
{
private ObservableCollection<Item> mItems;
private HashSet<Item> mCheckedItems;
public IEnumerable<Item> Items { get { return mItems; } }
public string Text
{
get { return _text; }
set { Set(ref _text, value); }
}
private string _text;
public MainWindowVM()
{
mItems = new ObservableCollection<Item>();
mCheckedItems = new HashSet<Item>();
mItems.CollectionChanged += Items_CollectionChanged;
// Adding test data
for (int i = 0; i < 10; ++i)
{
mItems.Add(new Item(string.Format("Item {0}", i.ToString("00"))));
}
}
private void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.OldItems != null)
{
foreach (Item item in e.OldItems)
{
item.PropertyChanged -= Item_PropertyChanged;
mCheckedItems.Remove(item);
}
}
if (e.NewItems != null)
{
foreach (Item item in e.NewItems)
{
item.PropertyChanged += Item_PropertyChanged;
if (item.IsChecked) mCheckedItems.Add(item);
}
}
UpdateText();
}
private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "IsChecked")
{
Item item = (Item)sender;
if (item.IsChecked)
{
mCheckedItems.Add(item);
}
else
{
mCheckedItems.Remove(item);
}
UpdateText();
}
}
private void UpdateText()
{
switch (mCheckedItems.Count)
{
case 0:
Text = "<none>";
break;
case 1:
Text = mCheckedItems.First().Name;
break;
default:
Text = "<multiple>";
break;
}
}
}
// Test item class
// Test item class
internal class Item : ObservableObject
{
public string Name { get; private set; }
public bool IsChecked
{
get { return _isChecked; }
set { Set(ref _isChecked, value); }
}
private bool _isChecked;
public Item(string name)
{
Name = name;
}
public override string ToString()
{
return Name;
}
}
If the selectable text is an issue, you may want to create a custom ComboBox control template (default example here). Alternatively, you could use something else instead of a ComboBox, and make it look like a ComboBox.
Screenshot of example:
Using a combination of the #Erik83 and #Xavier solution I still had the problem that selecting a ComboBoxItem in a location right from the CheckBox text closes the ComboBox-DropDown and shows the ToString() value of the ComboBoxItem as the CheckBox is not stretched to DropDown-Width. I solved it by adding
HorizontalContentAlignment="Stretch"
to the CheckBox and adding the ItemContainerStyle:
<ComboBox x:Name="combobox"
Background="White"
Padding="2"
Text="{Binding ElementName=DockPanelTemplateComboCheck, Path=ComboTextFilter}"
IsEditable="True"
IsReadOnly="True"
HorizontalAlignment="Stretch"
ItemsSource="{Binding ...}"
IsDropDownOpen="{Binding Path=DropOpen, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, UpdateSourceTrigger=PropertyChanged}">
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked}" Content="{Binding Eintrag}" HorizontalContentAlignment="Stretch" Checked="CheckBox_Checked_Unchecked" Unchecked="CheckBox_Checked_Unchecked"/>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
CodeBehind:
private string combotextfilter = "<No Selection>";
public string ComboTextFilter
{
get { return combotextfilter; }
set
{
if (value != null && value.IndexOf("ComboModel") != -1) return;
combotextfilter = value;
NotifyPropertyChanged(nameof(ComboTextFilter));
}
}
private void CheckBox_Checked_Unchecked(object sender, RoutedEventArgs e)
{
switch (((ObservableCollection<ComboModel>)combobox.ItemsSource).Count(x => x.IsChecked))
{
case 0:
ComboTextFilter = "<No Selection>";
break;
case 1:
ComboTextFilter = ((ObservableCollection<ComboModel>)combobox.ItemsSource).Where(x => x.IsChecked).First().Eintrag;
break;
default:
ComboTextFilter = ((ObservableCollection<ComboModel>)combobox.ItemsSource).Where(x => x.IsChecked).Select(x => x.Eintrag).Aggregate((i, j) => i + " | " + j);
//ComboTextFilter = "<Multiple Selected>";
break;
}
NotifyPropertyChanged(nameof(C_Foreground));
}
public bool DropOpen
{
get { return dropopen; }
set { dropopen = value; NotifyPropertyChanged(nameof(ComboTextFilter)); }
}
private bool dropopen = false;
#Xaviers answer works 99% of the way. However the user is able to accidently select a checkbox and then the ToString() of the checkbox is shown as the selected text. This can happen quite alot actually. I havent yet worked out why this happens, but I've found a way to prevent this.
Create a bool property that binds to the DropDownOpen property of the combobox and when the DropDownOpen has changed to false it means that the DropDown has just been closed and you might be facing the above problem.
So here you just raise the propertychanged event for the viewmodel and pass the property bound to the Text property of the combobox.