如何等待ItemsControl中容器的生成? [英] How to await the generation of containers in an ItemsControl?
问题描述
我有一个SettingsWindow
,其中有一个带有上下文菜单的音频文件选择器.某些代码在获取AudioFileSelector
之前先访问MyAudioFileSelector
计算属性,因为AudioFileSelector
恰好在ItemsControl
中某个项目的DataTemplate
内部,该项目当时尚未生成其容器.我曾尝试使用Dispatcher.BeginInvoke
和DispatcherPrority.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
的代码是在用户选择的数据文件中应用许多设置之一的方法.从Window
的Loaded
事件处理程序中为该程序的数据文件的架构中的每个设置同步调用此方法.
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
只会生成true
,false
和false
,true
)
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
中将处理程序附加到SettingsWindow
的ItemsControl
的ItemContainerGenerator
的StatusChanged
事件,然后继续窗口的初始化,包括所有窗口的加载.设置,但我想知道是否还有一种更有序/正确的方法.
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屋!