UserControl BringIntoView() 无法正常工作

Posted

技术标签:

【中文标题】UserControl BringIntoView() 无法正常工作【英文标题】:UserControl BringIntoView() not working properly 【发布时间】:2013-09-14 12:05:06 【问题描述】:

背景: 我在ScrollViewer 中定义了一个usercontrol 和一个ContentControlContentControl 将一直可见,其中有一个Button,单击按钮时将设置@987654326 @ 到 Visible,当 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 的底部眨。

奇怪的事情:在调用BringIntoView 之前显示messagebox 会正确地将我的usercontrol 显示到视图中间

当前的 hack 解决方案:您甚至可以在 Window 之后立即关闭它的 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 放回顶部)。我的问题的正确解决方法是什么?

【问题讨论】:

除了用户控件之外,您在滚动查看器中还有什么? 我已经按照您在一次性项目中的描述创建了设置,它可以按照您的预期工作,没有任何窗口。你必须有其他你认为与这个问题无关的事情发生。 @Rafal,我已经更新了我的问题。谢谢。 @nit,我有一个ContentControl 【参考方案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;
      

【讨论】:

【参考方案2】:

我不敢相信使用后台工作者是一个正确的解决方案!您可以使用 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;
      
    

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

【讨论】:

以上是关于UserControl BringIntoView() 无法正常工作的主要内容,如果未能解决你的问题,请参考以下文章

WPF中TreeView.BringIntoView方法的替代方案

WPF中TreeView.BringIntoView方法的替代方案

从另一个 UserControl 调用 UserControl 中的方法

wpf usercontrol的焦点问题

当 UserControl 有 DataContext 时,UserControl 的 DependencyProperty 为 null

属于 UserControl 子级的错误与 UserControl 而不是子级相关联