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!
Related
I've got a simple WPF ComboBox, displaying Orders/Positions on the Financial Markets.
<ComboBox Name="TradeDropDown"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
ItemsSource="{Binding Path=ActiveOrders}"
DisplayMemberPath="OrderLabel"
SelectedItem="{Binding Path=SelectedOrder, Mode=TwoWay}" IsSynchronizedWithCurrentItem="True" />
I need to see at a glance how many items are in the list. I've added a TextBlock above with summary information.
I don't like it, and would prefer to have the items in the dropdown listed like:
(1/2) Working Short 425K
(2/2) Filled Long 979K
etc - and have the 1/2 numbers correctly update as items are added and removed from the list.
The Items are stored in a BindingList.
Is there an easy way to do this?
Is there an easy way to do this?
Add another property to the class where the OrderLabel property is defined that returns a string like "(1/2) Working Short 425K" and set the DisplayMemberPath property of the ComboBox to the name of this property.
Make sure that the class implements the INotifyPropertyChanged interface.
You then set the new property to a new value and raise the PropertyChanged event whenever you want to update the label in the ComboBox.
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!
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.
In a project I have a very tricky requirement I don't know how to solve:
I have several datagrids in a single wpf window (I use MVVM) all binded to some collection in the linked ViewModel.
The customer wants to edit each of these grids, either within the grid or in a common textbox (like in excel).
I'm banging the head on how to do the latter. What I would do is bind the textbox with a property in the viewmodel, but when the value is changed there, I need to change the value in the original property binded with the datagrid cell accordingly. In other words, I need to know what collection and which property of that collection I need to change with the data in the textbox accordingly .
I tried several ways but with no luck.
Reflection? DependencyProperty? What else?
Any help?
Thank you
Assuming that you're using the built-in WPF DataGrid, you'll need to setup your grid similarly:
<DataGrid SelectionUnit="Cell" SelectionMode="Single" ItemsSource="{Binding Data}" SelectedCellsChanged="DataGrid_OnSelectedCellsChanged">
...
</DataGrid>
Also give your TextBox a name:
<TextBox x:Name="textBox" DockPanel.Dock="Top" />
In the code-behind, you'll need to manually wire up this event, since apparently the DataGrid doesn't allow you to bind to the selected item/cell/value when using SelectionUnit="Cell":
private void DataGrid_OnSelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
{
if (e.AddedCells.Count == 0)
this.textBox.SetBinding(TextBox.TextProperty, (string) null);
else
{
var selectedCell = e.AddedCells.First();
// Assumes your header is the same name as the field it's bound to
var binding = new Binding(selectedCell.Column.Header.ToString())
{
Mode = BindingMode.TwoWay,
Source = selectedCell.Item,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
};
this.textBox.SetBinding(TextBox.TextProperty, binding);
}
}
I tried getting this done without code-behind but after looking around it didn't seem like this was possible.
In addition to tencntraze answer I used this code to get te property bound to the cell
var property = (selectedCell.Column.ClipboardContentBinding as Binding).Path.Path;
I have a textbox which is bound to a property ItemID like so.
private string _itemID;
public string ItemID {
get { return _itemID; }
set {
_itemID = value;
}
}
The XAML of the text box is as follows:
<TextBox Text="{Binding Path=ItemID, Mode=TwoWay}" Name="txtItemID" />
The problem is, the value of ItemID does not update immediately as I type,
causing the Add button to be disabled (command), until I lose focus of the text box by pressing the tab key.
Yes, by default, the property would be updated only on lost focus. This is to improve performance by avoiding the update of bound property on every keystroke. You should use UpdateSourceTrigger=PropertyChanged.
Try this:
<TextBox
Text="{Binding Path=ItemID, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Name="txtItemID" />
You should also implement the INotifyPropertyChanged interface for your ViewModel. Else, if the property is changed in the ViewModel, the UI would not get to know about it. Hence it would not be updated. This might help in implementation.
You need to fire the OnPropertyChanged event in the setter, otherwise the framework has no way of knowing that you edited the property.
Here are some examples:
Implementing NotifyPropertyChanged without magic strings
Notify PropertyChanged after object update