我有两个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;
}
}
答案 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中,您可以轻松参考。