首页 > 解决方案 > 如何在 ItemsControl 中等待容器的生成?

问题描述

我有一个SettingsWindow,其中有一个音频文件选择器,它有一个上下文菜单。一些代码MyAudioFileSelector在获得之前访问计算属性,AudioFileSelector因为AudioFileSelector它只是在当时尚未生成其容器DataTemplate的项的a 内。ItemsControl我试图推迟MyAudioFileSelector使用Dispatcher.BeginInvokewith的访问DispatcherPrority.Loaded,但此时项目容器仍未生成。

访问 的代码MyAudioFileSelector是应用用户选择的数据文件中的许多设置之一的方法。对于程序的数据文件模式中的每个设置,此方法从Window的事件处理程序中同步调用。Loaded

我对 async-await 编程很陌生,我读过这个,但我不确定这对我有什么帮助,我读了这个页面,但我仍然不知道该怎么做。我已阅读内容,但唯一未被接受的答案似乎与我在下面已经使用的类似:

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

XAML 的一部分

InverseBooleanConv只做true, false, 和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处理程序的开头

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

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课堂上

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# 注释中,即空引用异常。

我想我可以在MainWindowa 处理程序中附加到SettingsWindow's ItemsControl'ItemContainerGenerator事件StatusChanged,然后继续初始化窗口,包括加载所有设置,但我想知道是否有更有序/正确的方法。

谢谢你。

标签: c#wpfasync-awaitdispatcheritemcontainergenerator

解决方案


ItemsControl如果您可以在变量 name 下的代码隐藏中访问您的MyItemsControl,那么您可以为事件添加事件处理程序ContainerGenerator StatusChanged

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
    }
}

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


推荐阅读