【问题标题】:How to prevent ScrollViewer firing ViewChanged event while it is updating?如何防止 ScrollViewer 在更新时触发 ViewChanged 事件?
【发布时间】:2019-01-15 03:32:59
【问题描述】:

我有两个 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;
        }
    }

【问题讨论】:

  • 因此,滚动查看器会在以下情况下更改视图:1) 用户滚动 2) 您调用 ChangeView,您希望在 (1) 发生时同步它们,而不是 (2) 。您可以使用标志来确保 .
  • 谢谢,一些代码参考会有所帮助。

标签: c# xaml uwp windows-runtime


【解决方案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;
    }
}

ScrollViewerViewChanged 事件都由这个ScrollViewer_ViewChanged 处理

【讨论】:

  • 如果Horizo​​ntal或Vertical Offset为0而不是使用额外的变量,如何返回?
  • 不,这会让你的代码更难看,这里我没有“声明”一个额外的变量,它们是另一个 ScrollViewer 的属性
  • 我的意思是你添加了额外的变量is_programmatic_call 来检查程序调用,使用Horizo​​ntalOffset 或VerticalOffset 是否为0 不是更好吗?
  • 您很可能误解了HorizontalOffsetVerticalOffset 的含义。那些值为 0 的值与 ScrollViewer 是否已更改其视图无关 - 以编程方式或由用户更改。
【解决方案2】:

对于同步两个ScrollViewers,更好的方法是创建一个新的Dependency Property,并将其绑定到相同的值。当Dependency Property 值更改时,它将通知ScrollViewer 自动滚动。此解决方案将阻止 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,您可以轻松参考。

【讨论】:

  • 谢谢,但是在您的示例中,您只有 VerticalScroll。当您同时拥有两个卷轴并想要相应地更新时,事情会变得更加棘手。
  • 是的,对于horizo​​ntalScroll,您还可以添加用于绑定PointX值的Dependency Property
  • 对。现在我只是检查这个changedScrollViewer.VerticalOffset == 0) return;,它适用于我的给定场景,但会将其标记为答案。
猜你喜欢
  • 2010-10-27
  • 1970-01-01
  • 2021-02-14
  • 1970-01-01
  • 1970-01-01
  • 2020-09-13
  • 2021-11-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多