I have combobox that is bound to a collection. Each item in the collection has a subcollection that might or might not contain items. I have a second combobox to which the subcollection is bound. And I only want to display (=Visibility.Visible) the second combobox if the subcollection has items. All that works. BUT: When I start my program, the selectedItem of the main combobox is null (=so far nothing has been selected). This (I guess) causes the converter to not be called (it doesn't, I have set a breakpoint on the converter and the program does not halt when it starts).
So I have the problem that I don't want to display the second combobox when the program starts. Can I force the ValueConverter to be called? I have tried invoking OnPropertyChanged at the end of the constructor, but that doesn't change anything. And when I set the combobox to Visible.Hidden in the constructor of the .xaml.cs, it will not show at all, even after the value converter is called. Any ideas?
Set a FallbackValue for the binding:
<SomeUIElement Visibility="{Binding SomeBinding, Converter={StaticResource SomeConverter}, FallbackValue=Hidden}" />
When there is nothing bound you can set a value that will be used in place of the binding.
The reason changing the value in code-behind to Visibility.Hidden leaves it invisible all the time is because setting the property explicitly removes the binding that you have added in the XAML
Related
I have a ComboBox with IsEditable set to true. If I type in a custom option and then reset the list of choices (or even remove all choices), the displayed text does not change. Which is exactly what I want. However if I select or type in an option that is on the list and then later remove that option from the list, the selected text gets reset to null or empty string. In this second instance I'd like the selected text to not change (essentially turn into a custom entry).
I tried using the SourceUpdated and setting Handled to true but the event doesn't trigger whether I try replacing it with a new ObservableCollection or calling Clear.
Additionally manually managing the value is problematic because of delayed events triggering on the control itself in response to the change. If I use the following code the control responds to the updated list (and resets the value) after everything has finished executing.
string holdValue = SelectedListString;
ListOptions.Clear();
SelectedListString = holdValue;
This is a "nice to have" so I'm hoping for a simple solution and can't use 3rd party libraries for it. Worst case I guess I can do some hacky change tracking that then prevents the value from being modified when the setter is called on the dependency property SelectedListString, but I was hoping for something simpler.
You can bind your 'SelectedListString' property with 'Text' property of the 'ComboBox' like this
<ComboBox IsEditable="True"
ItemsSource="{Binding ListOptions}"
SelectedItem="{Binding SelectedListString}"
Text="{Binding SelectedListString}"/>
And when you clear the list, you hold on the value manually like you did
string holdValue = SelectedListString;
ListOptions.Clear();
SelectedListString = holdValue;
I have a wpf user interface in which I use ComboBox to set a property to a value. The ItemsSource of the ComboBox has a Binding to a list of allowed values. I have made it so that this list is the list of allowed values but with the value currently selected removed from it. This way the list only contains values that one can actually change to. When the list is empty, I have a trigger that makes the combobox inactive.
This is all working well in terms of expected behaviour. However visually I have multiple red borders showing validation errors due to the fact the selectedvalue is no longer in the itemssource list upon update. There is no red border when I do not remove the currently SelectedValue from ItemsSource upon update.
Is there a way to get around this, maybe another Control I could use, or maybe somehow validate the SelectedValue from another list (that would contain the current value) compared to the one in ItemsSource ?
You can't actually select a value that is not in the ItemsSource. What you can do is to remove the red border that shows up when there is a validation error by setting the Validation.ErrorTemplate attached property to an empty ControlTemplate:
<ComboBox ...>
<Validation.ErrorTemplate>
<ControlTemplate/>
</Validation.ErrorTemplate>
</ComboBox>
So suppose there is an enum
enum SampleEnum
{
Item1,
Item2
}
Then there is a ComboBox
<ComboBox ItemsSource="{Binding SomeItemSource}"
SelectedItem="{Binding
Path=ItemX,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
Converter={StaticResource ResourceKey=SomeConverter}}">
Combo box has a ViewModel as its DataContext
ViewModel : INotifyPropertyChanged, ...
{
...
public SampleEnum ItemX
{
get => model.GetItemX();
set
{
model.SetItemX(value);
RaisePropertyChanged();
}
}
...
}
And RaisePropertyChanged([CallerMemberName] string caller = "") invokes PropertyChanged with property's name.
But.
When I run my code, open my CheckBox, select one item, I get following behaviour: my code enters setter, sets value of the model, raises PropertyChanged, then my getter is invoked, value is retrieved, but it never reaches ComboBox. ComboBox displays the value I chose by hand, not the value returned by accessor.
E.g. if you rewrite get => SampleEnum.Item2 to return always the same value, ComboBox will still display the value I picked by my hand in UI, not the one that is returned by accessor, even though I am 100% sure that getter is invoked, than the value travels to Converter and Convrter also returns proper value.
But if RaisePropertyChanged(nameof(ItemX)) is invoked from any other place, ComboBox immediately retrieves the value from accessor and displays it.
In short, ComboBox ignores PropertyChanged if invoked from setter, but in any other case it works perfectly fine. Specifying directly the name of property (instead of relying on compiler services) or invoking several RasiePropertyChanged in a row in setter does not work.
In general, one should expect that the value selected in combo box and the one returned by getter are the same, but sometimes model can reject provided value and instead return a default one. Not the best behaviour, but it is possible. And in this case the user will be misinformed which item of a ComboBox is actually selected.
I am just wondering what is so special about this accessor that ComboBox ignores it.
tl;dr: What you're trying to do is data validation. That's a solved problem: You can implement validation in your viewmodel with IDataErrorInfo, or in your view with ValidationRules. Either one of those works with WPF instead of against it. Working against WPF is almost invariably losing proposition.
You could also write an ItemContainerStyle for the ComboBox that disables invalid items, or your viewmodel could update the ComboBox item collection to exclude any items that are currently unselectable. I prefer that approach: Rather than "here, you can choose any of these options -- BZZZT, LOL, WRONG CHOICE!", it seems friendlier to present them only with the options that they can choose.
And if you'll know which options are valid after they make their choice, you can almost certainly know beforehand as well.
ComboBox ignores PropertyChanged if invoked from setter, but in any other case it works perfectly fine.
That's correct. The ComboBox is still handling the change it got from the user, and won't be finished doing so until some time after your setter is complete. The ComboBox will ignore any PropertyChanged events on a property it's currently updating. This is by design.
The standard workaround is to call BeginInvoke() with ApplicationIdle priority, and raise PropertyChanged in the delegate. That will have the effect of raising PropertyChanged again after the ComboBox is entirely finished with the current selection-change event. But that's nothing a viewmodel should ever be concerning itself with.
As you say, "Not the best behaviour". It would be preferable to write validation which rejects the wrong value in the first place, rather than writing a strange workaround like the above.
I have a ComboBox that has an ItemSource bound to a an ObservableCollection and its SelectedItem property is bound to a property of T. Both properties live on my view model. Everything works great except for one issue that is causing me to tear my hair out. When I set a property on the object bound to the SelectedItem, the ComboBox binding stops working. VS 2012 output window does not report any binding errors and no exceptions are thrown.
XAML
<ComboBox ItemSource="{Binding Path=Connections, Mode=OneWay}"
SelectedItem="{Binding Path=SelectedConnection, Mode=TwoWay}"
DisplayMemberPath="Name"/>
<TextBlock Text="{Binding Path=ConnectionName, Mode=TwoWay}" />
<TextBlock Text="{Binding Path=ConnectionGroup, Mode=TwoWay}" />
View Model
private void SaveChanges()
{
this.SelectedConnection.Name = this.ConnectionName;
this.SelectedConnection.Group = this.ConnectionGroup;
// -- Save the changes
}
Now all of that works just fine. But as soon as SaveChanges() completes the ComboBox starts acting funny. It looks like everything is fine, but if you attempt to change the selection nothing happens; the same item is selected and the setter for "SelectedConnection" does not fire. I tried systematically commenting out code in SaveChanges() and if I leave only a single line that is setting any property on SelectedConnection I see this failure happen.
I also added an event handler in the code behind of the control to handle the SelectionChanged event on the ComboBox. After the break happens, any time I try to change the value via the UI or in Code the event handler fires twice in immediate succession. The first has the event args' AddedItems set with a single value which is the value I just selected and would expect. However the event fires again immediately with only a single item in the RemovedItems collection of the event args. That collection also contains the item I just selected.
I have tried setting the SelectedConnection property to null and then after the method finishes, setting back to the object in question. No luck.
I also saw an old bug post about moving the ItemSource property after the SelectedItem property on my XAML mark up but that had no effect either.
I'm trying to avoid creating a shadow list and trying to keep everything in sync as that was going to be my next try but that is just getting ugly to do something so simple. Any ideas would be greatly appreciated.
If it matters, this is Visual Studio 2012 building to the 4.5 framework.
EDIT
I've now also tried just completely removing the modified object from the ObservableCollection after setting its properties. It still shows as if it was the selected value (its name is immediately visible in the ComboBox) but it no longer shows up in the drop down list. It also will still not allow itself to be "unselected" even though it doesn't exist in ItemSource any more.
** UPDATE **
I changed the method in question to completely null out the ObservableCollection that is bound to the ItemSource and then restore it once the process is over with like so:
private void SaveChanges()
{
var selected = this.SelectedConnection;
var name = this.ConnectionName; // -- bound to fields on the view
var group = this.ConnectionGroup;
this.Connections = null;
this.RaisePropertyChanged("Connections"); // -- SelectionChanged fires as expected
selected.Name = name;
selected.Group = group;
this._repository.SaveChanges();
this.Connections = new ObservableCollection<Connection>(this._repository.AllConnections);
this.RaisePropertyChanged("Connections"); // -- SelectionChanged event doesn't fire
this.SelectedConnection = selected; // -- SelectionChanged event doesn't fire
}
After that is done the ComboBox once again allows me to change the value as you would expect. However the selected item does not actually appear as selected (even though it is correctly set in the View Model). So yeah, it's a really ugly workaround and it creates its own issue as well.
Have just had this same issue, and I think I tracked down what was doing on.
It only appeared after I made some recent changes (suggested by the code analysis) to add equals/hashcode/operator overloads for a class that I had as IComparable, and was used in an ObservableCollection for a combobox. I made the hashcode based on a descriptive field of the object. However that field could be user-modified, and after the user modified the value, the combobox broke, being unable to unselect that item anymore.
Given that there are no unchanging fields in this object, there's no way to create a fixed hash code based on the object's properties. However if I remove the hashcode override entirely (or set it to something fixed, like type.GetHashCode), everything works again.
So, check to see if your hashcode is based on whatever value you were changing.
I gave up on doing it the way I had originally envisioned. Instead of directly modifying the object in the ItemSource collection, I made the ItemSource collection to be a list of Key/Value pairs and extracted the object I needed from the source list based on the key of the selected pair. I was able to edit that object without issue (as expected). It works now, I just added a layer of code in there that I was hoping to avoid. The UI also allowed me to update the Value side of the pair to reflect changes to the underlying model's "Name" field without giving me the same issues it was previously. I'll just count it as a small win and move on.
I have a ListView of items, and I want to run some code every time the user selects or deselects an item without resorting to event handlers in the control's codebehind - everything is being done in the view the control has its datacontext set to.
When the ListView's selection mode is "Single" I can simply bind "SelectedItem" to a property in my view, and watch for when that property's change event. If selection mode is "Multiple" however, the behaviour is completely unreliable. Sometimes the last item clicked changes the SelectedItem, and sometimes it doesn't. This DependencyProperty appears to be complete trash when the selection mode isn't single. How else can I use a binding to track changes to the ListView's SelectedItems collection?
Note that I don't use Expression blend so I won't be using Interaction.Triggers or similar library solutions.
<ListView ItemsSource="{Binding Path=Zones}"
SelectionMode="Multiple"
SelectedItem="{Binding SelectedZone}">
Don't see any other way then described in this good article.
The thing is, that unfrotunately, SelectedItems property is readonly, so can not be databinded.
Till now it's kind of tricky story, unfortunately.
The only solution was to wrap the ItemsTemplate for the list in a control that can be toggled and has a command binding (like a button) and then bind the viewmodel to that command binding. It's a huge pain and requires hacking around with the HitTestVisibility and binding the button's state to the item's selected state, but it works in the end.