Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delegating UI actions to ViewModel for execution

Tags:

c#

mvvm

wpf

I have an ScrollViewer which holds an ItemsSource. The items are bound to the ViewModel and are sorted in ascending order. The users are allowed to add items to this list, however, the list needs to be scrolled to bottom because of the sorting order. From what I found, ScrollViewer doesn't have a "lock to bottom" feature, but has a ScrollToEnd method which does what I'm looking for.

The problem though, is that the items are added in the ViewModel, and the ViewModel obviously doesn't have access to the View to call the ScrollToEnd method on the ScrollViewer. To get around this, I declared an action delegate in the ViewModel like this:

public Action ScrollAction { get; set; }

And set it in the View upon creation of the ViewModel:

viewModel.ScrollAction = () => scrollViewer.ScrollToEnd();

The delegate is executed in the ViewModel once an item is added to the list. Even though this works, it feels a little hacky to me, since this kind of breaks the isolation of the ViewModel from the View. Is there a better way to achieve this?

like image 762
PoweredByOrange Avatar asked Jan 19 '26 21:01

PoweredByOrange


1 Answers

I would also vote for an AttachedProperty to your scroll viewer.

I created following attached property to bind scroll to end with a boolean variable.

public static class ScrollViewerBehavior
    {
        public static readonly DependencyProperty ScrollToRightEndProperty =
            DependencyProperty.RegisterAttached("ScrollToRightEnd", typeof (bool), typeof (ScrollViewerBehavior),
                                                new PropertyMetadata(false, OnScrollToRightEndPropertyChanged));

        public static bool GetScrollToRightEnd(DependencyObject obj)
        {
            return (bool) obj.GetValue(ScrollToRightEndProperty);
        }

        public static void SetScrollToRightEnd(DependencyObject obj, bool value)
        {
            obj.SetValue(ScrollToRightEndProperty, value);
        }

        private static void OnScrollToRightEndPropertyChanged(DependencyObject sender,
                                                              DependencyPropertyChangedEventArgs e)
        {
            var sv = sender as ScrollViewer;
            if (sv == null) return;

            if ((bool) e.NewValue)
            {
                sv.ScrollToRightEnd();
            }
            else
            {
                sv.ScrollToLeftEnd();
            }
        }
    }

You can use this attached property in your XAML...

<ScrollViewer ... local:ScrollViewerBehavior.ScrollToRightEnd="{Binding IsScrolledToEnd}" ... />

Alternatively if you want to save the action delegate as in your question, you could do the following in the OnScrollToRightEndPropertyChanged method above.

.....
var viewModel = sv.DataContext as YourViewModel;
if (viewModel != null)
{
    viewModel.ScrollAction = () => sv.ScrollToRightEnd();
}
.....
like image 156
Rajiv Avatar answered Jan 22 '26 10:01

Rajiv