ComboBox binding with DisplayMemberPath and SelectedValuePath does not work as expected - c#

I bind an ObservableCollection to a ComboBox and set the DisplayMemberPath to the Name,
with the help of SelectedValuePath I wanted to achieve that Device.ObjectId gets the value of Object.Id.
But this doesn't seem to work, the Object.Name is still set for Device.ObjectId.
How do I get the ComboBox to show the Name but set the Id?
public class Object
{
public string Id { get; set; }
public string Name { get; set; }
}
ObservableCollection Objects = new ObservableCollection<Object>();
<ComboBox ItemsSource="{Binding Objects}" DisplayMemberPath="Name" SelectedValuePath="Id">
<ComboBox.Text>
<Binding Path="Device.ObjectId" UpdateSourceTrigger="PropertyChanged">
</ComboBox.Text>
</ComboBox>

Since you set the SelectedValuePath attribute to Id, you can bind to the SelectedValue attribute to get the value. Something like this:
<ComboBox ItemsSource="{Binding Objects}" DisplayMemberPath="Name"
SelectedValuePath="Id" SelectedValue="{Binding Device.ObjectId}" />

Related

Object Binding in Caliburn.Micro MVVM Framework with SelectedItem

I have the following Models:
public class Profile
{
public string name { get; set; }
public Member casemanager { get; set; }
public Member assistant { get; set; }
}
public class Member
{
public int Id { get; set; }
public int Type{ get; set; }
public string Name { get; set; }
}
In my ViewModel, I have the following objects:
an ObservableCollection<Profile> named Profiles that gets populated from a database;
a SelectedProfile of type Profile, of course;
Two Lists of Members named ListCaseManagers and ListAssistants that get populated from DB.
Each of the objects implement a NotifyOfPropertyChange method from Caliburn.Micro and are set with property and backing field.
The View:
<StackPanel>
<ListView ItemsSource="{Binding Profiles}" SelectedItem="{Binding SelectedProfile}" DisplayMemberPath="name" SelectionMode="Single" />
<ComboBox ItemsSource="{Binding ListCaseManagers}" SelectedItem="{Binding SelectedProfile.casemanager }" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="Name"/>
<ComboBox ItemsSource="{Binding ListAssistants}" SelectedItem="{Binding SelectedProfile.assistant}" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="Name"/>
</StackPanel>
In this XAML I was thinking that the SelectedItem of each ComboBox would bind directly to the SelectedProfile casemanager/assistant, changing everytime I modify the SelectedProfile using the ListView, but it doesn't seem to select the item in the combobox even though SelectedProfile.casemanager and SelectedProfile.assistant are not null.
What am I missing? Is there an easy way using conventions with Caliburn.Micro?
In combobox present couple properties which can allow you to modify your SelectedItem in your list.
SelectedValuePath="Id" <br/>
SelectedValue="{Binding SelectedProfile.casemanager.Id}"
Upd: this approach allow to bind selected element by their some unique attribute (ID or Name)
In case if you need to work with SelectedItem.. you must sure that props of items in Profiles collection are related to object in ListCaseManagers and ListAssistants
var query = from p in Profiles
join mgr in ListCaseManagers on p.casemanager==mgr
select p;
? query.Count
enter code here
I assume you will see 0.. since objects in collection are different.
to use the convention name of Caliburn, you have to give a name to your control
for example for ListView:
<StackPanel>
<ListView x:Name ="Profiles" />
</StackPanel>
with the convention of names, Caliburn binds automatically the collection Profiles and use SelectedProfiles as SelectedItem
You either need to raise the PropertyChanged for the SelectedProfile property when it's set:
private Profile_selectedProfile;
public ProfileSelectedProfile
{
get { return _selectedProfile; }
set
{
_selectedProfile = value;
NotifyOfPropertyChange(() => SelectedProfile);
}
}
Or you could bind to the SelectedItem property of the ListView control itself:
<StackPanel>
<ListView x:Name="lv" ItemsSource="{Binding Profiles}"
SelectedItem="{Binding SelectedProfile}"
DisplayMemberPath="name" SelectionMode="Single" />
<ComboBox ItemsSource="{Binding ListCaseManagers}"
SelectedItem="{Binding SelectedItem.casemanager, ElementName=lv }"
DisplayMemberPath="Name"/>
<ComboBox ItemsSource="{Binding ListAssistants}"
SelectedItem="{Binding SelectedItem.assistant, ElementName=lv}"
DisplayMemberPath="Name"/>
</StackPanel>

WPF bind combobox selected value to a string in a view model

I have this combobox in my view:
<ComboBox SelectedValue="{Binding StringObj, UpdateSourceTrigger=PropertyChanged}">
<ComboBoxItem>string0</ComboBoxItem>
<ComboBoxItem>string1</ComboBoxItem>
<ComboBoxItem>string2</ComboBoxItem>
<ComboBoxItem>string3</ComboBoxItem>
</ComboBox>
And in my view model I have this string object:
private string _stringObj;
public string StringObj
{
get { return _stringObj; }
set { _stringObj = value; }
}
How can I bind the selected value from the combobox to the string variable so I can work with it in the view model? This is what I have implemented so far but it doesn't work as I don't understand this binding stuff very well.
You can't set a string property to a ComboBoxItem value.
You could either replace the ComboBoxItems with strings in your XAML and bind the SelectedItem property:
<ComboBox SelectedItem="{Binding StringObj, UpdateSourceTrigger=PropertyChanged}"
xmlns:s="clr-namespace:System;assembly=mscorlib">
<s:String>string0</s:String>
<s:String>string1</s:String>
<s:String>string2</s:String>
<s:String>string3</s:String>
</ComboBox>
Or you could simply set the SelectedValuePath property to "Content":
<ComboBox SelectedValue="{Binding StringObj, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Content">
<ComboBoxItem>string0</ComboBoxItem>
<ComboBoxItem>string1</ComboBoxItem>
<ComboBoxItem>string2</ComboBoxItem>
<ComboBoxItem>string3</ComboBoxItem>
</ComboBox>
This will set your source property to the Content of the selected ComboBoxItem.

Add static item to RadComboBox in WPF

I have a set of two cascading RadComboBoxes - when one is set, the other populates. The second combobox has its ItemSource set to a CompositeCollection that's binding to an ObservableCollection in the viewmodel.
I'm attempting to add a static value to the list. The idea is that the CompositeCollection can change, but there should always be one static ComboBoxItem available named Other.
CustomerContact.cs:
public class CustomerContact
{
public string Name { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
}
CustomerContactSource:
<CollectionViewSource x:Key="CustomerContactSource" Source="{Binding CustomerSite.CustomerContacts}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="Name"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
OtherCustomerContact:
public CustomerContact OtherCustomerContactItem => new CustomerContact
{
Name = "Other",
Email = string.Empty,
PhoneNumber = string.Empty
};
Xaml page:
<telerik:RadComboBox ItemTemplate="{StaticResource ComboBoxItemTemplate}"
SelectedItem="{Binding CustomerContact}"
Text="{Binding Source=CustomerContact, Path=Name}">
<telerik:RadComboBox.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Source={StaticResource CustomerContactSource}}"/>
<TextBlock Text="{Binding Source=OtherCustomerContactItem, Path=Name}"/>
</CompositeCollection>
</telerik:RadComboBox.ItemsSource>
</telerik:RadComboBox>
I keep getting an error stating that no converter can be found for TextBlock to CustomerContact. What am I doing wrong? The RadComboBox has an ItemSource that's a list of CustomerContact and a single, unchanging item that's also of type CustomerContact.
Any help would be greatly appreciated!!!
The RadComboBox has an ItemSource that's a list of CustomerContact and
a single, unchanging item that's also of type CustomerContact.
That last part doesn't hold. You're wrapping the item in a TextBlock and that's
the type XAML sees. Giving an error due to lack of TypeConverter.
You could wrap the item:
in a Collection(Container) of its own.
in a RadComboBoxItem in the lines of what is
done here for a ListBox.

Extracting Data from DataGrid into Binded Combobox

What I am trying to do is take the string out of a specific column in my DataGrid and display that string in the Combobox.Text property. I am using this code to do so:
var dataString = ((DataRowView)dgvMain.SelectedItem).Row["Column"].ToString();
I have placed a breakpoint on this to see what it was pulling, and it was the correct String that is contained as an item in the Combobox, but whenever I try to set it via Combobox.Text, the Combobox is empty. I have however set my Combobox to isEditable = True and ReadOnly = True and this method works, but selecting an item in the Collection will display System.Data.DataRowView, due to the Combobox being binded to my DataTable. I'll add the XAML to my Combobox as well:
<ComboBox x:Name="cboAlarmType" HorizontalAlignment="Left" Margin="138,256,0,0" VerticalAlignment="Top" Width="320" TabIndex="5"
BorderBrush="Black" Background="White" ItemsSource="{Binding}" DisplayMemberPath="AlarmName" SelectedValuePath="AlarmName"
SelectedValue="{Binding Row.Column, ElementName=dgvMain}"/>
This method works in Textboxes and Checkboxes, but haven't seemed to figure it out for Comboboxes. Any guidance would be appreciated! :)
It appears to me that your DataContext for the ComboBox must be the DataTable you're using to populate the DataGrid, because ItemsSource="{Binding}". I'm going to assume that it's the DataContext for the DataGrid as well, and that your goal is to have the ComboBox selection reflect the selected row in the DataGrid.
<DataGrid
x:Name="dgvMain"
ItemsSource="{Binding}"
/>
<ComboBox
ItemsSource="{Binding}"
DisplayMemberPath="Column"
SelectedValuePath="Column"
SelectedValue="{Binding SelectedItem.Row[Column], ElementName=dgvMain}"
/>
If you'd like the selection changes to go both ways -- so a change to the ComboBox selection changes the DataGrid selection -- that's easy:
<DataGrid
x:Name="dgvMain"
ItemsSource="{Binding}"
SelectedValuePath="Column"
SelectedValue="{Binding SelectedValue, ElementName=ColumnComboBox}"
/>
<ComboBox
x:Name="ColumnComboBox"
ItemsSource="{Binding}"
DisplayMemberPath="Column"
SelectedValuePath="Column"
SelectedValue="{Binding SelectedItem.Row[Column], ElementName=dgvMain}"
/>
And here's a cleaner way to do the whole thing:
First, write a viewmodel class in C#.
public class ViewModelbase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class MyViewModel : ViewModelbase
{
public MyViewModel()
{
// I populated my DataTable with "Row 1", "Row 2", etc.
// Naturally you'd use a value from your own data.
SelectedColumnValue = "Row 2";
}
#region SelectedColumnValue Property
private String _selectedColumnValue = default(String);
public String SelectedColumnValue
{
get { return _selectedColumnValue; }
set
{
if (value != _selectedColumnValue)
{
_selectedColumnValue = value;
OnPropertyChanged();
}
}
}
#endregion SelectedColumnValue Property
public DataTable Data { get; protected set; }
}
Then use that SelectedColumnValue property to keep track of the selected value of "Column" for both controls. The bindings on the SelectedValue properties of the two controls will be two-way by default, because of framework stuff that makes it be that way (how's that for an explanation?). So if you change SelectedColumnValue programatically, the controls will update, and if the user changes the grid selection, the viewmodel property will be updated, which will in turn update the ComboBox -- and vice versa.
<DataGrid
ItemsSource="{Binding Data}"
SelectedValuePath="Column"
SelectedValue="{Binding SelectedColumnValue}"
/>
<ComboBox
ItemsSource="{Binding Data}"
DisplayMemberPath="Column"
SelectedValuePath="Column"
SelectedValue="{Binding SelectedColumnValue}"
/>
<ComboBox
ItemsSource="{Binding Data}"
DisplayMemberPath="ShoeSize"
SelectedValuePath="Column"
SelectedValue="{Binding SelectedColumnValue}"
/>
Another wrinkle is that DisplayMemberPath and SelectedValuePath needn't be the same. Say we've got a "ShoeSize" column in Data, and we'd like to have those values displayed in another ComboBox. So we can do that. We're still using Column as a unique identifier for rows in Data, but we can tell the ComboBox to display some other column.

Bind to the "value" of ObservableCollection<KeyValuePair<object, string>>

I want to bind my combo box ItemsSourse to the "value" i.e. (string component) of the
ObservableCollection<KeyValuePair<object, string>>.
How can I do that?
You could bind the ItemsSource to the ObservableCollection and then set the DisplayMemberPath to Value:
<ComboBox ItemsSource="{Binding YourCollection}" DisplayMemberPath="Value" />
The values in the combo box will then match the Values from the KeyValuePairs.
The easiest way to go would be to use the DisplayMemberPath property:
<ComboBox ItemsSource="{Binding Pairs}" DisplayMemberPath="Value" />
Alternatively, you could expose a new property in your viewmodel that will only contain the values. For example:
public ObservableCollection<string> AllValues { get; set; }
public ViewModel()
{
AllValues = new ObservableCollection<string>(Pairs.Select(x => x.Value));
}
<ComboBox ItemsSource="{Binding AllValues}" />

Categories