WPF Validation & IDataErrorInfo - wpf

A note - the classes I have are EntityObject classes!
I have the following class:
public class Foo
{
public Bar Bar { get; set; }
}
public class Bar : IDataErrorInfo
{
public string Name { get; set; }
#region IDataErrorInfo Members
string IDataErrorInfo.Error
{
get { return null; }
}
string IDataErrorInfo.this[string columnName]
{
get
{
if (columnName == "Name")
{
return "Hello error!";
}
Console.WriteLine("Validate: " + columnName);
return null;
}
}
#endregion
}
XAML goes as follows:
<StackPanel Orientation="Horizontal" DataContext="{Binding Foo.Bar}">
<TextBox Text="{Binding Path=Name, ValidatesOnDataErrors=true}"/>
</StackPanel>
I put a breakpoint and a Console.Writeline on the validation there - I get no breaks. The validation is not executed. Can anybody just press me against the place where my error lies?

This may be a silly answer, but by default the binding calls the setter when LostFocus happens. Try doing that if you haven't done that.
If you want the error code to be triggered on every key press, Use UpdateSourceTrigger=PropertyChanged inside the binding.

You forgot to implement INotifyPropertyChanged on the 'Bar' class, then only the binding system will trigger the setter.
So your 'Name' property should most likely be.
public string Name
{
get{ return _name; }
set
{
_name = value;
RaisePropertyChanged("Name"); // Or the call might OnPropertyChanged("Name");
}
}

I am not familiar with the EntityObject class, nor can I find it in the .NET Framework documentation or a quick google search.
Anyway, what you need to do us use NotifyOnValidationError as well:
<TextBox Text="{Binding Path=Name, ValidatesOnDataErrors=true, NotifyOnValidationError=true}"/>

Try setting the Mode=TwoWay on your binding

You should create local window resource containing Bar class reference and use its key to set StackPanel data context property. Also, don't forget to import its namespace in the window or user control.
Your XAML code should be like following:
<Window x:Class="Project.WindowName"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BarNamespace">
<Window.Resources>
<local:Bar x:Key="bar" />
</Window.Resources>
<StackPanel Orientation="Horizontal" DataContext="{StaticResource bar}">
<TextBox Text="{Binding Path=Name, ValidatesOnDataErrors=true}"/>
</StackPanel>
</Window>

You should make the methods implementing the IDataErrorInfo as public.

Related

Data Binding doesn't work in xaml

I try to use binding to display Hi in the Text content.
However, when clicking the button, it doesn't work.
Could someone help me to solve the problem?
Thanks.
1.XAML CODE :
<Window x:Class="Wpftest.binding.Window0"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window0" Height="300" Width="300">
<Grid>
<TextBox x:Name="textBox2" VerticalAlignment="Top" Width="168"
Text="{Binding Source= stu, Path= Name, Mode=TwoWay}"/>
</Grid>
</Window>
2.Class :
namespace Wpftest.binding.Model
{
public class student : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string name;
public string Name
{
get { return name; }
set { name = value;
if(this.PropertyChanged != null)
{
this.PropertyChanged.Invoke(this, new
PropertyChangedEventArgs("Name"));
}
}
}
}
}
3.XAML.cs:
namespace Wpftest.binding
{
public partial class Window0 : Window
{
student stu;
public Window0()
{
InitializeComponent();
stu = new student();
}
private void button_Click(object sender, RoutedEventArgs e)
{
stu.Name += "Hi!";
}
}
}
There are many ways to achieve what you need; the correct method depends very much on what style of application you want to create. I'll demonstrate two methods that will require minimal changes from your supplied example:
Method 1
Set the DataContext to stu and bind to the Name property.
XAML.cs
private student stu;
public Window0()
{
InitializeComponent();
stu = new student();
DataContext = stu;
}
XAML code
<TextBox Text="{Binding Path=Name, Mode=TwoWay}"/>
Method 2
Generally you will set the DataContext to some object other than the Window (e.g. the ViewModel if you are following the MVVM pattern), but sometimes you may need to bind a control to some property of the Window. In this case the DataContext can't be used, but you can still bind to a property of the Window by using RelativeSource. See below:
XAML.cs
// note this must be a property, not a field
public student stu { get; set; }
public Window0()
{
InitializeComponent();
stu = new student();
}
XAML code
<TextBox Text="{Binding Path=stu.Name, Mode=TwoWay,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
Hint: if you are having trouble with WPF data binding, then it often helps to look at the debugger output window to see the binding trace messages. And debugging can be further enhanced by adding this namespace to the Window element
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
and then setting the TraceLevel e.g.
<TextBox Text="{Binding Source=stu, diag:PresentationTraceSources.TraceLevel=High}"/>
Basically you need to set DataContext property to your Window.
For example:
public MainWindow()
{
DataContext=new YourViewModel();
}
DataContext of Window is a way to communicate between View(XAML) and ViewModel(C# code)
In addition, you can add DataContext in xaml:
<Window.DataContext>
<local:YourViewModel/>
</Window.DataContext>
Also, instead of handling Click event, you should use Command property of Button. Example can be seen here.

WPF. Understanding error validation inside a UserControl with MVVM

I'm trying to validate a form in a UserControl element that is being used by another UserControl that is inside a Window.
I'm using MVVM pattern and i'm implementing the INotifyDataErrorInfo in the ViewModel of the last UserControl child.
The problem is that, when an error occurs, both, the TextBox inside the UserControl that binds to the property that has generated the error, and the UserControl itself get surrounded by a red box indicating the error, and i want just the TextBox to be highlighted.
Here is the code:
The Window that has the MainView (or the first UserControl):
<Grid>
<pages:MainPage>
<pages:MainPage.DataContext>
<vm:MainViewModel/>
</pages:MainPage.DataContext>
</pages:MainPage>
</Grid>
(It just contains a UserControl as a page)
The UserControl of the "MainPage", that contains the other (and last) UserControl as a page inside a page:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<DataTemplate DataType="{x:Type vm:SearchViewModel}">
<pages:SearchPage/>
</DataTemplate>
...
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
...
<ContentControl Content="{Binding CurrentPage}"/>
Ok, now beleive me, "CurrentPage" has a ViewModel object taken from a MainViewModel property, so lets suppose that "CurrentPage" is a "SearchViewModel" object, so there we have the SearchPage UserControl.
And now the last UserControl, the SearchPage:
<TextBox Grid.Column="1" Grid.Row="0" Text="{Binding CaseNumber}"/>
<TextBox Grid.Column="1" Grid.Row="1" Text="{Binding PatientNumber}"/>
<TextBox Grid.Column="1" Grid.Row="2" Text="{Binding PatientName}"/>
<TextBox Grid.Column="1" Grid.Row="3" Text="{Binding PatientFamilyName}"/>
<TextBox Grid.Column="1" Grid.Row="4" Text="{Binding PatientMotherMaidenName}"/>
<TextBox Grid.Column="1" Grid.Row="5" Text="{Binding DoctorName}"/>
Just to make the post as small as possible, i've just added the "form" section of the UserControl.
And now the most important part, the SearchViewModel with the INotifyDataErrorInfo implementation:
public class SearchViewModel : ViewModelBase, INotifyDataErrorInfo, IVMPage
{
private SearchModel searchModel = new SearchModel();
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
private Dictionary<string, List<string>> errors = new Dictionary<string, List<string>>();
private string patientNumber;
public string PatientNumber
{
get { return patientNumber; }
set
{
int number;
patientNumber = value;
if (int.TryParse(value, out number))
{
searchModel.PatientNumber = number;
ClearErrors("PatientNumber");
}
else
{
AddErrors("PatientNumber", new List<string> { "The value must be a number" });
}
RaisePropertyChanged("PatientNumber");
}
}
private string caseNumber;
public string CaseNumber
{
get { return caseNumber; }
set
{
int number;
caseNumber = value;
if (int.TryParse(value, out number))
{
searchModel.CaseNumber = number;
ClearErrors("CaseNumber");
}
else
{
AddErrors("CaseNumber", new List<string> { "The value must be a number" });
}
RaisePropertyChanged("CaseNumber");
}
}
....
private void ClearErrors(string propertyName)
{
errors.Remove(propertyName);
if (ErrorsChanged != null)
ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
}
private void AddErrors(string propertyName, List<string> newErrors)
{
errors.Remove(propertyName);
errors.Add(propertyName, newErrors);
if(ErrorsChanged != null)
ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
}
public System.Collections.IEnumerable GetErrors(string propertyName)
{
if(string.IsNullOrEmpty(propertyName))
{
return errors.Values;
}
else
{
if(errors.ContainsKey(propertyName))
{
return errors[propertyName];
}
else
{
return null;
}
}
}
public bool HasErrors
{
get { return (errors.Count() > 0); }
}
So, the problem is:
For example, if i introduce characters in "CaseNumber" TextBox, it is surrounded with a red line indicating the error AND all the SearchPage UserControl is also surrounded with another red line. What i want is just to mark the TextBox with the red line to indicate the error and NOT all the UserControl.
The curious thing is that, if i comment the sections at AddError and ClearError methods where the ErrorChanged event is fired, the UserControl is no longer surrounded with the red line... But i don't lnow why...
Sorry for the long question and thanks.
Ok, the answer is simple.
The problem was with this line:
<ContentControl Content="{Binding CurrentPage}"/>
Because WPF by default sets the ValidatesOnNotifyDataErrors property to true, when an error happens inside the "CurrentPage" UserControl, the TextBox that generated the error inside the UserControl indicates the error with a red line arround him, as expected, BUT ALSO the ContentControl checks the "GetErrors" method and draws another redline arround all the "CurrentPage" UserControl.
To avoid this and just indicate the error at the TextBox and not all the UserControl, just had to add this to the ContentControl declaration:
<ContentControl Content="{Binding CurrentPage, ValidatesOnNotifyDataErrors=False}"/>
Brief version
Color the background of the text box instead of the border.
(optional) Detailed version
Been there, run into that problem. The border of a textbox is reasonably difficult to change, as so many things play with it. For example, if you are using DevExpress, you have to override the whole textbox style to get at the border, then you start to lose the natural highlighting when the box is selected, etc.
Thus, I suggest coloring the background of the textbox to indicate an error. Its much more obvious to the user, looks great, and works well in practice.
Use a very light red color, this page is good for finding colors that are in harmony with the existing color scheme of your page:
https://color.adobe.com/create/color-wheel/

Extremely simple silverlight binding not working

This should be an extremely simple solution, but searching through the internet there seems to be multiple different ways to do binding and NONE seem to actually work.
I've created a simple application with a button, textbox and listbox. The user adds text to the textbox, clicks Add and I want the text to appear in the list box. Note that the Add button will create a Person with the firstname the text in the textbox and the last name "Jones". This is just to figure out how to get binding to actually work. I have the ObservableCollection but can't seem to even figure out how to put in the resource to the object within the class itself. Is this even possible? do I have to create a separate class to have a binding?
Here is the complete XMAL
<UserControl x:Class="simpleBinding.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:z="clr-namespace:simpleBinding"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Canvas x:Name="LayoutRoot" Background="White">
<Button Name="_b" Content="Add" Height="23" HorizontalAlignment="Left" VerticalAlignment="Top" Width="58" Canvas.Left="90" Canvas.Top="5" Click="OnAdd" />
<TextBox Name="_tb" Canvas.Left="12" Canvas.Top="4" Height="24" Width="72"></TextBox>
<ListBox Name="_list" Canvas.Left="18" Canvas.Top="41" Height="98" Width="190" />
</Canvas>
and here is the complete Code behind
namespace simpleBinding
{
public partial class MainPage : UserControl
{
public ObservableCollection<Person> PersonList = new ObservableCollection<Person> ();
public MainPage()
{
InitializeComponent();
}
private void OnAdd(object sender, RoutedEventArgs e)
{
PersonList.Add(new Person(_tb.Text, "Jones"));
}
}
public class Person
{
public string FirstName {private set; get;}
public string LastName {private set; get; }
public Person(string fName, string lName)
{
FirstName = fName;
LastName = lName;
}
}
}
thanks for any help,
chris
To illustrate Ravuthasamy's & aqwert's comments. You have to set a DataContext first. You can set this in DataContext or read how MVVM work (It's a good Silvelight binding pattern) :
c#
public MainPage()
{
InitializeComponent();
DataContext = this;
}
After you can bind the class properties to elements :
Xaml
<ListBox
ItemsSource="{Binding PersonList}"
Canvas.Left="18"
Canvas.Top="41"
Height="98"
Width="190" />
Following the timeline you can see that this has taken me a week to finally get to a solution. I post it here now in hopes that someone else won't waste this much time. There seems to be a lot of posts about how to deal with this issue and the examples are limited. They either show only C# or Xaml. Then CollectionChanged and PropertyChanged aren't dealt with in a single example.
This is a simple example, that implements both collection changed and property changed. As well as binding in Xaml
Here is the Xaml.
<UserControl x:Class="simpleBinding.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:src="clr-namespace:simpleBinding"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Canvas x:Name="LayoutRoot" Background="White" DataContext="{Binding}">
<Canvas.Resources>
<src:PersonList x:Key="myDataSource"></src:PersonList>
</Canvas.Resources>
<Button Name="_b" Content="Add" Height="23" HorizontalAlignment="Left" VerticalAlignment="Top" Width="58" Canvas.Left="90" Canvas.Top="5" Click="OnAdd" />
<Button Canvas.Left="150" Canvas.Top="5" Content="Edit" Height="23" Name="button1" Width="58" Click="OnEdit" />
<TextBox Name="_tb" Canvas.Left="12" Canvas.Top="4" Height="24" Width="72"></TextBox>
<ListBox Name="_list" Canvas.Left="18" Canvas.Top="41" Height="98" Width="190" ItemsSource="{Binding Source={StaticResource myDataSource}}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=FirstName}" Margin="0,0,2,0" />
<TextBlock Text="{Binding Path=LastName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Canvas>
Add a xmlns that will reference your code behind. In this case my namespace is xmlns:src then you can use VS intellisense to go to the correct class.
Add a resource to the layoutRoot item. In my case I'm using a canvas, but it could be Grid or Stackpanel etc.
With the resource declared, you can now set the ItemSource binding in the ListBox.
I've chosen to use a template to display the data which I think is really cool (best part of Xaml!) In this case there are two textBlocks but if my underlying data source had an image, I could have used this was well to graphically display the data. The binding for each textbox can be set because the exposed properties of the object are declared in the C# code. Which will be discussed next
C# Code behind
namespace simpleBinding
{
public partial class MainPage : UserControl
{
public PersonList m_pList = new PersonList();
public MainPage()
{
InitializeComponent();
_list.ItemsSource = m_pList;
m_pList.Add(new Person("John", "Doe"));
}
private void OnAdd(object sender, RoutedEventArgs e)
{
m_pList.Add(new Person("Jones", _tb.Text));
}
private void OnEdit(object sender, RoutedEventArgs e)
{
m_pList[1].FirstName = _tb.Text;
}
}
public class PersonList : ObservableCollection<Person> , INotifyPropertyChanged
{
public PersonList() : base() // need to call base on intialization otherwise the binded resource is not updated.
{
Add(new Person("Willa", "Cather"));
Add(new Person("Isak", "Dinesen"));
Add(new Person("Victor", "Hugo"));
Add(new Person("Jules", "Verne"));
}
}
public class Person : INotifyPropertyChanged
{
private string _fName;
private string _lName;
public event PropertyChangedEventHandler PropertyChanged;
public string FirstName
{
set
{
_fName = value;
NotifyPropertyChanged("FirstName");
}
get
{
return _fName;
}
}
public string LastName
{
set
{
_lName = value;
NotifyPropertyChanged("LastName");
}
get
{
return _lName;
}
}
public Person(string fName, string lName) : base()
{
FirstName = fName;
LastName = lName;
}
public override string ToString()
{
return String.Format("{0} {1}", FirstName, LastName);
}
private void NotifyPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
I've chosen to use the ObservableCollection because it implements INotifyCollectionChanged. The public variable is exposed which allows you to bind to the resource declared in the Xaml. (Better code, make the var private and have a property that exposes the variable through a get!)
The ListBox _List needs to have its ItemsSource property set in Code Behind!!! without this whenever you change the list (add, delete etc) the UI is not updated. AND in fact you do not need the binding in the ListBox at all because we set the source in Code behind it is nice however in that in the designer with this bound control you can see that the binding is working because there are four names added when instantiating the PersonList.
The ObservableCollection needs to have the INotifyCollectionChanged added. Without this, when a property is changed the UI is NOT changed.
The properties that are to be exposed to the UI need to be implement in the object that is contained within the ObservableCollection (in my case the class Person exposed both FirstName and LastName) and then these properties can be bound in the Xaml (see the textBlocks's)
INotifyPropertyChanged requires that you implement a PropertyChanged event i.e. public event PropertyChangedEventHandler PropertyChanged;
To actually fire that event the "Person" object needs to implement code to do that, which in my case is the NotifyPropertyChanged Method. Each time a property is set, I call this method, which in turn looks to see is the PropertyChanged event is not null, and if not, then it raises that event.
Here is the key to property changes, without adding the , INotifyPropertyChanged to the Observable collection PropertyChanged is null.
Hope this helps someone

Siliverlight databound combobox doesn't display initialized value

I am databinding a view to a viewmodel and am having trouble initializing a combobox to a default value. A simplification of the class I'm using in the binding is
public class LanguageDetails
{
public string Code { get; set; }
public string Name { get; set; }
public string EnglishName { get; set; }
public string DisplayName
{
get
{
if (this.Name == this.EnglishName)
{
return this.Name;
}
return String.Format("{0} ({1})", this.Name, this.EnglishName);
}
}
}
The combobox is declared in the view's XAML as
<ComboBox x:Name="LanguageSelector" Grid.Row="0" Grid.Column="1"
SelectedItem="{Binding SelectedLanguage,Mode=TwoWay}"
ItemsSource="{Binding AvailableLanguages}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
and the viewmodel contains this code
private List<LanguageDetails> _availableLanguages;
private LanguageDetails _selectedLanguage;
public LoginViewModel()
{
_availableLanguages = LanguageManager.GetLanguageDetailsForSet(BaseApp.AppLanguageSetID);
_selectedLanguage = _availableLanguages.SingleOrDefault(l => l.Code == "en");
}
public LanguageDetails SelectedLanguage
{
get { return _selectedLanguage; }
set
{
_selectedLanguage = value;
OnPropertyChanged("SelectedLanguage");
}
}
public List<LanguageDetails> AvailableLanguages
{
get { return _availableLanguages; }
set
{
_availableLanguages = value;
OnPropertyChanged("AvailableLanguages");
}
}
At the end of the constructor both _availableLanguages and _selectedLanguage variables are set as expected, the combobox's pulldown list contains all items in _availableLanguages but the selected value is not displayed in the combobox. Selecting an item from the pulldown correctly displays it and sets the SelectedLanguage property in the viewmodel. A breakpoint in the setter reveals that _selectedLanguage still contains what it was initialized to until it is overwritten with value.
I suspect that there is some little thing I'm missing, but after trying various things and much googling I'm still stumped. I could achieve the desired result in other ways but really want to get a handle on the proper use of databinding.
You need to change the order of you bindings in XAML so that your ItemsSource binds before the SelectedItem.
<ComboBox x:Name="LanguageSelector" Width="100"
ItemsSource="{Binding AvailableLanguages}"
SelectedItem="{Binding SelectedLanguage,Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
If you set a breakpoint on the 'get' of both the SeletedLanguage and AvailibleLanguage, you will notice that the SelectedLanguage gets hit before your AvailibleLanguage. Since that's happening, it's unable to set the SelectedLanguage because the ItemsSource is not yet populated. Changing the order of the bindings in your XAML will make the AvailibleLanguages get hit first, then the SelectedLanguage. This should solve your problem.
1) When you assign the SelectedLanguage, use the public property SelectedLanguage instead of the private _selectedLanguage, so that the setter gets executed,
2) You need to move the assignment of the selectedlanguage to the moment that the view has been loaded. You can do it by implementing the Loaded event handler on the View. If you want to be "mvvm compliant" then you should use a Blend behavior that will map UI loaded event to a viewmodel command implementation in which you would set the selected language.

ComboBox wpf not item not being selected

I am trying to bind a combo box to a list of objects, and it works great, besides the selected value, am I missing somethign?
<ComboBox ItemsSource="{Binding OrderInfoVm.AllCountries}"
SelectedValuePath="country_code" DisplayMemberPath="country_name"
SelectedValue="{Binding OrderInfoVm.BillingCountry}" />
Basically I want to bind value to country codes and set the selected value to the country code bound to OrderInfoVm.BillingCountry (which implements INotifyPropertyChanged)
Initially when the control loads selected value is empty, but on click BillingCountry is populated. Selected value does not seem to change. How can I remedy that?
I do agree with Alex that using SelectedItem gives the desired behaviour. See the code below. It works and will hopefully help you further:
<Window x:Class="SelectedValueSpike.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">
<StackPanel>
<ComboBox ItemsSource="{Binding OrderInfoVm.AllCountries}"
SelectedValuePath="country_code" DisplayMemberPath="country_name"
SelectedItem="{Binding OrderInfoVm.BillingCountry}"
IsSynchronizedWithCurrentItem="True"
Name="AllCountriesBox"/>
<TextBox Text="{Binding ElementName=AllCountriesBox, Path=SelectedValue}"/>
<Button>
Change the textbox to "Ca","NL",or "US" and click!
</Button>
</StackPanel>
</Window>
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
namespace SelectedValueSpike
{
public partial class Window1 : Window
{
public OrderInfoVm OrderInfoVm{ get; set;}
public Window1()
{
InitializeComponent();
OrderInfoVm=new OrderInfoVm();
OrderInfoVm.AllCountries.Add(new Country("US","US of A"));
OrderInfoVm.AllCountries.Add(new Country("NL","Netherlands"));
OrderInfoVm.AllCountries.Add(new Country("Ca","Canada"));
OrderInfoVm.BillingCountry = OrderInfoVm.AllCountries[1];
DataContext = this;
}
}
public class OrderInfoVm:INotifyPropertyChanged
{
public OrderInfoVm()
{
AllCountries=new ObservableCollection<Country>();
}
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<Country> _allCountries;
public ObservableCollection<Country> AllCountries
{
get { return _allCountries; }
set
{
_allCountries = value;
OnPropertyChanged("AllCountries");
}
}
private Country _billingCountry;
public Country BillingCountry
{
get { return _billingCountry; }
set
{
_billingCountry = value;
OnPropertyChanged("BillingCountry");
}
}
private void OnPropertyChanged(string property)
{
if(PropertyChanged!=null)
PropertyChanged(this,new PropertyChangedEventArgs(property));
}
}
public class Country
{
public string country_code { get; set; }
public string country_name { get; set; }
public Country(string code, string name)
{
country_code = code;
country_name = name;
}
}
}
Maybe you are trying to implement something similar to this: Bound ComboBox
Try changing it to SelectedItem and set Mode=TwoWay...
<ComboBox ItemsSource="{Binding OrderInfoVm.AllCountries}"
SelectedValuePath="country_code" DisplayMemberPath="country_name"
SelectedItem="{Binding OrderInfoVm.BillingCountry, Mode=TwoWay}" />
Edit: You may not need to change it to SelectedItem, perhaps just setting TwoWay will work, but that is how I've done it in my own code.
Please ensure that you've specified correct binding path.
Try starting project in debug mode and look at the output window to see if there are any binding errors
Give this a shot; I believe you have your SelectedValuePath and SelectedValue mixed up:
<ComboBox ItemsSource="{Binding OrderInfoVm.AllCountries}"
SelectedValue="country_code"
DisplayMemberPath="country_name"
SelectedValuePath="{Binding OrderInfoVm.BillingCountry}" />
For Reference:
ItemsSource = Gets or sets a collection used to generate the content of the ItemsControl (ComboBox).
SelectedValue = Gets or sets the value of the SelectedItem, obtained by using SelectedValuePath.
SelectedValuePath = Gets or sets a value that indicates the path used to get the SelectedValue from the SelectedItem.
DisplayMemberPath = Gets or sets a path to a value on the source object to serve as the visual representation of the object.

Resources