当一个用户控件被显示时,会触发什么事件?

43

我正在尝试为 WPF 的 UserControls(实际上是更通用的 FrameworkElement)添加淡入效果(动画)。

如果我让 Loaded 事件触发初始的淡入效果,则有时淡入效果会在 UserControl 显示之前就已经开始。结果就一团糟了。例如,如果 UserControl 在 Loaded 事件处理程序中执行某些消耗时间的操作(几分之几秒),例如执行查询,则会发生这种情况。

所以,我需要处理 FrameworkElement/UserControl 在内容渲染后触发的某个事件,然后再开始淡入效果。 System.Windows.Window 有一个 ContentRendered 事件,但是 UserControl 没有。那么,当 FrameworkElement(或 UserControl)被渲染后会触发哪个事件呢?


我不能回答 - 因为我的声望,但我需要扩展这篇帖子https://dev59.com/tW445IYBdhLWcg3wws4u#18569270 首先 - 这对我很有帮助。 为了确保它完全呈现,请将 IsLoaded 添加到 if 语句中 - Michael Bernhard
6个回答

27

尝试在SizeChanged或LayoutUpdated事件中检查大小。 当实际宽度或高度不等于0时执行操作。

view.LayoutUpdated+=(o,e)=>
{
  if (!loaded && (view.ActualHeight > 0 || view.ActualWidth > 0))
  {
     // You can also unsubscribe event here.
     loaded =true;
  }
}

这对我有用。IsVisibleChanged没有起作用。我点了个赞。 - IntegerWolf

19

虽然有些晚了,但由于我一直在寻找可行的解决方案却始终未果,现在我想分享我的发现。

如果您想为任��控件(或任何可视化对象甚至是依赖对象)添加ContentRendered事件,您需要深入到Visual层级。

我使用以下代码:

// Wait for Control to Load
void TestUserControl_Loaded(object sender, RoutedEventArgs e)
{
    // Get PresentationSource
    PresentationSource presentationSource = PresentationSource.FromVisual((Visual)sender);

    // Subscribe to PresentationSource's ContentRendered event
    presentationSource.ContentRendered += TestUserControl_ContentRendered;
}

void TestUserControl_ContentRendered(object sender, EventArgs e)
{
    // Don't forget to unsubscribe from the event
    ((PresentationSource)sender).ContentRendered -= TestUserControl_ContentRendered;

    // ..
}

如果不等待控件Loaded,PresentationSource.FromVisual()会返回null。

对我来说,各种Dispatcher.BeginInvoke方法并不总是有效。有时在实际显示控件之前就触发了我的渲染事件。
这种方法在每次使用时都有效。

我知道我正在处理HwndSource,这是相当低级的操作,我不确定可能的影响。(也许更有经验的人可以详细说明。)


8
不幸的是,“ContentRendered”仅在所有者“Window”第一次完成渲染时触发;这意味着:如果此“UserControl”(或任何“DependencyObject”)在“ContentRendered”被触发之后才被渲染,它将不会再次触发。当您具有某些控件根据用户交互进行惰性加载时,可能会遇到此问题。 - Aly Elhaddad

10
您可以使用IsVisibleChanged事件 分配事件处理程序
MyUserControl.IsVisibleChanged += ScheduleUserControl_IsVisibleChanged;

在事件处理程序中检查元素是否可见。

void _IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
    if ((bool)e.NewValue)
    {
        //Visible
    }
    else
    {
        //Not Visible
    }
}

好技巧,谢谢。 - Ahmed HM

8
你可以为你的Storyboard安排一个较低的优先级,例如:
Dispatcher.BeginInvoke(BeginStoryboardAction, DispatcherPriority.ContextIdle);

这是一篇讨论使用此方法相关问题的文章:


7

也许可以尝试使用IsVisibleChanged,但我自己并没有太多使用经验。

如果元素没有通过布局系统呈现,不会触发此事件,原因可能是与IsVisible属性的值无关。例如,该元素可能没有相关联的可视化。


1
不需要,在Loaded事件处理程序中,元素已经有IsVisible == true了。 - John Reynolds
很遗憾,当你的初始工作完成后,手动触发淡入效果如何? - H.B.
1
问题在于 - 我应该在哪里触发它?在处理了 Loaded 事件之后,仍然会发生一些耗时的操作。这可能是 WPF 数据绑定触发实体框架从数据库加载数据,在 Loaded 事件和 UserControl 显示之间的某个地方。 - John Reynolds

3

您可以使用 GotFocus 事件。

 <i:Interaction.Triggers>
        <i:EventTrigger EventName="GotFocus">
            <i:InvokeCommandAction Command="{Binding ContentControlLoadedCommand}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接