如何防止ScrollViewer更新时触发ViewChanged事件?

时间:2018-08-08 00:15:44

标签: c# xaml uwp windows-runtime

我有两个ScrollViewer,当任何ScrollViewer发生更改时,我需要同步那些ScrollViewer的位置,但是现在假设当任何一个ScrollViewer2更改时,在调用ScrollViewer1的ChangeView事件时会触发其ViewChangedEvent,从而将ScrollViewer2的位置重置为

 private void Scroll(ScrollViewer changedScrollViewer)
    {
        var group = ScrollViewers[changedScrollViewer];
        VerticalScrollOffsets[group] = changedScrollViewer.VerticalOffset;
        HorizontalScrollOffsets[group] = changedScrollViewer.HorizontalOffset;
        foreach (var scrollViewer in ScrollViewers.Where(s => s.Value == group && s.Key != changedScrollViewer))
        {
            scrollViewer.Key.ViewChanged -= ScrollViewer_ViewChanged;
            if (scrollViewer.Key.VerticalOffset != changedScrollViewer.VerticalOffset)
            {
                scrollViewer.Key.ChangeView(null, changedScrollViewer.VerticalOffset, null, true);
            }

            if (scrollViewer.Key.HorizontalOffset != changedScrollViewer.HorizontalOffset)
            {
                scrollViewer.Key.ChangeView(changedScrollViewer.HorizontalOffset, null, null, true);
            }
           //Commenting this line works. But I need to set ViewChange event back.
            scrollViewer.Key.ViewChanged += ScrollViewer_ViewChanged;
        }
    }

2 个答案:

答案 0 :(得分:1)

@Nico的解决方案更可取。如果您仍然需要带有标志的东西,它将看起来像这样:

bool is_programmatic_call = false;
private void ScrollViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{

    if (is_programmatic_call)
    {
        is_programmatic_call = false;
        return;
    }
    if(sender == ScrollViewer1)
    {
        ScrollViewer2.ViewChanged -= ScrollViewer_ViewChanged;
        is_programmatic_call = true;
        ScrollViewer2.ChangeView(ScrollViewer1.HorizontalOffset, ScrollViewer1.VerticalOffset, null, true);
        ScrollViewer2.ViewChanged += ScrollViewer_ViewChanged;
    }
    else
    {
        ScrollViewer1.ViewChanged -= ScrollViewer_ViewChanged;
        is_programmatic_call = true;
        ScrollViewer1.ChangeView(ScrollViewer2.HorizontalOffset, ScrollViewer2.VerticalOffset, null, true);
        ScrollViewer1.ViewChanged += ScrollViewer_ViewChanged;
    }
}

ScrollViewer的{​​{1}}事件均由此ViewChanged

处理

答案 1 :(得分:0)

对于同步两个ScrollViewers,更好的方法是制作一个新的Dependency Property,并将其绑定为相同的值。当ScrollViewer值更改时,它将通知Dependency Property自动滚动。此解决方案将阻止Circular Reference在ViewChanged事件中发生。

在此code sample中,我已经为ListView实现了它。您可以参考段代码。但是对于ScrollViewer,您需要制作xaml Behavior,因为ScrollViewer是密封类,因此无法继承。

public class SyncBehavior : Behavior<ScrollViewer>
{

    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.Loaded += OnAssociatedObjectLoaded;
        AssociatedObject.LayoutUpdated += OnAssociatedObjectLayoutUpdated;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.Loaded -= OnAssociatedObjectLoaded;
        AssociatedObject.LayoutUpdated -= OnAssociatedObjectLayoutUpdated;
    }

    private void OnAssociatedObjectLayoutUpdated(object sender, object o)
    {
        SyncPointOffSetY();
    }

    private void OnAssociatedObjectLoaded(object sender, RoutedEventArgs routedEventArgs)
    {
        SyncPointOffSetY();
        AssociatedObject.Loaded -= OnAssociatedObjectLoaded;
    }

    private void SyncPointOffSetY()
    {
        if (AssociatedObject == null) return;

        AssociatedObject.ViewChanged += AssociatedObject_ViewChanged;
    }

    private void AssociatedObject_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
    {
        var MyScrollViewer = sender as ScrollViewer;
        this.SetValue(PointOffSetYProperty, MyScrollViewer.VerticalOffset);

    }

    public double PointOffSetY
    {
        get { return (double)GetValue(PointOffSetYProperty); }
        set { SetValue(PointOffSetYProperty, value); }
    }

    // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty PointOffSetYProperty =
        DependencyProperty.Register("PointOffSetY", typeof(double), typeof(SyncBehavior), new PropertyMetadata(0.0, CallBack));

    private static void CallBack(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var current = d as SyncBehavior;
        var temScrollViewer = current.AssociatedObject;
        if (e.NewValue != e.OldValue & (double)e.NewValue != 0)
        {
            temScrollViewer.ScrollToVerticalOffset((double)e.NewValue);
        }
    }


}

用法

<ScrollViewer  >
    <Interactivity:Interaction.Behaviors>
        <local:SyncBehavior  PointOffSetY="{Binding PointY,Mode=TwoWay}"/>
    </Interactivity:Interaction.Behaviors>
    <StackPanel >
        <Rectangle Height="500" Fill="Red"/>
        <Rectangle Height="500" Fill="Black"/>
        <Rectangle Height="500" Fill="Yellow"/>
    </StackPanel>
</ScrollViewer>
<ScrollViewer Grid.Column="1" >
    <Interactivity:Interaction.Behaviors>
        <local:SyncBehavior  PointOffSetY="{Binding PointY,Mode=TwoWay}"/>
    </Interactivity:Interaction.Behaviors>
    <StackPanel >
        <Rectangle Height="500" Fill="Red"/>
        <Rectangle Height="500" Fill="Black"/>
        <Rectangle Height="500" Fill="Yellow"/>
    </StackPanel>

</ScrollViewer>

我还将上面的代码添加到了sample中,您可以轻松参考。