如何等待ItemsControl中容器的生成? [英] How to await the generation of containers in an ItemsControl?

查看:76
本文介绍了如何等待ItemsControl中容器的生成?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个SettingsWindow,其中有一个带有上下文菜单的音频文件选择器.某些代码在获取AudioFileSelector之前先访问MyAudioFileSelector计算属性,因为AudioFileSelector恰好在ItemsControl中某个项目的DataTemplate内部,该项目当时尚未生成其容器.我曾尝试使用Dispatcher.BeginInvokeDispatcherPrority.Loaded推迟对MyAudioFileSelector的访问,但当时仍未生成项目容器.

I have a SettingsWindow, in it there is an audio file selector which has a context menu. Some code accesses the MyAudioFileSelector computed property before it can get the AudioFileSelector because the AudioFileSelector is just inside a DataTemplate of an item in an ItemsControl that has not yet generated its containers at that moment. I tried to defer the access to MyAudioFileSelector using Dispatcher.BeginInvoke with DispatcherPrority.Loaded, but the item containers are still not yet generated at that moment.

访问MyAudioFileSelector的代码是在用户选择的数据文件中应用许多设置之一的方法.从WindowLoaded事件处理程序中为该程序的数据文件的架构中的每个设置同步调用此方法.

The code that accesses the MyAudioFileSelector is the method that applies one of the many settings inside the user-selected data file. This method is called from the Window's Loaded event handler synchronously for each setting in the program's data files' schema.

我对异步等待编程非常陌生,我已阅读,但是我不确定这对我有什么帮助,并且阅读了此页面,但是我仍然不确定该怎么办.我已阅读 a,但是唯一的答案(不被接受)似乎类似于我在下面已经使用的答案:

I am very new to async-await programming, I have read this but I am not sure how this helps me, and I read this page but I am still not sure what to do. I have read this a but the only answer, unaccepted, seems similar to what I already use below:

MySettingsWindow.Dispatcher.BeginInvoke(new Action(() =>
{
    [...]
}), System.Windows.Threading.DispatcherPriority.Loaded);

XAML的一部分

(InverseBooleanConv只会生成truefalsefalsetrue)

A part of the XAML

(InverseBooleanConv just makes true, false, and false, true)

<ItemsControl Grid.ColumnSpan="3" Margin="0,0,-0.6,0" Grid.Row="0"
ItemsSource="{Binding SettingsVMs}" x:Name="MyItemsControl">
    <ItemsControl.Resources>
        <xceed:InverseBoolConverter x:Key="InverseBooleanConv"/>
        <DataTemplate DataType="{x:Type local:AudioFileSettingDataVM}">
            <local:AudioFileSelector MaxHeight="25" Margin="10" FilePath="{Binding EditedValue, Mode=TwoWay}">
                <local:AudioFileSelector.RecentAudioFilesContextMenu>
                    <local:RecentAudioFilesContextMenu
                        PathValidationRequested="RecentAudioFilesContextMenu_PathValidationRequested"
                        StoragePropertyName="RecentAudioFilePaths"
                        EmptyLabel="No recent audio files."/>
                </local:AudioFileSelector.RecentAudioFilesContextMenu>
            </local:AudioFileSelector>
        </DataTemplate>
        [...]

部分代码

在MainWindow.xaml.cs中,是Window_Loaded处理程序的开头

Parts of the code-behind

In MainWindow.xaml.cs, the beginning of the Window_Loaded handler

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    VM.ClockVMCollection.Model.FiltersVM.Init();
    VM.Settings.IsUnsavedLocked = true;
    VM.ClockVMCollection.Model.IsUnsavedLocked = true;
    foreach (KeyValuePair<string, SettingDataM> k in VM.Settings)
    {
        ApplySetting(k.Value);
    }
    [...]

在MainWindow.xaml.cs中,在方法ApplySetting

In MainWindow.xaml.cs, in the method ApplySetting

case "AlwaysMute":
    VM.MultiAudioPlayer.Mute = (bool)VM.Settings.GetValue("AlwaysMute");
    break;
case "RecentAudioFilePaths":
    MySettingsWindow.Dispatcher.BeginInvoke(new Action(() =>
    {
        MySettingsWindow.MyRecentAudioFilesContextMenu. // here, MyRecentAudioFilesContextMenu is null, this is the problem
            LoadRecentPathsFromString(VM.Settings.GetValue("RecentAudioFilePaths") as string);
    }), System.Windows.Threading.DispatcherPriority.Loaded);
    break;
case "RecentImageFilePaths":
    MySettingsWindow.Dispatcher.BeginInvoke(new Action(() =>
    {
        MySettingsWindow.MyRecentImageFilesContextMenu. // here, MyRecentImageFilesContextMenu is null, this is the problem
            LoadRecentPathsFromString(
                VM.Settings.GetValue("RecentImageFilePaths") as string);
    }), System.Windows.Threading.DispatcherPriority.Loaded);
    break;
    [...]

SettingsWindow类中

In the SettingsWindow class

internal AudioFileSelector MyAudioFileSelector
{
    get
    {
        foreach (SettingDataVM vm in MyItemsControl.ItemsSource)
        {
            if (vm is AudioFileSettingDataVM)
            {
                return (AudioFileSelector)MyItemsControl.ItemContainerGenerator.ContainerFromItem(vm);
            }
        }
        return null;
    }
}
internal ImageFileSelector MyImageFileSelector
{
    get
    {
        foreach (SettingDataVM vm in MyItemsControl.ItemsSource)
        {
            if (vm is ImageFileSettingDataVM)
            {
                return (ImageFileSelector)MyItemsControl.ItemContainerGenerator.ContainerFromItem(vm);
            }
        }
        return null;
    }
}
internal RecentAudioFilesContextMenu MyRecentAudioFilesContextMenu
{
    get
    {
        return MyAudioFileSelector?.RecentAudioFilesContextMenu;
    }
}
internal RecentFilesContextMenu MyRecentImageFilesContextMenu
{
    get
    {
        return MyImageFileSelector?.RecentImageFilesContextMenu;
    }
}

该错误位于上述代码段之一中的两个C#注释中,空引用异常.

The bug is in the two C# comments in one of the code snippets above, null reference exceptions.

我想我可以在MainWindow中将处理程序附加到SettingsWindowItemsControlItemContainerGeneratorStatusChanged事件,然后继续窗口的初始化,包括所有窗口的加载.设置,但我想知道是否还有一种更有序/正确的方法.

I think I could attach in the MainWindow a handler to SettingsWindow's ItemsControl's ItemContainerGenerator's StatusChanged event and then continue the initialization of the window, including the loading of all the settings, but I wonder if there is a more orderly/correct way.

谢谢.

推荐答案

如果可以在变量名MyItemsControl下的代码后方访问ItemsControl,则可以为 StatusChanged事件:

If you have access to your ItemsControl in the code-behind under the variable name MyItemsControl, then you can add an event handler for the ContainerGenerator StatusChanged event:

private void Window_Loaded(object sender, RoutedEventArgs e) {
    //Subscribe to generated containers event of the ItemsControl
    MyItemsControl.ItemContainerGenerator.StatusChanged += ContainerGenerator_StatusChanged;
}

/// <summary>
/// Handles changed in container generator status.
///</summary>
private void ContainerGenerator_StatusChanged(object sender, EventArgs e) {
    var generator = sender as ItemContainerGenerator;
    //Check that containers have been generated
    if (generator.Status == GeneratorStatus.ContainersGenerated ) {
        //Do stuff
    }
}

如果您只是从文件中保存/加载数据,因为它们完全无关,我真的建议不要使用此功能.

I really recommand not to use this if what you're after is simply save/load data from a file, as they are completely unrelated.

这篇关于如何等待ItemsControl中容器的生成?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆