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.
Related
I have heard a lot about two-way bindings in WPF, but I'm not entirely clear on how to accomplish it or what it actually means.
I have a ListView with a bunch of items in it. When the user selects a new item, a TextBox in the application will change its text to display some property of the selected item.
But when the user changes the text in the text box I want the ListView item to be updated immediately as well. Is there any "two-way binding" magical WPF way of accomplishing this?
Mark's answer shows how to accomplish what you want, but you also asked more generally about "how to accomplish [two-way binding] and what it actually means."
One-way binding means that the binding target (e.g. control) will display data from the binding source (e.g. business object), and will update itself as the business object changes, but that changes to the control will not be propagated back to the business object. E.g. if the Person.Name changes from "bob" to "kate", the TextBlock.Text bound to the Name will change from "bob" to "kate" too.
Two-way binding simply means that not only are changes in the business object reflected in the UI, but changes made by the user in the UI are propagated back to the business object too. So now when the user edits the TextBox.Text bound to the Name, say changing "kate" to "edmund", WPF will set the Person.Name property to "edmund" as well.
To accomplish this, just set Mode=TwoWay on the Binding declaration. Some properties bind two-way by default: TextBox.Text, for example, binds TwoWay by default, which is why Mark's code doesn't need the Mode declaration. In addition, as Mark notes, by default WPF only propagates changes back to the business object when the control loses focus. If you have two UI elements bound to the same property, this can mean they appear out of sync, in which case you can use the UpdateSourceTrigger to force WPF to propagate whenever the property changes.
MSDN covers this in detail with some good clear diagrams: see Data Binding Overview in the WPF SDK.
If you haven't you'll need to implement INotifyPropertyChanged for your class that you're binding to.
Also, when you say you want the ListBox item to be updated immediately, you mean that you want it to change as you type in the TextBox. By default the TextBox.Text property updates its source when it loses focus, but you can change this by setting the binding UpdateSourceTrigger to PropertyChanged:
{Binding Source={...}, Path=Whatever, UpdateSourceTrigger=PropertyChanged}
What is the Type of the items in the ListView? To get the two way binding going the need you implement INotifyPropertyChanged...
This might help WPF event property changed?
I bound Selected Item of ListView with my viewmodel property SelectedLayout and I have changed SelectedLayout from code behind. The Selected Item is changing properly But focus is not coming to the selected item. It is clear when I press enter. Focus is at button where it was before setting SelectedLayout . How can I get Focus to the selected Item in windows 10 UWP?
Just changing the value of bound property does not automatically change the focus to the corresponding element. It is done by design, as in many cases you do not need to change focus, just to update the value of the control. So what you need to do is to implement the logic of changing focus in your MVVM code.
To do that, the good practice would be to use Attached Property as outlined here: Set focus on textbox in WPF from view model (C#). In this manner, you can bind the IsFocused attached property of your controls to your ViewModel, and then implement any focusing logic in the ViewModel.
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.
I am trying to implement a little search module in a WPF application. The results of the search are bound to a ListView and I want to set the focus on the first item of this ListView as soon as it is populated with new results.
The solution of this question - Set Item Focus in ListView WPF - suggests calling ContainerFromIndex(int index) method to get the necessary ListViewItem. Trying this at the very end of my search procedure returns null. So, I deduce that although the source of the ListView has been populated, the view itself isn't updated at this moment.
The next thing I tried is handling a SourceUpdated event of my ListView. It doesn't fire. I added NotifyOnSourceUpdated=True to the binding as was pointed out in the answer of this question - sourceupdated event not firing - but the event still doesn't fire.
I'll just add that the binding itself works. So, what is the right way to set focus on the first item (or on any other item) of a ListView?
EDIT: I'll supply the relevant part of the XAML to make things more clear:
<ListView Name="foundRequestsListView"
IsSynchronizedWithCurrentItem="True"
utils:GridViewSort.AutoSort="True"
ItemsSource="{Binding Path=FoundRequests, NotifyOnSourceUpdated=True}"
SourceUpdated="foundRequestsListView_SourceUpdated" >
<ListView.Resources>...</ListView.Resources>
<ListView.View>
<GridView>...</GridView>
</ListView.View>
</ListView>
FoundRequests property is an ordinary List.
EDIT: Calling UpdateLayout() beforehand solves the problem.
The problem is solved by calling UpdateLayout() method before trying to get a ListViewItem.
One way that you can achieve your requirement is to use the selected item that you say you have to access the associated ListViewItem that you can focus. Try this:
ListViewItem item = YourListView.ItemContainerGenerator.
ContainerFromItem(YourSelectedItem) as ListViewItem;
Once you have the associated ListViewItem, you can focus it simply, like this:
item.Focus();
Bind the SelectedItem to a property in your ViewModel.
SelectedItem="{Binding PropertyNameHere}"
When the selected item is changed on the collection, it will also be updated on the property bound to the selected item.
So at the end of your search procedure, simply set the bound property to the first item in the collection.
if (FoundRequests.Count > 0)
PropertyNameHere = FoundRequests[0];
You do not need to worry about INotifyPropertyChanged for the bound property.
Apologies for not posting any code for this but all my efforts seem to be going nowhere.
I need to get the name of an observableCollection a textblock is bound to from the mouseDown event handler, so I can then perform some operations on the data, is there any way to do this?
I have set the Tag of the textblock to {binding} so i get the entire object back the textblock is bound to. Other than this im not sure where to go next.
Update:
The reason for this is that I have 2 multiselect treeviews, with a heirarchial data template, each template shares the same treeview but is bound to a different observable collection.
The way my multiselect works is by applying a style to each treeview if the IsSelected value of that item in my collection is true.
Now I am using the Mousedown event handler for the textblock in my datatemplate to get the item I am working on, BUT some items can be in both collections at once. I need to know which item to set the IsSelected value on. Using Binding{tag} I get the Item I need to set on but not the collection it is within.
I'm using Mousedown as the event handler because If you click on one item to select it, then click again it needs to unselect and the treeview event handlers didnt seem to allow this to happen (SelectedItemChanged etc).
As a side note I also need to be able to hide the default selected style of the Treeview as this isnt used and it gets confusing.
You can't determine what collection an item is in, but you can determine what TreeView the user clicked on. Then you can get the collection by knowing the TreeView, which should solve your immediate problem.