Caliburn Micro WPF: Message.Attach with guard property disables whole host control - c#

I'm using Caliburn.Micro (2.0.2, currently the latest version) with a WPF application. Say I have a simple list of items, and I want to enable some action on the selected item. The action is bound to a button.
So, my VM exposes a bindable collection of those items (e.g. Persons), and an object of the same type bound to the selected item (e.g. SelectedPerson). Say the action corresponds to a method named Test, and I have a guard property CanTest, which returns true when the selected item is not null. When SelectedPerson changes, I also call NotifyOfPropertyChanged for CanTest.
This trivial scenario works fine: when nothing is selected the button corresponding to my action is disabled; when I select any item, it gets enabled.
The issue arises when I bind the method to a control event, using Message.Attach: say I want to attach it to mouse double click. My XAML would look like this:
<ListBox ItemsSource="{Binding Path=Persons,Mode=OneWay}"
SelectedItem="{Binding Path=SelectedPerson,Mode=TwoWay}"
cal:Message.Attach="[Event MouseDoubleClick] = [Action Test]"/>
This totally disables the control (the whole control, not only the descendant button, so that I can never select an item in the ListBox, which stays disabled forever). The only way I have to enable it is to remove the guard property from my VM (or the Message.Attach from the view).
You can find a simple repro at http://1drv.ms/1OTSgq2.
I also found this post at https://caliburnmicro.codeplex.com/discussions/246571, which seems to suggest that attaching a message disables the convention-based logic for guard properties, so that you have to explicitly bind the IsEnabled property of the control to your guard property. I tried this, but nothing changes.

Because of Caliburn's conventions, in your case I would suggest to merely change the property name of CanTest to something like IsTestEnabled.

Related

WPF ComboBox (with IsEditable) - Prevent selected item from being deleted when removed from the list of choices

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;

Bind button enabled property to combobox selected item

A while back i had read a tutorial on data binding (maybe it was MVVM?) in Windows Forms. I've sense forgot everything and forgot the name of the tutorial.
What i would like to do is bind the enabled property of a button to a combox's selected item.
Logic: If combobox has selected item- enable button.
else disable button.
I'm aware of the combobox_textchanged and combobox_selecteditemchanged event and i would like to avoid using it if possible.
In WPF / MVVM, this is a UI issue that can be handled in a ViewModel class. In Windows Forms, you might also want to create a ViewModel class separate from your model class to keep UI concerns away from the Model classes. Either way, you can create a Boolean property like "IsActiveCustomer", or whatever it is in your case, in the object class you are binding to. Your property could either have a getter that returns a value based on the property that is bound to the combo box -- or you could use the combo box selected index changed or selected value changed events and set the Boolean property accordingly. Then, of course, bind the Enabled property of the button to the Boolean property. Probably need to know what you are data binding to in order to provide specifics (binding to object vs. BindingSource/table adapter, etc.)

ComboBox Binding Breaks on Changes to object in SelectedItem

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.

Monitor ListView selection changes using a binding

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.

WPF Databinding Magic

I have ObservableCollection<Foo> that is bound to an ItemsControl (basically displaying a list).
Foo mostly looks like this (there are other members but it doesn't implement any interfaces or events):
class Foo
{
public string Name { get; set; }
//...
}
When the user clicks on an item I open a dialog where the user can edit Foo's properties (bound to a small viewmodel with a Foo property for the selected item), the Xaml looks like this:
<TextBox Text="{Binding Foo.Name,Mode=TwoWay}"
Grid.Column="1" Grid.Row="0" Margin="2" />
The really strange thing is, when the user edits the name, the value in the list changes! (not while typing but after the focus leaves the field)
How does it do that? I haven't implemented the INotifyPropertyChanged interface on the Foo object!
So far I checked that it doesn't just refresh the whole list - only the selected item. But I don't know where I could set a breakpoint to check who's calling.
Update: thanks to casperOne for the link to the solution! I'll add a summary here in case it goes 404:
[..] actually you are encountering a another hidden aspect of WPF, that's it WPF's data binding engine will data bind to PropertyDescriptor instance which wraps the source property if the source object is a plain CLR object and doesn't implement INotifyPropertyChanged interface. And the data binding engine will try to subscribe to the property changed event through PropertyDescriptor.AddValueChanged() method. And when the target data bound element change the property values, data binding engine will call PropertyDescriptor.SetValue() method to transfer the changed value back to the source property, and it will simultaneously raise ValueChanged event to notify other subscribers (in this instance, the other subscribers will be the TextBlocks within the ListBox.
And if you are implementing INotifyPropertyChanged, you are fully responsible to implement the change notification in every setter of the properties which needs to be data bound to the UI. Otherwise, the change will be not synchronized as you'd expect.
This is a total guess, but I'm thinking that because you have two-way binding enabled, WPF is now aware of when it makes changes, and will update other items it knows is bound to the same instance.
So because you have changed the value of the Name property through the textbox, and WPF knows when you have changed that value, it does its best to update whatever else it knows is bound to it.
It uses reflection to set the value of that property. INotifyPropertyChanged is only needed if the TextBox needs to be informed of a change in the Name property of the Foo class.
Because they're databound to the same object. If you change the binding to
{Binding Foo.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}
then they'll be in synch when the user types in the textbox.

Categories