UserControl BringIntoView()无法正常工作

时间:2013-09-10 16:50:14

标签: c# wpf user-controls scrollbar

背景 的 我在usercontrol中定义ScrollViewer以及ContentControlContentControl将始终可见,其中有Button,单击按钮时会将usercontrol设置为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首先滚动到视图中然后恢复到{{1的底部眨眼间。

奇怪的事情: 在调用ScrollViewer之前显示messagebox会正确地将BringIntoView显示在视图的中间< / p>

当前黑客解决方案 您可以看到,即使在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放回顶部。什么是我的问题的正确解决方案?

2 个答案:

答案 0 :(得分: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;
    }  

答案 1 :(得分:0)

将背景工作者用于此类事情是不正确的!如果您只能使用LayoutUpdated事件,那么它将是最佳选择。当实际宽度或高度不等于0或使用计时器而不是bg worker时执行。

userControl.LayoutUpdated+=OnLayoutUpdated;

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