如何分配全局初始化事件?

11

我在我的 App.xaml.cs 文件中有以下代码:

protected override void OnStartup(StartupEventArgs e)
{
    EventManager.RegisterClassHandler(typeof(TextBox), TextBox.TextChangedEvent, new RoutedEventHandler(TextBox_TextChangedEvent));
}
private void TextBox_TextChangedEvent(object sender, RoutedEventArgs e)
{
    // Works
}

我希望对InitializedEvent做类似的事情。
这是我的失败尝试:

protected override void OnStartup(StartupEventArgs e)
{
    EventManager.RegisterClassHandler(typeof(FrameworkElement), FrameworkElement.InitializedEvent, new EventHandler(FrameworkElement_InitializedEvent));
}
private void FrameworkElement_InitializedEvent(object sender, EventArgs e)
{

}

初始化事件是否在其他地方?
这真的可能吗?

我尝试使用LoadedEvent:

protected override void OnStartup(StartupEventArgs e)
{
    EventManager.RegisterClassHandler(typeof(FrameworkElement), FrameworkElement.LoadedEvent, new RoutedEventHandler(FrameworkElement_LoadedEvent));
}
private void FrameworkElement_LoadedEvent(object sender, RoutedEventArgs e)
{
    // Fires only for Windows
}

它只对 Windows 产生了作用,而不对 Windows 内部的控件产生作用。然而,我确实意识到:当我为窗口内的标签添加一个加载事件时,即使我的普通加载事件(我专门为标签创建的)为空,全局的 FrameworkElement_LoadedEvent 也会为该标签触发。我还尝试过这些:

EventManager.RegisterClassHandler(typeof(Button), Button.LoadedEvent, new RoutedEventHandler(Button_LoadedEvent));
EventManager.RegisterClassHandler(typeof(Grid), Grid.LoadedEvent, new RoutedEventHandler(Grid_LoadedEvent));
EventManager.RegisterClassHandler(typeof(DataGrid), DataGrid.LoadedEvent, new RoutedEventHandler(DataGrid_LoadedEvent));

但是它们只有在我特别添加另一个空的加载事件到这些控件上时才会触发。

我的目标是建立一个每个被初始化的控件的时间日志。

如何在不为我拥有的每个单一控件添加加载事件的情况下实现这一目标呢?(我有很多)


1
事件管理器用于处理路由事件。我认为Initialized不是一个路由事件(换句话说,当它被触发时,它不会通过控件进行冒泡/隧道传递)。 - Jeff R.
也许你能告诉我们,为什么需要这样一个全局事件处理程序? - dymanoid
@dymanoid 我想建立一个时间日志,记录每个被初始化的控件。 - Shiasu-sama
我尝试过使用Loaded事件,但那只对容器控件有效,而不适用于容器控件内部的控件。 - Shiasu-sama
@JeffR。我正在尝试的还有其他选择吗?也许有另一种类型的事件可以触发所有我的控件?包括网格、按钮和标签等等? - Shiasu-sama
使用Initialized是可以的,但它不能像那样成为一个单独的全局分配。您需要为每个控件订阅事件。通常,父控件在所有子控件初始化之前不会引发Initialized。因此,如果根面板甚至窗口引发了Initialized,则可以安全地假设所有子控件也已经初始化完成。 - Jeff R.
1个回答

6
这里是你需要的内容!
public partial class App : Application
{
    // ##############################################################################################################################
    // Constructor
    // ##############################################################################################################################

    #region Constructor

    static App()
    {
        // set MyInitialized=true for new windows (happens before Loaded)
        EventManager.RegisterClassHandler(typeof(Window), FrameworkElement.SizeChangedEvent, new RoutedEventHandler(OnSizeChanged));

        // our loaded handler
        EventManager.RegisterClassHandler(typeof(UIElement), FrameworkElement.LoadedEvent, new RoutedEventHandler(OnLoaded), true);
        EventManager.RegisterClassHandler(typeof(ContentElement), FrameworkContentElement.LoadedEvent, new RoutedEventHandler(OnLoaded), true);
    }

    private static void OnSizeChanged(object sender, RoutedEventArgs e)
    {
        //Console.WriteLine("SizeChanged {0}", sender);
        SetMyInitialized((Window) sender, true);
    }

    private static void OnLoaded(object sender, RoutedEventArgs e)
    {
        Trace.WriteLine($"{DateTime.Now:O}: {sender} loaded");
    }

    #endregion

    // ##############################################################################################################################
    // MyInitialized
    // ##############################################################################################################################

    #region MyInitialized

    public static void SetMyInitialized(UIElement element, bool value)
    {
        element.SetValue(MyInitializedProperty, value);
    }

    public static bool GetMyInitialized(UIElement element)
    {
        return (bool) element.GetValue(MyInitializedProperty);
    }

    public static readonly DependencyProperty MyInitializedProperty = DependencyProperty.RegisterAttached("MyInitialized", typeof (bool), typeof (App), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits, OnMyInitializedChanged));

    private static void OnMyInitializedChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs ev)
    {
        if ((bool)ev.NewValue)
        {
            // registering instance handler unbreaks class handlers
            if (dpo is FrameworkElement element)
                element.Loaded += _EmptyRoutedEventHandler;
            if (dpo is FrameworkContentElement contentElement)
                contentElement.Loaded += _EmptyRoutedEventHandler;
        } else
        {
            throw new ArgumentException("Cannot set to false", ev.Property.Name);
        }
        //Console.WriteLine("MyInitialized {0} {1}=>{2}", dpo, ev.OldValue, ev.NewValue);
    }

    private static readonly RoutedEventHandler _EmptyRoutedEventHandler = delegate { };

    #endregion              
}

XAML

<Window x:Class="WpfApp3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp3"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800"
        d:DataContext="{d:DesignInstance local:MainWindow}">
        <Grid>
            <Border Background="Green" VerticalAlignment="Center" HorizontalAlignment="Center" Width="30" Height="20">
                <TextBlock Background="Orange" Text="hello"></TextBlock>
            </Border>
        </Grid>
</Window>

示例控制台输出:

2018-07-31T14:20:52.6052225+02:00: WpfApp3.MainWindow loaded
2018-07-31T14:20:52.6112064+02:00: System.Windows.Controls.Border loaded
2018-07-31T14:20:52.6132008+02:00: System.Windows.Documents.AdornerDecorator loaded
2018-07-31T14:20:52.6141984+02:00: System.Windows.Controls.ContentPresenter loaded
2018-07-31T14:20:52.6141984+02:00: System.Windows.Controls.Grid loaded
2018-07-31T14:20:52.6151966+02:00: System.Windows.Controls.Border loaded
2018-07-31T14:20:52.6161935+02:00: System.Windows.Controls.TextBlock loaded
2018-07-31T14:20:52.6161935+02:00: System.Windows.Documents.AdornerLayer loaded

这个可以正常工作,不过我需要注释掉下面这段代码:throw new ArgumentException("不能设置为false", ev.Property.Name); - Shiasu-sama

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