WPF: How to trigger GUI behaviours in response to view-model events?
Asked Answered
S

2

6

I'm developing a WPF/MVVM application and I have a listbox binding to data in a ViewModel. At various points I need the view model to cause the listbox to scroll to a given element.

How can I do this without creating a custom control and while still maintaining good separation of concerns?

I've currently got it working by creating a custom behavior class in the view layer with a dependency property VisibleIndex which the XAML code then binds to an integer in the view model:

<ListBox x:Name="myListBox" 
        local:ListBoxVisibilityBehavior.VisibleIndex="{Binding VisibleIndex}">

When the integer is set it triggers the dependency properties update handler which tells the listbox to scroll to the associated index.

This seems a bit hacky though because the dependency property value is never changed by the listbox and the update handler only gets called when the value changes, so the only way to ensure that the relevent item is visible is to do something like this:

// view-model code
this.VisibleIndex = -1;
this.VisibleIndex = 10;

The only reason I'm using a behaviour class at the moment is for binding my custom dependency property, is there a way to do something like this with events instead?

Sexology answered 21/4, 2013 at 23:41 Comment(2)
I feel @EdwardTanguay's questions Why are events and commands in MVVM so unsupported by WPF / Visual Studio? and When does it make sense to abandon MVVM? have been self-answering itRecapitulate
Why do you think this is a hack? These are what Behaviors are for. When are you intending for the ListBox to update VisibleIndex value? Your behavior in it's OnAttached() function can listen to particular events desired from the ListBox and update VisibleIndex accordingly. IMO it's a very decent implementation and also makes it re-usable than tying into the events in the code-behind. Nothing wrong with typing to events in code-behind as it's only View related but it's not as reusable(needs copy-paste for future) as a Behavior.Thermo
S
0

For anyone else interested in the answer to this one of the MS engineers on the WPF forum cleared it up for me. Instead of binding to an event directly you bind to a wrapper object that encapsulates that event. The behaviour can then grab the reference to the wrapper from its DP and do whatever it wants with it i.e. subscribe to the event, trigger it etc.

Sexology answered 3/5, 2013 at 5:23 Comment(1)
Better very, very late than never: social.msdn.microsoft.com/Forums/vstudio/en-US/…Sexology
C
0

Attached properties are somewhat required in your case - as at some point, 'somewhere' you need to call the following method...

ListBox.ScrollIntoView(item)  

or

ListBoxItem.BringIntoView();

And for that you need some sort of code behind - and attached properties/behaviors are a nice way of packaging that, w/o impacting your MVVM.

Having said that - if you just need to have your 'selected item' scrolled into view at all times (which is the case most of the time). Then you could use a different attached-property based solution (that again):

mvvm how to make a list view auto scroll to a new Item in a list view

All you have to do then is to set or bind to SelectedItem.

That's a bit 'nicer' if you wish - but the mechanism is the same.

Cataphoresis answered 22/4, 2013 at 16:3 Comment(3)
Thanks for your response NSGaga. I may not have been entirely clear with my question but I already am using attached properties. The problem with attached properties is their handlers don't get called unless the value changes, and to my knowledge there's no such thing as a "write-only" attached property. What I'm trying to find out is if WPF supports anything that is similar to an attached property but where the handler gets called in response to a custom event instead.Sexology
you're welcome Mark - You're confusing some things I think - 'dp' value changes when you set VisibleIndex - that's how it works. If you do just that, you have a write only (it has a get but it's not used, so who cares?). You have triggers, and data triggers in XAML - but doesn't work in your case - it's what I tried to say. You need either a) code behind or b) attached-p. If you want something else you'd need to 'make a drawing' sort of as I don't get it :)Cataphoresis
If I set "VisibleIndex" to 3 then the DP update handler will get called, but if I do it again after that it won't because the handler only gets called when the value changes. Even setting it to -1 and then back to 3 immediately afterwards doesn't work because WPF batches changes to DPs and goes "oh, the value isn't changing after all, so let's just leave it as is". I'm trying to figure out if there's a way for the dependency property update handler to get called irrespective of whether or not the value has actually changed.Sexology
S
0

For anyone else interested in the answer to this one of the MS engineers on the WPF forum cleared it up for me. Instead of binding to an event directly you bind to a wrapper object that encapsulates that event. The behaviour can then grab the reference to the wrapper from its DP and do whatever it wants with it i.e. subscribe to the event, trigger it etc.

Sexology answered 3/5, 2013 at 5:23 Comment(1)
Better very, very late than never: social.msdn.microsoft.com/Forums/vstudio/en-US/…Sexology

© 2022 - 2024 — McMap. All rights reserved.