Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle `ScrollViewer.ScrollChanged` event in MVVM?

I've tried to handle the routed event ScrollViewer.ScrollChanged of DataGrid in obvious way:

<i:Interaction.Triggers>
    <i:EventTrigger EventName="ScrollViewer.ScrollChanged">
       <ei:CallMethodAction MethodName="ScrollChangedHandler" TargetObject="{Binding}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

But ScrollChangedHandler did not even fired.

Then, I've found this article about handling events, but I could not figure out what xml namespace(xmlns) is used for mvvmjaco:

<Image Width="360" Height="177" Source="Resources\PlayerArea.png">
   <i:Interaction.Triggers>
      <mvvmjoy:RoutedEventTrigger RoutedEvent="s:Contacts.ContactDown">
          <mvvmjaco:CommandAction Command="{Binding TouchCommand}" />
      </mvvmjoy:RoutedEventTrigger>
   </i:Interaction.Triggers>
</Image>

mvvmjoy uses this class from the article:

public class RoutedEventTrigger :EventTriggerBase<DependencyObject>
{
    RoutedEvent _routedEvent;    
    //The code omitted for the brevity
}

Basically, I have two questions:

  1. What class or library should I use for mvvmjaco xml namespace?
  2. How I can handle ScrollViewer.ScrollChanged event in my viewModel with its arguments?
like image 967
StepUp Avatar asked Oct 21 '25 04:10

StepUp


1 Answers

I would solve it with the following Attached-Property:

using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace WpfApplication2
{
    public class DataGridExtensions
    {
        public static readonly DependencyProperty ScrollChangedCommandProperty = DependencyProperty.RegisterAttached(
            "ScrollChangedCommand", typeof(ICommand), typeof(DataGridExtensions),
            new PropertyMetadata(default(ICommand), OnScrollChangedCommandChanged));

        private static void OnScrollChangedCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DataGrid dataGrid = d as DataGrid;
            if (dataGrid == null)
                return;
            if (e.NewValue != null)
            {
                dataGrid.Loaded += DataGridOnLoaded;
            }
            else if (e.OldValue != null)
            {
                dataGrid.Loaded -= DataGridOnLoaded;
            }
        }

        private static void DataGridOnLoaded(object sender, RoutedEventArgs routedEventArgs)
        {
            DataGrid dataGrid = sender as DataGrid;
            if (dataGrid == null)
                return;

            ScrollViewer scrollViewer = UIHelper.FindChildren<ScrollViewer>(dataGrid).FirstOrDefault();
            if (scrollViewer != null)
            {
                scrollViewer.ScrollChanged += ScrollViewerOnScrollChanged;
            }
        }

        private static void ScrollViewerOnScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            DataGrid dataGrid = UIHelper.FindParent<DataGrid>(sender as ScrollViewer);
            if (dataGrid != null)
            {
                ICommand command = GetScrollChangedCommand(dataGrid);
                command.Execute(e);
            }
        }

        public static void SetScrollChangedCommand(DependencyObject element, ICommand value)
        {
            element.SetValue(ScrollChangedCommandProperty, value);
        }

        public static ICommand GetScrollChangedCommand(DependencyObject element)
        {
            return (ICommand)element.GetValue(ScrollChangedCommandProperty);
        }
    }
}

The class UIHelper looks like:

using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;

namespace WpfApplication2
{
    internal static class UIHelper
    {
        internal static IList<T> FindChildren<T>(DependencyObject element) where T : FrameworkElement
        {
            List<T> retval = new List<T>();
            for (int counter = 0; counter < VisualTreeHelper.GetChildrenCount(element); counter++)
            {
                FrameworkElement toadd = VisualTreeHelper.GetChild(element, counter) as FrameworkElement;
                if (toadd != null)
                {
                    T correctlyTyped = toadd as T;
                    if (correctlyTyped != null)
                    {
                        retval.Add(correctlyTyped);
                    }
                    else
                    {
                        retval.AddRange(FindChildren<T>(toadd));
                    }
                }
            }
            return retval;
        }

        internal static T FindParent<T>(DependencyObject element) where T : FrameworkElement
        {
            FrameworkElement parent = VisualTreeHelper.GetParent(element) as FrameworkElement;
            while (parent != null)
            {
                T correctlyTyped = parent as T;
                if (correctlyTyped != null)
                {
                    return correctlyTyped;
                }
                return FindParent<T>(parent);
            }
            return null;
        }
    }
}

Then you can write in the definition of your DataGrid:

<DataGrid ItemsSource="{Binding MySource}" extensionsNamespace:DataGridExtensions.ScrollChangedCommand="{Binding ScrollCommand}"/>

And in your ViewModel you have an ICommand which looks like:

private ICommand scrollCommand;
public ICommand ScrollCommand
{
    get { return scrollCommand ?? (scrollCommand = new RelayCommand(Scroll)); }
}

private void Scroll(object parameter)
{
    ScrollChangedEventArgs scrollChangedEventArgs = parameter as ScrollChangedEventArgs;
    if (scrollChangedEventArgs != null)
    {

    }
}

For the first your question(special thanks to Andy ONeill and Magnus Montin ):

MVVMJaco is xmlns:mvvmjaco="galasoft.ch/mvvmlight" And the libraries needed are:

  • GalaSoft.MVVmLight
  • GalaSoft.MVVmLight.Extras
  • GalaSoft.MVVmLight.Platform
like image 70
Tomtom Avatar answered Oct 22 '25 16:10

Tomtom