wpf binding by selected value - swap out bound object without disturbing binding - c#

I've got combo box bound to a custom collection type - its basically an overridden ObservableCollection which I've added a facility to update the underlying collection (via Unity).
I don't want to confuse the issue too much, but thats the background.
My xaml looks like this
<ComboBox ItemsSource="{Binding Manufacturers}" DisplayMemberPath="Name" SelectedValuePath="ID" SelectedValue="{Binding Vehicle.ManufacturerID}" />
And in my overridden collection i was doing this.
var index = IndexOf(oldItem);
this[index] = (T)newItem;
I had hoped because it was bound by value, that inserting the new object(which had the same id) over the old object would work. But it seems that although its bound by SelectedValue it still knows that its being swapped for a different one. The combo just looses its selection.
Can anyone help please?

I assume that your oldItem was the SelectedValue in this case?
When you remove this item, your binding gets updated instantly. So removing that value from the list will clear your selection.
If you want to replace the item, you might want to try to get the SelectedValue, save it in a variable, and set the SelectedValue later when you have replaced your item, like so:
object oldValue = SelectedValue;
//Do awesome stuff to replace things
SelectedValue = oldValue;
HTH

Related

WPF Combobox display value doesn't update

I am having a furious struggle with the WPF Combobox in our bi-languge application.
I have the combobox binded to a collection. I have a button that replaces the values of the collection with their corresponding string values in another language.
What is the problem: I select a value from the drop down list. I change the language, by pressing a button, then the displayed value remains on the old language, but when the drop down is dropped the values in in are replaced with the right ones.
Here is my XAML:
<ComboBox x:Name="ProjectClassComboBox"
Width="150"
ItemsSource="{Binding Path=ProjectClassCollection}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
SelectedValue="{Binding Path=RegionContext.CurrentItem.ClassNomenclatureId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Update: Because it is asked in the comments I must add that
the collectionis a custom class of ours, DERIVED from ObservableCollection, that is fairly complex. The collection items must be implementing INotifyPropertyChanged, and the collection has a listener to the PropertyChanged event of each item.
It just looks like the displayed text of the combo is not updated, when the drop down list and the selected item value is updated.
Binding ObservableCollection (and derrived class too) works only in case where you add or delete items, cause that's the action that invokes change event. If you need to manipulate data inside collection I suggest using BindingList. Maybe some kind of wrapper would be solution for you.
Do one thing. In the button click,
1. get the selected index in the combo box
2. Replace all the strings in the collection
3. set the selecteditem property of the combobox using the selected index that we have stored earlier in the step 1.
So, it's a bit late but we just encountered the same problem on a project that needs to support multiple languages.
Our solution was to return a new instance of ObservableCollection on the property getter.
So, your code should look like this:
<ComboBox x:Name="ProjectClassComboBox"
ItemsSource="{Binding Path=ProjectClassCollection}"/>
And in your ViewModel:
public ObservableCollection<Project> ProjectClassCollection
{
get {return new ObservableCollection<Project>(){_projectClassCollection};}
set {...}
}
This code is a quick snippet from my memory. It will not work if you just copy-pasta, but the idea is that another collection instance worked for us.
We tried to call OnPropertyChanged(nameof(ProjectClassCollection)) but that didn't work. We tried to set UpdateSourceTrigger=PropertyChanged in XAML but that didn't work either. Having a new instance worked.
Hope this helps and saves you time, cheers!

Databinding to a Combobox, two List<object>, selected value should be copied AND shown

The Problem:
Combobox shows no selected item on startup. To be exact: Item and Value are null, index is -1 and valuepath is empty. While the functionality of the binding itself seems to work, it simply refuses to show a value on startup.
What I am trying to do:
I have two lists of objects. ListOne is the full list of all available objects.
ListTwo is a list of 21 "selected" objects out of the objects in list one.
So I want to bind ListOne to a combobox with the namevalue of the object to show. Meanwhile, the selected object should be bound to ListTwo[index].
Basically, if I change the selection in the combobox I want ListTwo[index] to be a copy of the now selected object.
What I have right now:
The Object in question:
public class Rezept
{
public string strName { set; get; }
public string strSomething { set; get; }
}
The XAML-Code:
<ComboBox x:Name="comboboxName" ItemsSource="{Binding listOne}" SelectedValue="{Binding listTwo[0], Mode=TwoWay}" DisplayMemberPath="strName"/>
This seems to be working, as in selecting something updates ListTwo just fine. But on startup the combobox has nothing selected.
Another XAML-Code try:
<ComboBox x:Name="comboboxName" ItemsSource="{Binding listOne}" SelectedItem="{Binding listTwo[0], Mode=TwoWay}" DisplayMemberPath="strName"/>
This seems to behave exactly as the first, even tho it sounds more "correct".
Try three on the XAML:
<ComboBox x:Name="comboboxName" ItemsSource="{Binding listOne}" SelectedItem="{Binding listTwo[0], Mode=TwoWay}" DisplayMemberPath="strName" SelectedValue="{Binding listTwo[0].strName}"/>
This changes nothing. Neither does binding to SelectedValuePath it seems.
What do i have to change so the combobox actually shows ListTwo[0] on startup? I did make sure ListTwo[0] actually has the right value. It does get populated by a xml file on startup tho, after the UI is loaded. Would this be a case of needing a "OnPropertyChanged"-Event somewhere? And if so, where? I am out of ideas otherwise.
Could still use a hand here tho :-(
Instead of binding with SelectedValue property, you should have bind it with SelectedItem property like as follows:
<ComboBox x:Name="comboboxName" ItemsSource="{Binding listOne}" SelectedItem="{Binding listTwo[0], Mode=TwoWay}" DisplayMemberPath="strName"/>
So, since noone seems to have a solution (including myself after another 5 days of searching) I will at least post my VERY dirty workaround.
I made a List where I added every combobox in question via a loop (21 ones). Then I did another loop and manually set the Index of listOne in every Combobox to match the right value in listTwo. So it simply checks which index contains the right name and set it to that like this:
for(int i = 0, i < 21, i++)
{
listCombo[i].SelectedIndex = listOne.IndexOf(listOne.Where(x => x.name == listTwo[i].name).First();
}
This only has to be called once one on startup of the programm, since listTwo actually has the right values in the right order (as seen in the XML save file as well).
Id still much rather find out how this is supposed to work in XAML Databinding tho. So if anyone has a real answer later on, feel free to poke me so i can update this one.

WPF ComboBox to ObservableCollection binding

I know there is a few topics on this problem but haven't find any solutions to this problem I have..
I have a ViewModel with an observable collection and I want to bind this collection to a combo box. However, there is no selected item, no index, just the collection itself.
in the XAML I have
ComboBox ItemsSource="{Binding OSCollection}" DisplayMemberPath="OSCollection.Name"
I believe the trouble lies with the bold above, I want to get a property from the collection called name, but like I say - no item will be selected before the bind.
I could use a foreach or something to extract the properties from the collection but I don't think this is the MVVM and WPF way.
Any help would be grateful.
Thanks
DisplayMemberPath specifies the path to the display property.So it should be Name not OSCollection.Name
ComboBox ItemsSource="{Binding OSCollection}" DisplayMemberPath="Name"
In addition to Sajeetharans comment:
When binding to a List of Type T, DisplayMemberPath will always refer to the Name of a Property of T. In your case it is only "Name"

Filter combobox itemsource to display active values only plus the currently selected value

I have a custom combobox control used throughout an application. I want the ItemsSource to display active items only and the currently selectedvalue even if it is not active. I actually have it all working, but it is only working if there is a converter contained the SelectedValue's XAML. Both properties are implementing INotifyPropertyChanged.
My filtering looks like this in the code behind (which is being fired by the Target_Updated and SelectionChanged events):
if (combobox.SelectedValue != null)
base.ItemsSource = combobox.ItemsSource.Cast<IEntity2>().Where(x => (bool)x.Fields["IsActive"].CurrentValue == true || x.Fields[combobox.SelectedValuePath].CurrentValue.ToString() == combobox.SelectedValue.ToString()).ToList().AsEnumerable();
else
base.ItemsSource = combobox.ItemsSource.Cast<IEntity2>().Where(x => (bool)x.Fields["IsActive"].CurrentValue == true).ToList().AsEnumerable();
Two XAML samples SelectedValue bindings: The first one works, the second does not.
SelectedValue="{Binding MyVm.ObjectId, Converter={StaticResource IntToShortConverter}}"
SelectedValue="{Binding MyVm.AnotherObjectId}"
Something else is happening with the binding using the converter that is causing all of this to work. Is there another property or event I can use on the combobox to pick this up?
Turns out it wasn't the converter. In the second line of sample XAML, the ItemsSource was defined first or to the left of the SelectedValue binding. The top sample XAML had the ItemsSource defined second or to the right.
After modifying the order by defining the SelectedValue before the ItemsSource "left-to-right" it works flawlessly. Not the optimal answer, since this means have to keep this in mind when writing the XAML...

Updating the DisplayMember of a ListBox

This question deals with a dinky little Winforms GUI. Let it be known that I have basic knowledge of data bindings and INotifyPropertyChanged and use both in my ViewModels in WPF. But I don't know Winforms. This is for a school assignment.
So I have a class that has a DisplayName property. I also have a ListBox whose Items are a sequence of instances of my class. I have pointed myListBox.DisplayMember = "DisplayName"; After changing a value in an instance of my class that will cause the DisplayName property to return a different value, how do I tell my ListBox to pull the DisplayName property again to refresh its value?
I needed to do the same thing but with a combobox. The workaround I found is to clear and reset the DisplayMember property.
This worked:
myComboBox.DisplayMember = null;
myComboBox.DisplayMember = "DisplayName";
It's important to note that this is perhaps not the best solution as it will cause multiple SelectedValueChanged events but the end result is successful.
Doing it this way probably requires re-binding the listbox, loosing selectedIndex etc.
One workaround is to forget about the DisplayMember property and handle the Format event of the ListBox instead. Something like (from memory) :
// untested
e.Value = (e.Item as MyClass).DisplayValue;
I know this was ages ago but I had similar problem and could not find satisfying solution and finally solved with this single line at the end after updating the values:
bindingsource.EndEdit();
Items on listbox reflects any changes entered into textboxes after Update button clicked. So after lines like this:
textbox1.DataBindings["Text"].WriteValue();
textbox2.DataBindings["Text"].WriteValue();
just insert this line:
bindingsourcevariable.EndEdit();
Hope this helps others who also encounter similar problem but haven't found the right solution
Here is solution code that does everything in XAML as opposed to back end C#. This is how I do my projects utilizing MVVM (minimizing the back end code, and if possible having no back end code)
<ListBox x:Name="lstServers" HorizontalAlignment="Left" Height="285" Margin="20,37,0,0" VerticalAlignment="Top" Width="215"
ItemsSource="{Binding Settings.Servers}"
SelectedItem="{Binding Settings.ManageSelectedServer, Mode=TwoWay}"
DisplayMemberPath="UserFriendlyName"/>
This is a listbox on the Window. The keys to point out here, which can be very tricky, are the usual ItemsSource property being set to a Settings object on my view model, which has a Servers Observable collection.
Servers is a class that has a property called UserFriendlyName.
public sealed class AutoSyncServer : ObservableModel
{
public AutoSyncServer()
{
Port = "80";
UserFriendlyName = "AutoSync Server";
Server = "localhost";
}
private string _userFriendlyName;
public string UserFriendlyName
{
get { return _userFriendlyName;}
set
{
_userFriendlyName = value;
OnPropertyChanged("UserFriendlyName");
}
}
This is a partial code snippet for you of the class itself.
The SelectedItem of the ListBox is bound to an instance of the Selected object that I store in the model view called ManageSelectedServer.
The tricky part here is the DisplayMemberPath is set to "UserFriendlyName" as opposed to "{Binding UserFriendlyName}". This is key
If you use {Binding UserFriendlyName} it will display the UserFriendlyNames in the collection but will not reflect any changes to that property.
The XAML for the TextBox where the user can update the user friendly name (which should change the text in the listbox also) is:
<TextBox x:Name="txtDisplayName" HorizontalAlignment="Left" Height="23" Margin="395,40,0,0" TextWrapping="Wrap"
Text="{Binding ElementName=lstServers,Path=SelectedItem.UserFriendlyName, UpdateSourceTrigger=PropertyChanged}"
VerticalAlignment="Top" Width="240"/>
This sets the Text property of the TextBox and binds it to the ListBox element lstServers SelectedItem property UserFriendlyName. I've also included an UpdateSourceTrigger=PropertyChanged so that any changes made to the text source notify that they have been changed.
XAML is tricky!

Categories