2

背景: 我在 ausercontrol中定义了 aScrollViewer和 aContentControlContentControl将始终可见,并且在其中有 aButton,单击按钮时将设置usercontrolVisible,并且当usercontrol显示 (Visiblility="Visible") 我希望它滚动到风景。我有

XAML

<ScrollViewer  VerticalScrollBarVisibility="Auto"  MaxHeight="465">
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="*" />
      <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <ContentControl Content="{Binding MyOtherViewModel}"  Width="960" ></ContentControl>
    <local:MyView  IsVisibleChanged="MyView_IsVisibleChanged" Grid.Row="1" Visibility="{Binding IsNonCompliant, Converter={StaticResource BooltoVisible}, UpdateSourceTrigger=PropertyChanged}" />        
</ScrollViewer>

代码背后

private void MyView_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            (sender as Control).BringIntoView();        
        }

问题:这不起作用,或者更准确地说,我usercontrol先滚动到视图中,然后眨眼间又回到底部ScrollViewer

奇怪的事情:messagebox调用前显示一个BringIntoView将正确显示我usercontrol到视图的中间

当前的破解解决方案:Window您甚至可以在关闭后立即看到此方法loaded

private void MyView_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
            {
                Window ss = new Window();
                ss.Loaded += new RoutedEventHandler(ss_Loaded);
                ss.ShowDialog();
                (sender as Control).BringIntoView();        
            }   

private void ss_Loaded(object sender, RoutedEventArgs e)
        {
            (sender as Window).Close();
        }

问题:我知道肯定有其他事情发生,但我无法识别它,但我真的想知道当一个窗口显示时发生了什么ShowDialog?这是因为它会刷新,window以便加载BringIntoView后才会发生usercontrol(不像我现在遇到的问题:BringIntoView首先发生,然后window刷新并放回scrollbar顶部)。我的问题的正确解决方法是什么?

4

2 回答 2

1

我不敢相信使用后台工作者是一个正确的解决方案!您可以使用 LayoutUpdated 事件轻松找到控件何时加载并最终显示。

userControl.LayoutUpdated+=OnLayoutUpdated;    
private bool loaded=false;


    private void OnLayoutUpdated(object sender,EventArgs e)
    {
      if (!loaded && (view.ActualHeight > 0 || view.ActualWidth > 0))
      {
         // Unsubscribe or set a flag.
         userControl.LayoutUpdated -= OnLayoutUpdated;
         loaded = true;
      }
    }

因此,您可以在更新布局并设置高度或宽度时执行该代码。这将意味着控件已加载并显示。比您可以取消订阅或设置初始化已编译的标志。

于 2014-11-24T10:52:38.417 回答
1

它看起来像BringIntoView在我Usercontrol被渲染之前被调用,因此当它被完全渲染时,它会scrollbar恢复到顶部(正如我在我的问题中所描述的那样)。感谢@Evgeny对另一个问题的回答,我现在得到了一个更好的解决方案(也许更少的黑客攻击?)。还是想看看有没有更好的解决办法。

   private void MyView_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
   {
        var border = (FrameworkElement)sender;
        if (border.IsVisible)
        {
            //Window ss = new Window();
            //ss.Loaded += new RoutedEventHandler(ss_Loaded);
            //ss.ShowDialog();
            using (BackgroundWorker bg = new BackgroundWorker())
            {
                bg.DoWork += new DoWorkEventHandler(bg_DoWork);
                bg.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bg_RunWorkerCompleted);
                Tuple<FrameworkElement, double> b = new Tuple<FrameworkElement, double>(border, border.Height);
                bg.RunWorkerAsync(b);
            }
        }
   }       

    private void bg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        (e.Result as UserControl).BringIntoView();
    }

    private void bg_DoWork(object sender, DoWorkEventArgs e)
    {   
        int maxwait = 300 //not scrolled to the view is not a disaster, but if the program hangs forever it will be a disaster, so set this to prevent that from happening
        while (maxwait!=0 
               && 
               (e.Argument as Tuple<FrameworkElement, double>).Item1.ActualHeight != (e.Argument as Tuple<FrameworkElement, double>).Item2)
        {
            Thread.Sleep(1);
            maxwait --;
        }
        e.Result = (e.Argument as Tuple<FrameworkElement, double>).Item1;
    }  
于 2013-09-13T15:02:21.793 回答