ASHOK A
ASHOK A

Reputation: 2026

How to get Visual child of WPF DataGrid using MVVM?

The view having one DataGrid and The ViewModel having the following function

public  DependencyObject ScrollViewer(DependencyObject targetControl)
    {
        if (targetControl is ScrollViewer)
        {
            return targetControl;
        }

        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(targetControl); i++)
        {
            var child = VisualTreeHelper.GetChild(targetControl, i);
            var result = ScrollViewer(child);
            if (result == null)
            {
                continue;
            }
            else
            {
                return result;
            }
        }
        return null;
    }

now i want to get scrollviewer of the grid using above function.

var scrolViewer = ScrollViewer(dataGridID) as ScrollViewer;

If i pass the datagrid id as parameter. i will get the result. But it is not possible in MVVM pattern. is there any way to get the visual child data grid ?

Upvotes: 1

Views: 1851

Answers (1)

keyboardP
keyboardP

Reputation: 69372

I've created a quick Attached Property that should allow you to stick with the MVVM concepts and also provide you the ability to scroll to the top and bottom of the DataGrid. It can be improved (e.g. avoid two separate properties and use an IValueConverter to decide how to scroll) but this should give you a good starting point.

First, we create the Attached Properties (one for scrolling to the bottom and one for the top)

public static class Scroller
{
     //Create the attached property and register it
     public static readonly DependencyProperty ScrollToBottomProperty =
        DependencyProperty.RegisterAttached("ScrollToBottom", typeof(bool), typeof(Scroller), new PropertyMetadata(false, ScrollToBottomPropertyChanged));


     //Create the get and set methods for the property
     public static bool GetScrollToBottom(DependencyObject obj)
     {
         return (bool)obj.GetValue(ScrollToBottomProperty);
     }

     public static void SetScrollToBottom(DependencyObject obj, bool value)
     {
         obj.SetValue(ScrollToBottomProperty, value);
     }


     //Get the control that you've attached this to (DataGrid in this case) and find its ScrollViewer 
     private static void ScrollToBottomPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
     {
         var scrollViewer = GetScrollViewer(d) as ScrollViewer;
         if (scrollViewer != null && (bool)e.NewValue)
         {
            //Use built in ScrollToBottom method to scroll to...well...the bottom :)
             scrollViewer.ScrollToBottom();
         }
     }


     //Same as above but for "ScrollToTop" method
     public static readonly DependencyProperty ScrollToTopProperty =
     DependencyProperty.RegisterAttached("ScrollToTop", typeof(bool), typeof(Scroller), new PropertyMetadata(false, ScrollToTopPropertyChanged));


     public static bool GetScrollToTop(DependencyObject obj)
     {
         return (bool)obj.GetValue(ScrollToTopProperty);
     }

     public static void SetScrollToTop(DependencyObject obj, bool value)
     {
         obj.SetValue(ScrollToTopProperty, value);
     }



     private static void ScrollToTopPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
     {
         var scrollViewer = GetScrollViewer(d) as ScrollViewer;

         if (scrollViewer != null && (bool)e.NewValue)
         {
             scrollViewer.ScrollToTop();
         }
     }


     //Your ScrollViewerMethod (I renamed it to GetScrollViewer for clarity)
     public static DependencyObject GetScrollViewer(DependencyObject targetControl)
     {
         if (targetControl is ScrollViewer)
         {
             return targetControl;
         }

         for (int i = 0; i < VisualTreeHelper.GetChildrenCount(targetControl); i++)
         {
             var child = VisualTreeHelper.GetChild(targetControl, i);
             var result = GetScrollViewer(child);
             if (result == null)
             {
                 continue;
             }
             else
             {
                 return result;
             }
         }
         return null;
     }
}

In your XAML, you need to add the relevant namespace (change yours accordingly)

xmlns:custom="clr-namespace:ScrollExampleMVVM"

We can then attach the properties to our DataGrid

<DataGrid custom:Scroller.ScrollToBottom="{Binding ScrollBottom}" custom:Scroller.ScrollToTop="{Binding ScrollTop}" ...?

In your ViewModel, you can have your public ScrollBottom and ScrollTop properties

private bool _scrollBottom = false;
public bool ScrollBottom 
{
    get { return _scrollBottom; }
    set
    {
         _scrollBottom = value;                
         NotifyPropertyChanged("ScrollBottom");              
    }
}

private bool _scrollTop = false;
public bool ScrollTop
{
     get { return _scrollTop; }
     set
     {
          _scrollTop = value;
          NotifyPropertyChanged("ScrollTop");
     }
}

Finally handle your buttons (I'm guessing you're using ICommands) to call the scrolling

private void ScrollBottomCommand(object param)
{
    //Immediately set back to false so that it can be reused
    ScrollBottom = true;
    ScrollBottom = false;
}

private void ScrollTopCommand(object param)
{
    ScrollTop = true;
    ScrollTop = false;
}

That should work. As mentioned earlier, you could probably improve it to avoid the reset code but hopefully this should give an idea.

Upvotes: 1

Related Questions