Binding Combobox Name using Id value - c#

I have a combobox control. I am getting combobox values from Database. There I have Id and Name. But I am binding only Name in the combobox. what i want is,
1. If I select any name in the Combobox, I need to save the Corresponding Id in my Database. Now I am able to bind the value from database and Display the Name in Combobox. but when I select any value in combobox and try to save means, it shows null value on the ID. Here is my code. Please help me to find some solution.
Xaml:
<ComboBox x:Name="cb_rentaltype" HorizontalAlignment="Left" Margin="150,5,0,0" VerticalAlignment="Top" Height="35" Width="200"
SelectedValue="{Binding MasterRentalType}"
DisplayMemberPath="RentalTypeName"
SelectedValuePath="RentalTypeId" />
My code Behind:
var status = new MasterRentalType();
List<MasterRentalType> listRentalType =
status.Get<MasterRentalType>() as
List<MasterRentalType>;
cb_rentaltype.ItemsSource = listRentalType;
and i want to bind the Id to the Data context. here is the code,
private void FacilityDataBind()
{
cb_rentaltype.DataContext = ??
}
Note: MasterRentalType is the Table where i get the Values. There I have ID and Name values.
public class MasterRentalType : EntityBase
{
public string RentalTypeId {get; set;}
public string RentalTypeName {get; set;}
}
how can I bind and save the id value?

Your problem is caused because you have data bound the ComboBox.SelectedValue property to your MasterRentalType property, which I assume is of type MasterRentalType, but then you set the SelectedValuePath property to RentalTypeId. So you're saying *make the SelectedValue use the string RentalTypeId property, but then data binding a MasterRentalType to it.
There are a number of solutions. Correcting your example, you should try this:
<ComboBox x:Name="cb_rentaltype" HorizontalAlignment="Left" Margin="150,5,0,0"
SelectedValue="{Binding MasterRentalType.RentalTypeId}"
DisplayMemberPath="RentalTypeName"
SelectedValuePath="RentalTypeId" />
Alternatively, you could have done this:
<ComboBox x:Name="cb_rentaltype" HorizontalAlignment="Left" Margin="150,5,0,0"
SelectedItem="{Binding MasterRentalType}"
DisplayMemberPath="RentalTypeName" />
To find out more about the differences, please take a look at the How to: Use SelectedValue, SelectedValuePath, and SelectedItem page on MSDN.

There are several ways to achieve this. One of them is implemented below:
The xaml should look like this:
<ComboBox x:Name="cb_rentaltype" HorizontalAlignment="Left" Margin="150,5,0,0"
ItemsSource="{Binding MasterRentalTypeColl, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
SelectedItem="{Binding SelectedMasterRentalType, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
DisplayMemberPath="RentalTypeName">
</ComboBox>
The MasterRentalType class:
public class MasterRentalType : EntityBase, INotifyPropertyChanged
{
private string _RentalTypeId;
private string _RentalTypeName;
public string RentalTypeId
{
get { return _RentalTypeId; }
set
{
_RentalTypeId = value;
NotifyPropertyChanged("RentalTypeId");
}
}
public string RentalTypeName
{
get { return _RentalTypeName; }
set
{
_RentalTypeName = value;
NotifyPropertyChanged("RentalTypeName");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
The model class:
public class MasterRentalModel: INotifyPropertyChanged
{
private MasterRentalType _SelectedMasterRentalType;
private List<MasterRentalType> _MasterRentalTypeColl = new List<MasterRentalType>();
public MasterRentalType SelectedMasterRentalType
{
get { return _SelectedMasterRentalType; }
set
{
_SelectedMasterRentalType = value;
NotifyPropertyChanged("SelectedMasterRentalType");
}
}
public List<MasterRentalType> MasterRentalTypeColl
{
get { return _MasterRentalTypeColl; }
set { _MasterRentalTypeColl = value; }
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
And in the code-behind, you just need to assign the model to the DataContext of you ComboBox; you can implement this any other function (here I have implemented it in the event handler of Loaded event of my Window):
private void Window_Loaded(object sender, RoutedEventArgs e)
{
MasterRentalModel masterRentalModel = new MasterRentalModel();
// Fill the list of RentalType here
masterRentalModel.MasterRentalTypeColl.Add(new MasterRentalType() { RentalTypeId = "1", RentalTypeName = "Monthly" });
masterRentalModel.MasterRentalTypeColl.Add(new MasterRentalType() { RentalTypeId = "2", RentalTypeName = "Quarterly" });
masterRentalModel.MasterRentalTypeColl.Add(new MasterRentalType() { RentalTypeId = "1", RentalTypeName = "Yearly" });
cb_rentaltype.DataContext = masterRentalModel;
}
Whenever user changes the selection, SelectedMasterRentalType will be updated. And in the SelectedMasterRentalType, you can get both RentalTypeId and RentalTypeName. If you follow proper binding your model will always be updated; that's the essence of WPF.
Hope this will help.

Related

Binding constant Collection to ComboBox & SelectedItem to TextBox

I'm new to MVVM in WPF and I have the following problem.
What I try to have is two ComboBoxes, each binding to the same ObservableCollection<TwoProperties> DList property as ItemsSource and with synchronized SelectedItem, so I wrote this in my XAML
<ComboBox ItemsSource="{Binding DList}" DisplayMemberPath="Property1" SelectedItem="{Binding SelectedD}" />
<ComboBox ItemsSource="{Binding DList}" DisplayMemberPath="Property2" SelectedItem="{Binding SelectedD}" />
with this viewmodel
public class ViewModel : INotifyPropertyChanged
{
private ObservableCollection<TwoProperties> _dList =
new ObservableCollection<TwoProperties> {
new TwoProperties(1,"one"),
new TwoProperties(2,"two")
};
public ObservableCollection<TwoProperties> DList
{
get { return _dList; }
set { _dList = value; OnPropertyChanged("DList"); }
}
private TwoProperties _selectedD;
public TwoProperties SelectedD
{
get { return _selectedD; }
set { _selectedD = value; OnPropertyChanged("SelectedD"); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
where
public class TwoProperties
{
public double Property1 { get; set; }
public string Property2 { get; set; }
public TwoProperties (double p1, string p2)
{
Property1 = p1;
Property2 = p2;
}
}
I would also like to have two TextBoxes that display the properties of the currently SelectedItem of the synchronized ComboBoxes. The properties Property1 and Property2 of SelectedD should be editable, however the ObservableCollection<TwoProperties> _dList should remain constant/readonly and not change its values.
<TextBox Text="{Binding SelectedD.Property1}" />
<TextBox Text="{Binding SelectedD.Property2}" />
But when I edit the TextBoxes and therefore SelectedD, also _dList changes its values, which is not what I want.
I hope I could explain my problem. I'm sure I'm missing something simple here.
This could be implemented easily by changing the binding mode for the TextBoxes into one way as following:
<TextBox Text="{Binding SelectedD.Property1,Mode=OneWay}" />
<TextBox Text="{Binding SelectedD.Property2,Mode=OneWay}" />
Thus when you change the textBox value, the changes would not be reflected back to the Observable collection objects.
Note that you can get rid of magical strings in your view model OnPropertyChanged by modifying the method as following:
private void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
And then you can call it inside the setter of any property inside the view model without passing the name of the property as following:
private TwoProperties _selectedD;
public TwoProperties SelectedD
{
get { return _selectedD; }
set { _selectedD = value; OnPropertyChanged(); }
}
Edit 2:
Update my binding and view model to get the edited values inside the view model
View model updates:
private double? editPropertyOne;
public double? EditPropertyOne
{
get { return editPropertyOne; }
set
{
editPropertyOne = value;
OnPropertyChanged();
}
}
private string editPropertyTwo;
public string EditPropertyTwo
{
get { return editPropertyTwo; }
set
{
editPropertyTwo = value;
OnPropertyChanged();
}
}
private TwoProperties _selectedD;
public TwoProperties SelectedD
{
get { return _selectedD; }
set
{
_selectedD = value; OnPropertyChanged();
if (_selectedD != null)
{
EditPropertyOne = _selectedD.Property1;
EditPropertyTwo = _selectedD.Property2;
}
}
}
Xaml changes:
<TextBox Text="{Binding EditPropertyOne}" />
<TextBox Text="{Binding EditPropertyTwo}" />

Xamarin.Forms Picker`s ItemDisplayBinding doesn't change displayed text when property on object changes

I have a xaml code:
<Picker ItemsSource="{Binding profiles}" ItemDisplayBinding="{Binding name}" SelectedItem="{Binding selectedProfile}" HorizontalOptions="FillAndExpand" Margin="0, 0, 20, 0" VerticalOptions="Center"/>
In cs file I defined:
BindingContext = this;
and
private ObservableCollection<Profile> _profiles = new ObservableCollection<Profile>();
public ObservableCollection<Profile> profiles
{
get { return _profiles; }
set { _profiles = value; }
}
And profile class is:
public class Profile
{
private string _name = "New profile";
public string name
{
get { return _name; }
set {
_name = value;
}
}
}
It works properly when I add/remove elements, and select new one in dropdown list or code (selectedProfile = profiles[index]).
But the problem occurs when I trying to rename profile. I changed profile name, but Picker didn`t update it and I see the old value.
I also tried this. But now result.
public class Profile : INotifyPropertyChanged
{
private string _name = "New profile";
public string name
{
get { return _name; }
set {
_name = value;
OnPropertyChanged("name");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
So, you want to update a information inside a item on a list. Here is my answer to how you can do it:
How to update informations inside a item on a list
You will probably adapt for your need, but I guess that it will help to guide you.
'ItemDisplayBinding' isn't bindable property, that's why I think u can't notify it.
You will have to create a custom picker.
I have faced this problem several times.
Here is a light solution to update a picker linked to a source of type ObservableCollection<T>:
var l_nIndex = Items.IndexOf(SelectedItem);
Items.Move(l_nIndex, l_nIndex);
RaisePropertyChanged(nameof(SelectedItem));
Items property is a collection of Item it is an ObservableCollection<Item>
SelectedItem is the binded property that represents the selected Item in the picker
Here is the xaml code that creates the picker in the view :
<Picker ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" />

C# BindingList<> not updating a WPF Listbox on changed items

I am on a MVVM C# project.
I want to display a list of objects.
I want to add and remove items in this list and ALSO change items in this list.
So I choosed the BindingList<> over the ObservableCollection<>, which would not get noticed if an item has changed.
(I also tested the ObservableCollectionEx which is out there in the web, but this has the same behavior like the BindingList for me).
But the Listbox is not changing when items are changed.
(Adding and removing items is updated in the Listbox)
In my XAML
<ListBox DisplayMemberPath="NameIndex" ItemsSource="{Binding Profiles}" SelectedItem="{Binding SelectedProfile}">
or alternative with the ItemTemplate
<ListBox DockPanel.Dock="Right" ItemsSource="{Binding Profiles}" SelectedItem="{Binding SelectedProfile}" Margin="0,10,0,0">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding NameIndex}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In my ViewModel (ViewModelBase is implementing INotifyPropertyChanged etc)
public class ProfileListViewModel : ViewModelBase
{
private BindingList<Profile> profiles;
public BindingList<Profile> Profiles
{
get
{
return profiles;
}
set
{
profiles = value;
RaisePropertyChanged();
}
}
My items are also implementing INotifyPropertyChanged and I am calling OnPropertyChanged("Name") in my Setters.
My model
public class Profile : INotifyPropertyChanged
{
public Profile(){}
public int ProfileID { get; set; }
private string name;
public string Name
{
get
{
return name;
}
set
{
name = value;
OnPropertyChanged("Name");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Wiring the View with the ViewModel (BindingList is initialized before View)
ProfileListViewModel plvw= new ProfileListViewModel(message.Content);
var profileView = new ProfileListView(plvw);
profileView.ShowDialog();
In the View.xaml.cs
public ProfileListView(ProfileListViewModel plvw)
{
InitializeComponent();
DataContext = plvw;
}
When I am changing the name of an object then I get the ListChanged event to which I have subscribted in my ViewModel (Profiles.ListChanged += Profiles_ListChanged;) for testing BUT the items in the ListBox are NOT changing.
What am I doing wrong?
How can I get a updated Listbox?
Since your DisplayIndex is the computed property NameIndex, you need to call OnPropertyChanged("NameIndex") when its value changes due to a change in other properties, e.g.:
public string Name
{
get
{
return name;
}
set
{
name = value;
OnPropertyChanged("Name");
OnPropertyChanged("NameIndex");
}
}
Use
Profiles.ResetBindings() to bind it again.

WPF TwoWay binding in multiple UserControl

I have multiple UserControl which contain a shared ViewModel.
It's a DataGrid where the user click on a row to see the detail of the row (the actual structure is more complex).
The problem is when I handle the SelectionChanged in the grid, I update the shared ViewModel to update the ContactDetail but it doesn't update the value in the TextBoxes (the object is updated in ContactDetail but values are not displayed).
ListContact.xaml.cs
public void contactsTable_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
contacts.current_identity = //Get the associated `IdentityViewModel`
}
ContactDetail.xaml.cs
public partial class ContactDetail : UserControl
{
public ContactsViewModel contacts;
public DetailContact(ContactsViewModel contacts)
{
InitializeComponent();
this.contacts = contacts;
this.DataContext = contacts;
}
}
ContactDetail.xaml
<UserControl x:Class="ContactDetail">
<TextBox Name='address' Text="{Binding Path=contacts.current_identity.address, Mode=TwoWay}"/>
<TextBox Name='phone' Text="{Binding Path=contacts.current_identity.phone, Mode=TwoWay}"/>
<TextBox Name='email' Text="{Binding Path=contacts.current_identity.email, Mode=TwoWay}"/>
</UserControl>
ContactsViewModel.cs (IdentityViewModel uses the same structure)
public class ContactsViewModel : INotifyPropertyChanged
{
private List<Contact> _contacts;
public List<Contact> contacts;
{
get { return _contacts; }
set { _contacts = value; OnPropertyChanged("contacts"); }
}
private IdentityViewModel _current_identity;
public IdentityViewModel current_identity
{
get { return _current_identity; }
set { _current_identity = value; OnPropertyChanged("current_identity"); }
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
The question is, why doesn't this work and how to notify ContactDetail so that it displays the new value ?
Your data for contacts changes but the original reference location Binding Path=contacts.current_identity.address is still being referred to in the binding. I.E. address is still valid and has not changed. What changed was contacts.current but you are not binding to that.
Remember that binding is simply reflection to a location reference. If the original address changes you would see a change because that is what is being looked for to have a change. But instead the parent instance is what changed.
You need to refactor your bindings to allow for proper update when the current_identity changes.

Bind collection to combobox

I have this combobox
<ComboBox Height="30" SelectedIndex="0" Margin="5 3 5 3" Width="170" ItemsSource="{Binding WonderList}" SelectedValuePath="selectedWonder">
<ComboBox.ItemTemplate>
<DataTemplate>
<WrapPanel>
<Image Source="{Binding Path}" Height="20"></Image>
<Label Content="{Binding Name}" Style="{StaticResource LabelComboItem}"></Label>
</WrapPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
where I want to show items as an image plus a text.
This is the business class for the objects in the item list
public class Wonder: INotifyPropertyChanged
{
private string name;
private string path;
public event PropertyChangedEventHandler PropertyChanged;
#region properties, getters and setters
public String Name { get; set; }
public String Path { get; set; }
#endregion
public Wonder(string name, string path)
{
this.name = name;
this.path = path;
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
and the code behind of the window
public class Window1 {
public List<Wonder> WonderList;
public Window1()
{
InitializeComponent();
WonderList = new List<Wonder>();
WonderList.Add(new Wonder("Alexandria", "Resources/Images/Scans/Wonders/Alexandria.jpg"));
WonderList.Add(new Wonder("Babylon", "Resources/Images/Scans/Wonders/Babylon.jpg"));
}
}
I´m pretty new to this xaml "magic" and guess I dont understand correctly how the data binding works, I think that with ItemsSource="{Binding WonderList}" it should take the collection with that name (from the code behind) and show their Name and Path, but it shows an empty list.
If I do Combo1.ItemsSource = WonderList; in the code behind (I prefer to use the xaml and avoid the code behind), it shows two blank slots but still don´t know how to show the items.
Can you point me in the right direction?
Thanks
If you want to bind like this ItemsSource="{Binding WonderList}" you have to set the DataContext first.
public Window1()
{
...
this.DataContext = this;
}
Then Binding will find the WonderList in Window1 but only if it is a property too.
public List<Wonder> WonderList { get; private set; }
Next: It is useless to bind to property Name if you assign your value to private field name. Replace your constructor with
public Wonder(string name, string path)
{
this.Name = name;
this.Path = path;
}
Next: Your auto properties ({ get; set; }) will not notify for changes. For this you have to call OnPropertyChanged in setter. e.g.
public String Name
{
get { return name; }
set
{
if (name == value) return;
name = value;
OnPropertyChanged("Name");
}
}
Same thing for WonderList. If you create the List to late in constructor it could be all bindings are already resolved and you see nothing.
And finally use ObservableCollection if you want to notify not for a new list but a new added item in your list.
You are not doing the correct way. Simply saying, you should have a Wonders class holding an ObservableCollection property, which is bound to ComboBox's ItemsSource. You should read MSDN:
http://msdn.microsoft.com/en-us/library/ms752347.aspx

Categories

Resources