2

背景: aとともに a がusercontrol定義されては常に表示され、その中に があり、ボタンをクリックすると が に設定され、(ときに() がスクロールされます。景色。私は持っているScrollViewerContentControlContentControlButtonusercontrolVisibleusercontrolVisiblility="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呼び出す前にaすると、ビューの中央にBringIntoViewmy が正しく表示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ため(私が今抱えている問題とは異なります:最初に発生し、次にリフレッシュしてトップに戻します)。そして、私の問題の正しい修正は何ですか?BringIntoViewusercontrolBringIntoViewwindowscrollbar

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 に答える