维持ScrollViewer中的相对滚动调整儿童在偏移 [英] Maintain scrollviewer's relative scrollbar offset when resizing child

查看:179
本文介绍了维持ScrollViewer中的相对滚动调整儿童在偏移的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个网格作为孩子的ScrollViewer。我改变网格的宽度和高度属性来显示不同的放大级别。该网格包含两排带有图像的多列,大小相同。



不过,我想滚动条的相对位置保持不变。 ,无论是在屏幕上应该还是在画面的中央改变网格的大小后的中心



默认视图,在放大:

 私人无效SizeGrid()
{
grid1.Width =(scrollViewer1.ViewportWidth / 2)* grid1.ColumnDefinitions.Count ;
grid1.Height =(scrollViewer1.ViewportHeight / 2)* grid1.RowDefinitions.Count;
}



缩小的说法:

 私人无效scrollViewer1_KeyDown(对象发件人,发送KeyEventArgs E)
{
如果(e.KeyboardDevice.IsKeyDown(Key.Insert))
{
grid1.Width =(scrollViewer1.ViewportWidth / 2)* grid1.ColumnDefinitions.Count / 5;
grid1.Height =(scrollViewer1.ViewportHeight / 2)* grid1.RowDefinitions.Count / 3;
}
}






我试着做 ...



如果我知道是什么的重点列(我不希望需要知道这一点):

 双shiftAmount =(scrollViewer1.ScrollableWidth /(grid1.ColumnDefinitions.Count  -  columnsOnScreen)); 
scrollViewer1.ScrollToHorizo​​ntalOffset(列* shiftAmount);

如果我不知道到底他们正在寻找什么列,但我只是想保持相对位置...

 双previousScrollRatio = scrollViewer1.Horizo​​ntalOffset / scrollViewer1.ScrollableWidth; 
//调整电网...
scrollViewer1.ScrollToHorizo​​ntalOffset(previousScrollRatio * scrollViewer1.ScrollableWidth);






这两种方法的工作原理。如果我缩小与中心的滚动条,然后滚动条会去到最右边。任何想法?



一个很小的代码示例,可以发现这里 。再加上scroll_KeyDown方法从上面






默认缩放屏幕截图:





截图缩小后的错误的(海军蓝色和粉红色方块是遥远屏):





截图缩小后,它的的是这样的:




解决方案

下面是保持内容的中心,同时或缩小



缩放的解决方案<预类=郎-CS prettyprint-覆盖> //变量来存储偏移值
双RELX;
双重依赖;
无效scrollViewer1_ScrollChanged(对象发件人,ScrollChangedEventArgs E)
{
的ScrollViewer滚动=发件人为的ScrollViewer;
//看到,如果内容大小改变
如果(e.ExtentWidthChange!= 0 || e.ExtentHeightChange!= 0)
{
//计算并据此设置
scroll.ScrollToHorizo​​ntalOffset(CalculateOffset(e.ExtentWidth,e.ViewportWidth,scroll.ScrollableWidth,RELX));
scroll.ScrollToVerticalOffset(CalculateOffset(e.ExtentHeight,e.ViewportHeight,scroll.ScrollableHeight,靠));
}
,否则
{
//存储的相对值,如果正常滚动
RELX =(e.Horizo​​ntalOffset + 0.5 * e.ViewportWidth)/ e.ExtentWidth;
=依赖(e.VerticalOffset + 0.5 * e.ViewportHeight)/ e.ExtentHeight;
}
}

私有静态双CalculateOffset(双程度,双视口,双scrollWidth,双relBefore)
{
//计算新的偏移
双胶纸= relBefore *程度 - 0.5 *视口;
//看它是否是负的,因为初始值
如果(偏移℃,)
{
//中心内容
//这可以被设置0如果不需要默认中心
偏移= 0.5 * scrollWidth;
}
收益抵消;
}



背后的想法是将存储最后滚动位置,并用它来计算新只要内容大小改变,这将使在程度上的变化所抵消。



只是附加 ScrollChanged 的事件的ScrollViewer 在构造等这个事件处理程序,并离开休息吧。



例如



<预类=郎-CS prettyprint-覆盖> scrollViewer1.ScrollChanged + = scrollViewer1_ScrollChanged;



上面的解决方案将确保以保持电网中心,即使是第一次加载

$ B中心内容
$ b

样品



缩放



放大





其他



我也试图为同一个可连接的行为,所以你不需要连线的事件,只是设置该属性将启用或禁止行为



<预类=郎-CS prettyprint-覆盖> 命名空间CSharpWPF
{
公共类AdvancedZooming:DependencyObject的
{
公共静态布尔GetKeepInCenter(DependencyObject的OBJ)
{
返回(布尔)obj.GetValue(KeepInCenterProperty);
}

公共静态无效SetKeepInCenter(OBJ的DependencyObject,布尔值)
{
obj.SetValue(KeepInCenterProperty,值);
}

//使用的DependencyProperty作为后备存储KeepInCenter。这使得动画,造型,绑定等等...
公共静态只读的DependencyProperty KeepInCenterProperty =
DependencyProperty.RegisterAttached(KeepInCenter的typeof(布尔)的typeof(AdvancedZooming),新PropertyMetadata(假,OnKeepInCenterChanged ));

//使用的DependencyProperty作为后备存储行为。这使得动画,造型,绑定等等...
公共静态只读的DependencyProperty BehaviorProperty =
DependencyProperty.RegisterAttached(行为的typeof(AdvancedZooming)的typeof(AdvancedZooming),新PropertyMetadata(NULL)) ;

私有静态无效OnKeepInCenterChanged(DependencyObject的D,DependencyPropertyChangedEventArgs E)
{
的ScrollViewer滚动= D为ScrollViewer中;

如果((布尔)e.NewValue)
{
//附加的行为
AdvancedZooming行为=新AdvancedZooming();
scroll.ScrollChanged + = behavior.scroll_ScrollChanged;
scroll.SetValue(BehaviorProperty,行为);
}
,否则
{
// dettach行为
AdvancedZooming行为= scroll.GetValue(BehaviorProperty)为AdvancedZooming;
如果(行为!= NULL)
scroll.ScrollChanged - = behavior.scroll_ScrollChanged;
scroll.SetValue(BehaviorProperty,NULL);
}
}

//变量来存储偏移值
双RELX;
双重依赖;
无效scroll_ScrollChanged(对象发件人,ScrollChangedEventArgs E)
{
的ScrollViewer滚动=发件人为的ScrollViewer;
//看到,如果内容大小改变
如果(e.ExtentWidthChange!= 0 || e.ExtentHeightChange!= 0)
{
//计算并据此设置
scroll.ScrollToHorizo​​ntalOffset(CalculateOffset(e.ExtentWidth,e.ViewportWidth,scroll.ScrollableWidth,RELX));
scroll.ScrollToVerticalOffset(CalculateOffset(e.ExtentHeight,e.ViewportHeight,scroll.ScrollableHeight,靠));
}
,否则
{
//存储的相对值,如果正常滚动
RELX =(e.Horizo​​ntalOffset + 0.5 * e.ViewportWidth)/ e.ExtentWidth;
=依赖(e.VerticalOffset + 0.5 * e.ViewportHeight)/ e.ExtentHeight;
}
}

私有静态双CalculateOffset(双程度,双视口,双scrollWidth,双relBefore)
{
//计算新的偏移
双胶纸= relBefore *程度 - 0.5 *视口;
//看它是否是负的,因为初始值
如果(偏移℃,)
{
//中心内容
//这可以被设置0如果不需要默认中心
偏移= 0.5 * scrollWidth;
}
收益抵消;
}
}
}



使行为



通过XAML



<预类=郎咸平的XML prettyprint-覆盖> <的ScrollViewer L: AdvancedZooming.KeepInCenter =真>



<预类=郎咸平的XML prettyprint-覆盖> <风格的TargetType =ScrollViewer中X:键=zoomCenter>
< setter属性=L:AdvancedZooming.KeepInCenter
值=真/>
< /样式和GT;



还是通过象



<预类代码=郎-CS prettyprint-覆盖> scrollViewer1.SetValue(AdvancedZooming.KeepInCenterProperty,真);



<预类=郎-CS prettyprint-覆盖> AdvancedZooming.SetKeepInCenter(scrollViewer1,真);



设置属性 L:AdvancedZooming.KeepInCenter =真内嵌,通过样式或编程,以便能够在任何的ScrollViewer



微升行为:是指命名空间来AdvancedZooming类的xmlns :L =CLR的命名空间:CSharpWPF在这个例子


I have a scrollviewer with a grid as the child. I am changing the grid's width and height properties to show different "zoom" levels. The grid contains 2 rows with many columns of images, all the same size.

However, I want the relative position of the scrollbar to stay the same. Whatever is on the center of the screen should still be on the center of the screen after changing the grid's size.

Default "zoomed in" view:

private void SizeGrid()
{
    grid1.Width = (scrollViewer1.ViewportWidth / 2) * grid1.ColumnDefinitions.Count;
    grid1.Height = (scrollViewer1.ViewportHeight / 2) * grid1.RowDefinitions.Count;        
}

"Zoomed out" view:

private void scrollViewer1_KeyDown(object sender, KeyEventArgs e)
{
    if (e.KeyboardDevice.IsKeyDown(Key.Insert))
    {
        grid1.Width = (scrollViewer1.ViewportWidth / 2) * grid1.ColumnDefinitions.Count / 5;
        grid1.Height = (scrollViewer1.ViewportHeight / 2) * grid1.RowDefinitions.Count / 3;
    }
}


What I tried doing...

If I know what column is focused (I don't want to need to know this):

double shiftAmount = (scrollViewer1.ScrollableWidth / (grid1.ColumnDefinitions.Count - columnsOnScreen));
scrollViewer1.ScrollToHorizontalOffset(column * shiftAmount);

If I don't know exactly what column they are looking at, but I just want to keep the relative position...

double previousScrollRatio = scrollViewer1.HorizontalOffset / scrollViewer1.ScrollableWidth;
//resize grid...
scrollViewer1.ScrollToHorizontalOffset(previousScrollRatio * scrollViewer1.ScrollableWidth);


Neither approach works. If I zoom out with the scrollbar centered, then the scrollbar will go to the far right. Any idea?

A minimal code example can be found here plus the scroll_KeyDown method from above.


Screenshot of the default zoom:

Screenshot after zooming out, incorrectly (the navy blue and pink squares are far off screen):

Screenshot after zooming out, what it should look like:

解决方案

Here is a solution to keep the content in center while zooming in or out

    //variables to store the offset values
    double relX;
    double relY;
    void scrollViewer1_ScrollChanged(object sender, ScrollChangedEventArgs e)
    {
        ScrollViewer scroll = sender as ScrollViewer;
        //see if the content size is changed
        if (e.ExtentWidthChange != 0 || e.ExtentHeightChange != 0)
        {
            //calculate and set accordingly
            scroll.ScrollToHorizontalOffset(CalculateOffset(e.ExtentWidth, e.ViewportWidth, scroll.ScrollableWidth, relX));
            scroll.ScrollToVerticalOffset(CalculateOffset(e.ExtentHeight, e.ViewportHeight, scroll.ScrollableHeight, relY));
        }
        else
        {
            //store the relative values if normal scroll
            relX = (e.HorizontalOffset + 0.5 * e.ViewportWidth) / e.ExtentWidth;
            relY = (e.VerticalOffset + 0.5 * e.ViewportHeight) / e.ExtentHeight;
        }
    }

    private static double CalculateOffset(double extent, double viewPort, double scrollWidth, double relBefore)
    {
        //calculate the new offset
        double offset = relBefore * extent - 0.5 * viewPort;
        //see if it is negative because of initial values
        if (offset < 0)
        {
            //center the content
            //this can be set to 0 if center by default is not needed
            offset = 0.5 * scrollWidth;
        }
        return offset;
    }

idea behind is to store the last scroll position and use it to calculate the new offset whenever the content size is changed which will make the change in extent.

just attach the event ScrollChanged of ScrollViewer to this event handler in constructor etc. and leave the rest to it.

eg

    scrollViewer1.ScrollChanged += scrollViewer1_ScrollChanged;

above solution will ensure to keep the grid in center, even for first load

sample of centered content

zoomed in

zoomed out

Extra

I also tried to create an attachable behavior for the same so you do not need to wire the events, just setting up the property will enable or disable the behavior

namespace CSharpWPF
{
    public class AdvancedZooming : DependencyObject
    {
        public static bool GetKeepInCenter(DependencyObject obj)
        {
            return (bool)obj.GetValue(KeepInCenterProperty);
        }

        public static void SetKeepInCenter(DependencyObject obj, bool value)
        {
            obj.SetValue(KeepInCenterProperty, value);
        }

        // Using a DependencyProperty as the backing store for KeepInCenter.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty KeepInCenterProperty =
            DependencyProperty.RegisterAttached("KeepInCenter", typeof(bool), typeof(AdvancedZooming), new PropertyMetadata(false, OnKeepInCenterChanged));

        // Using a DependencyProperty as the backing store for Behavior.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty BehaviorProperty =
            DependencyProperty.RegisterAttached("Behavior", typeof(AdvancedZooming), typeof(AdvancedZooming), new PropertyMetadata(null));

        private static void OnKeepInCenterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ScrollViewer scroll = d as ScrollViewer;

            if ((bool)e.NewValue)
            {
                //attach the behavior
                AdvancedZooming behavior = new AdvancedZooming();
                scroll.ScrollChanged += behavior.scroll_ScrollChanged;
                scroll.SetValue(BehaviorProperty, behavior);
            }
            else
            {
                //dettach the behavior
                AdvancedZooming behavior = scroll.GetValue(BehaviorProperty) as AdvancedZooming;
                if (behavior != null)
                    scroll.ScrollChanged -= behavior.scroll_ScrollChanged;
                scroll.SetValue(BehaviorProperty, null);
            }
        }

        //variables to store the offset values
        double relX;
        double relY;
        void scroll_ScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            ScrollViewer scroll = sender as ScrollViewer;
            //see if the content size is changed
            if (e.ExtentWidthChange != 0 || e.ExtentHeightChange != 0)
            {
                //calculate and set accordingly
                scroll.ScrollToHorizontalOffset(CalculateOffset(e.ExtentWidth, e.ViewportWidth, scroll.ScrollableWidth, relX));
                scroll.ScrollToVerticalOffset(CalculateOffset(e.ExtentHeight, e.ViewportHeight, scroll.ScrollableHeight, relY));
            }
            else
            {
                //store the relative values if normal scroll
                relX = (e.HorizontalOffset + 0.5 * e.ViewportWidth) / e.ExtentWidth;
                relY = (e.VerticalOffset + 0.5 * e.ViewportHeight) / e.ExtentHeight;
            }
        }

        private static double CalculateOffset(double extent, double viewPort, double scrollWidth, double relBefore)
        {
            //calculate the new offset
            double offset = relBefore * extent - 0.5 * viewPort;
            //see if it is negative because of initial values
            if (offset < 0)
            {
                //center the content
                //this can be set to 0 if center by default is not needed
                offset = 0.5 * scrollWidth;
            }
            return offset;
        }
    }
}

enabling the behavior

via xaml

<ScrollViewer l:AdvancedZooming.KeepInCenter="True">

or

<Style TargetType="ScrollViewer" x:Key="zoomCenter">
    <Setter Property="l:AdvancedZooming.KeepInCenter"
            Value="True" />
</Style>

or via code like

scrollViewer1.SetValue(AdvancedZooming.KeepInCenterProperty, true);

or

AdvancedZooming.SetKeepInCenter(scrollViewer1, true);

set the property l:AdvancedZooming.KeepInCenter="True" inline, via styles or programmatically in order to enable the behavior on any scrollviewer

l: refers to the namespace to AdvancedZooming class xmlns:l="clr-namespace:CSharpWPF" in this example

这篇关于维持ScrollViewer中的相对滚动调整儿童在偏移的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆