views:

80

answers:

3

I have an ItemsControl that contains a canvas within a ScrollViewer. The canvas is large and only a portion of it displays at a time. I want to programatically scroll it (the user clicks and drags the canvas to scroll). I looked through the ScrollViewer methods and tried the following in the mouse event handlers:

var scrollViewer = (sender) as ScrollViewer;
scrollViewer.ScrollToHorizontalOffset(scrollViewer.HorizontalOffset + deltaX);
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset + deltaY);

However, this does nothing. I checked the values of deltaX and deltaY and they are valid values (like 3, 5 etc.). The HorizontalOffset and VerticalOffset remain 0 at all times, even after executing the above lines.

Here is my XAML:

<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"
              MouseUp="ScrollViewer_MouseUp" MouseMove="ScrollViewer_MouseMove"
              PreviewMouseLeftButtonDown="ScrollViewer_PreviewMouseLeftButtonDown" Background="Transparent">
        <ItemsControl ItemsSource="{Binding BubbleVMCollection}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                   <!-- My template here -->
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemContainerStyle>
                <Style>
                    <Setter Property="Canvas.Left" Value="{Binding AbsoluteLeft}" />
                    <Setter Property="Canvas.Top" Value="{Binding AbsoluteTop}" />
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ItemsControl>
</ScrollViewer>

Any help/suggestions is appreciated!

A: 

Try below code.

you need to tweak some code according to your specific requirement, i have done this for Tree View , when user drags a node in tree view , i need to give scrolling.

private void ScrollViewer_MouseUp(object sender, MouseButtonEventArgs e)
    {
        Scroll(sender as DependencyObject, e);
    }

    private void ScrollViewer_MouseMove(object sender, MouseEventArgs e)
    {
        Scroll(sender as DependencyObject, e);
    }



    public static T GetVisualDescendent<T>(DependencyObject d) where T : DependencyObject
    {
        return GetVisualDescendents<T>(d).FirstOrDefault();
    }

    public static IEnumerable<T> GetVisualDescendents<T>(DependencyObject d) where T : DependencyObject
    {
        int childCount = VisualTreeHelper.GetChildrenCount(d);

        for (int n = 0; n < childCount; n++)
        {
            DependencyObject child = VisualTreeHelper.GetChild(d, n);

            if (child is T)
            {
                yield return (T)child;
            }

            foreach (T match in GetVisualDescendents<T>(child))
            {
                yield return match;
            }
        }

        yield break;
    }

    static void Scroll(DependencyObject o, MouseEventArgs e)
    {
        ScrollViewer scrollViewer = GetVisualDescendent<ScrollViewer>(o);

        if (scrollViewer != null)
        {
            Point position = e.GetPosition(scrollViewer);
            double scrollMargin = Math.Min(scrollViewer.FontSize * 2, scrollViewer.ActualHeight / 2);

            if (position.X >= scrollViewer.ActualWidth - scrollMargin &&
                scrollViewer.HorizontalOffset < scrollViewer.ExtentWidth - scrollViewer.ViewportWidth)
            {
                scrollViewer.LineRight();
            }
            else if (position.X < scrollMargin && scrollViewer.HorizontalOffset > 0)
            {
                scrollViewer.LineLeft();
            }
            else if (position.Y >= scrollViewer.ActualHeight - scrollMargin &&
                scrollViewer.VerticalOffset < scrollViewer.ExtentHeight - scrollViewer.ViewportHeight)
            {
                scrollViewer.LineDown();
            }
            else if (position.Y < scrollMargin && scrollViewer.VerticalOffset > 0)
            {
                scrollViewer.LineUp();
            }
        }
    }
saurabh
A: 

Another potential solution...used for a TreeView, however don't see why the code should not work in your case:

Programmatically scrolling a TreeView

Aaron
A: 

It works fine (ScrollViewer scrolls) in my test application:

<ScrollViewer Name="scrollViewer"
        VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"
          MouseUp="ScrollViewer_MouseUp" MouseMove="ScrollViewer_MouseMove"
          PreviewMouseLeftButtonDown="ScrollViewer_PreviewMouseLeftButtonDown" Background="Transparent">
        <ItemsControl ItemsSource="{Binding BubbleVMCollection}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas Width="5000" Height="5000"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <!-- My template here -->
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemContainerStyle>
                <Style>
                    <Setter Property="Canvas.Left" Value="{Binding AbsoluteLeft}" />
                    <Setter Property="Canvas.Top" Value="{Binding AbsoluteTop}" />
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ItemsControl>
    </ScrollViewer>

and the code behind of:

Point capturePoint { get; set; }

    private void ScrollViewer_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
        scrollViewer.CaptureMouse();
        capturePoint = e.MouseDevice.GetPosition(scrollViewer);
    }

    private void ScrollViewer_MouseUp(object sender, MouseButtonEventArgs e) {
        scrollViewer.ReleaseMouseCapture();
    }

    private void ScrollViewer_MouseMove(object sender, MouseEventArgs e) {
        if (!scrollViewer.IsMouseCaptured) return;
        Point currentPoint = e.MouseDevice.GetPosition(scrollViewer);
        var deltaX = capturePoint.X - currentPoint.X;
        var deltaY = capturePoint.Y - currentPoint.Y;
        scrollViewer.ScrollToHorizontalOffset(scrollViewer.HorizontalOffset + deltaX);
        scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset + deltaY);
    }

Could you post some more details of the problem you are experiencing?

Stanislav Kniazev
Haha, the answer is in your XAML. I guess I have to specify width and height explicitly. Awesome, thanks :)
aip.cd.aish
You should not have to really. In my xaml it was just easier for me to test this way. But if it works for you then be it.
Stanislav Kniazev