UserControl BringIntoView() 无法正常工作
Posted
技术标签:
【中文标题】UserControl BringIntoView() 无法正常工作【英文标题】:UserControl BringIntoView() not working properly 【发布时间】:2013-09-14 12:05:06 【问题描述】:背景:
我在ScrollViewer
中定义了一个usercontrol
和一个ContentControl
,ContentControl
将一直可见,其中有一个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 中的方法
当 UserControl 有 DataContext 时,UserControl 的 DependencyProperty 为 null