What is the difference betweeen the following:
SelectedItem
SelectedValue
SelectedValuePath
All these dependency properties are defined in Selector class. I often confuse SelectedItem with SelectedValue , and SelectedValue with SelectedValuePath.
I would like to know the difference between them, and also when do we use them, especially SelectedValue and SelectedValuePath. Please explain their use with some simple examples.
Their names can be a bit confusing :). Here's a summary:
The SelectedItem property returns the entire object that your list is bound to. So say you've bound a list to a collection of Category objects (with each Category object having Name and ID properties). eg. ObservableCollection<Category>. The SelectedItem property will return you the currently selected Category object. For binding purposes however, this is not always what you want, as this only enables you to bind an entire Category object to the property that the list is bound to, not the value of a single property on that Category object (such as its ID property).
Therefore we have the SelectedValuePath property and the SelectedValue property as an alternative means of binding (you use them in conjunction with one another). Let's say you have a Product object, that your view is bound to (with properties for things like ProductName, Weight, etc). Let's also say you have a CategoryID property on that Product object, and you want the user to be able to select a category for the product from a list of categories. You need the ID property of the Category object to be assigned to the CategoryID property on the Product object. This is where the SelectedValuePath and the SelectedValue properties come in. You specify that the ID property on the Category object should be assigned to the property on the Product object that the list is bound to using SelectedValuePath='ID', and then bind the SelectedValue property to the property on the DataContext (ie. the Product).
The example below demonstrates this. We have a ComboBox bound to a list of Categories (via ItemsSource). We're binding the CategoryID property on the Product as the selected value (using the SelectedValue property). We're relating this to the Category's ID property via the SelectedValuePath property. And we're saying only display the Name property in the ComboBox, with the DisplayMemberPath property).
<ComboBox ItemsSource="{Binding Categories}"
SelectedValue="{Binding CategoryID, Mode=TwoWay}"
SelectedValuePath="ID"
DisplayMemberPath="Name" />
public class Category
{
public int ID { get; set; }
public string Name { get; set; }
}
public class Product
{
public int CategoryID { get; set; }
}
It's a little confusing initially, but hopefully this makes it a bit clearer... :)
Chris
To answer a little more conceptually:
SelectedValuePath defines which property (by its name) of the objects bound to the ListBox's ItemsSource will be used as the item's SelectedValue.
For example, if your ListBox is bound to a collection of Person objects, each of which has Name, Age, and Gender properties, SelectedValuePath=Name will cause the value of the selected Person's Name property to be returned in SelectedValue.
Note that if you override the ListBox's ControlTemplate (or apply a Style) that specifies what property should display, SelectedValuePath cannot be used.
SelectedItem, meanwhile, returns the entire Person object currently selected.
(Here's a further example from MSDN, using TreeView)
Update: As #Joe pointed out, the DisplayMemberPath property is unrelated to the Selected* properties. Its proper description follows:
Note that these values are distinct from DisplayMemberPath (which is defined on ItemsControl, not Selector), but that property has similar behavior to SelectedValuePath: in the absence of a style/template, it identifies which property of the object bound to item should be used as its string representation.
SelectedItem and SelectedValue are an object.
and SelectedValuePath is a string.
for example using the ListBox:
Below listbox1.SelectedValue becomes a string value.
string value = listbox1.SelectedValue;
if you say give me listbox1.SelectedItem it will give you the entire object.
ListItem item = listbox1.SelectedItem;
string value = item.value;
inspired by this question I have written a blog along with the code snippet here. Below are some of the excerpts from the blog
SelectedItem – Selected Item helps to bind the actual value from the DataSource which will be displayed. This is of type object and we can bind any type derived from object type with this property. Since we will be using the MVVM binding for our combo boxes in that case this is the property which we can use to notify VM that item has been selected.
SelectedValue and SelectedValuePath – These are the two most confusing and misinterpreted properties for combobox. But these properties come to rescue when we want to bind our combobox with the value from already created object. Please check my last scenario in the following list to get a brief idea about the properties.
Every control that uses Collections to store data have SelectedValue, SelectedItem property. Examples of these controls are ListBox, Dropdown, RadioButtonList, CheckBoxList.
To be more specific if you literally want to retrieve Text of Selected Item then you can write:
ListBox1.SelectedItem.Text;
Your ListBox1 can also return Text using SelectedValue property if value has set to that before. But above is more effective way to get text.
Now, the value is something that is not visible to user but it is used mostly to store in database. We don't insert Text of ListBox1, however we can insert it also, but we used to insert value of selected item. To get value we can use
ListBox1.SelectedValue
Source
Related
In my project i have one Datagrid and i bind the following fields that are listed below.
CustomerID, Name, Email.
I have the Entity named WS_Customer. i have put one button control for all row in datagrid.If i click the button means the i need to get the CustomerID value.
How to get it.
If i put like this means,
WS_Customer getid=(WS_Customer)DG.SelectedItem;
getidshows null..
How i got the value?
Assuming you are using MVVM... and you DataGrid is bound to a collection of the WS_Customer objects you can put a property in your view model that you can bind to the SelectedItem property of the DataGrid. Keep in mind the row must be selected for the SelectedItem property to have a value.
In xaml:
<DataGrid SelectedItem="{Binding SelectedWS_Customer}" />
In viewModel:
public WS_Customer SelectedWS_Customer
{
get
{
return _selectedWS_Customer; //private variable
}
set
{
_selectedWS_Customer = value;
}
}
Why doesn't #2 work? (It seems like most examples say to do this).
#1(works)<ComboBox ItemsSource="{Binding Marker.ReadOnlyContentRegions}"
SelectedItem="{Binding Marker.SelectedRegion}"
SelectedValue="{Binding
Marker.SelectedRegion.UniqueId, Mode=TwoWay}"
SelectedValuePath="UniqueId"
DisplayMemberPath="Label" />
#2(doesn't work)<ComboBox ItemsSource="{Binding Marker.ReadOnlyContentRegions}"
SelectedValue="{Binding Marker.SelectedRegion.UniqueId,Mode=TwoWay}"
SelectedValuePath="UniqueId"
DisplayMemberPath="Label" />
This is the class that contains the objects that should be databound.
class...
public CancellableObservableCollection<InvisibleContentMarkerBase>
ReadOnlyContentRegions
{
get { return
CancellableObservableCollection<InvisibleContentMarkerBase>)
GetValue(ReadOnlyContentRegionsProperty); }
set { SetValue(ReadOnlyContentRegionsProperty, value); }
}
public static readonly DependencyProperty ReadOnlyContentRegionsProperty =
DependencyProperty.Register("ReadOnlyContentRegions",
typeof(CancellableObservableCollection<InvisibleContentMarkerBase>),
typeof(TargetedContentMarker), new UIPropertyMetadata(null);
public InvisibleContentMarkerBase SelectedRegion
{
get { return (InvisibleContentMarkerBase)GetValue(SelectedRegionProperty); }
set { SetValue(SelectedRegionProperty, value); }
}
public static readonly DependencyProperty SelectedRegionProperty =
DependencyProperty.Register("SelectedRegion",
typeof(InvisibleContentMarkerBase), typeof(TargetedContentMarker), new
UIPropertyMetadata(null));
...// end of class
First of all, don't set both the SelectedItem and the SelectedValue
They both set the exact same property, so when you set both only one value will actually get used
When you set SelectedValuePath and SelectedValue, you are setting the selected item by value. The SelectedValuePath tells WPF what property on objects in the collection is the Id field, and SelectedValue tells WPF to set the selected item to the value that is equal to SelectedValue.
SelectedItem simply tells WPF to select the item in the collection that exactly matches the SelectedItem object. Note that this comparison is by reference, so if the SelectedItem is a class that doesn't point to the exact same reference in memory as one of the objects in the ItemsSource, it won't evaluate the two objects as the same and won't set the item as Selected
So in short, either get rid of the SelectedItem binding and just use SelectedValue/SelectedValuePath, or remove SelectedValue/SelectedValuePath and ensure that the object bound in SelectedItem refers to the exact same object in memory as the copy in the ItemsSource.
If you really can't reference that object, and insist on using SelectedItem instead of SelectedValue, you could also overwrite the .Equals() on your class so it returns true if the data is equal, regardless of if the memory reference is the same. I prefer to avoid this since this changes the functionality of any instances of this class, but wanted to let you know that option is available.
I am using wpf and MVVM pattern. I need to bind my comboboxedit ItemsSource to collection of User class. It contains Employee field, which contains string FullName field. I need to bind selected FullName value to string Field of another object in my ViewModel (Document->UserFullName). How can I do this.
If I understand you correctly, you have a ComboBox bound to a List of User-instances. The User class has a property of type Employee and the Employee class has a property called FullName of type string. The viewmodel also has a property of type Document and the Document class has a property called UserFullName of type string. When you select a value (a user) in the ComboBox you want to set the value of FullName (User.Employee.FullName) to the UserFullName property of Document (Document.UserFullName).
Correct?
If that is the only thing you want to do, maybe the easisest solution would be to not bind the ComboBox to a collection of User-instances but to a collection of strings that is the FullName of those users (from Employee). That collection wouldn't be to hard to create just by iterating through your list of users. If you bind the ComboBox to a collection of strings then you should be able to just bind the SelectedValue of the ComboBox directly to the UserFullName of the Document (Document.UserFullName).
Another solution would be to have a property "SelectedUser" of type User in your viewmodel and bind the SelectedValue of the ComboBox to this. Whenever the value of this change you also set the value of Document.UserFullName, like this:
private User _selectedUser;
public User SelectedUser
{
get
{
_return _selectedUser;
}
set
{
if (value != _selectedUser)
{
_selectedUser = value;
Document.UserFullName = _selectedUser.Employee.FullName;
OnPropertyChanged("SelectedUser");
}
}
}
I have a combobox bound to a collection, so the user can select one of the items. So far, so good.
The content of the combo box is driven by the item, but also by a value in my viewmodel. Imagine the value in my viewmodel is the language, I have dictionary of descriptions by language in my bound item, and I want to display the correct one.
How should I go about this?
This is a classic example of why the ViewModel exists - you want to have logic which depends on trivial state in the view, as well as the main model.
Imagine you are writing a unit test to run against the ViewModel for this behaviour. You would need the ViewModel to have a property mapped to the selected item. The ViewModel would also have another property which varies according to this selected item as well as the other value in the ViewModel you mentioned.
I think of this as the test-driven approach to ViewModel design - if you can't write a unit test to evaluate it then you haven't got the mix of state and published interfaces right.
So, yes, the ViewModel can solve the problem and if you push all the state down into it you can do the unification within the ViewModel.
Make an observable collection in your viewmodel of type Item. Bind the itemsource of your viewmodel to this observable collection.
public class Item
{
public String description {get;set;}
public String language {get;set;}
public override ToString()
{
return description;
}
}
Selected item would also be bound to a property of type Item as well.
The override of ToString displays the description.
The Selected item propery will have a reference to the selected object property where you can get the language from.
I have a DataGrid with ItemsSource set to a list of products and
I have a DataGridComboBoxColumn inside the DataGrid with ItemsSource set to a list of categories. That way I want the user to choose a certain category for each product.
I always get the binding error:
BindingExpression path error: 'Categories' property not found on 'object' ''Product' (Hash)
Well I do not want to make the Category list part of the Product entity as 1:N relation, although it would work that way.
I want to keep them separate.
Anyone knows a workaround?
Create class with static property like
static class ValueLists
{
public static IEnumerable<Category> Categories {get {... }}
}
and use following binding
ItemsSource="{x:Static myNs:ValueList.Categories}" />
this is kind of late reply but in order to share the knowledge I found this:
Binding a WPF DataGridComboBoxColumn with MVVM
This answer shows that is not always mandatory to convert the second list to a static class, you can always specify a RelativeSource and search for an specific Ancestor and then bind to the "other" list you have in your ViewModel.
This is probably relevant to your problem.
What is happening here?
The Columns collection is just a property in the Datagrid; this collection is not in the logical (or visual) tree, therefore the DataContext is not being inherited, which leads to there being nothing to bind to.