如何等待 ItemsControl 中容器的生成?
How to await the generation of containers in an ItemsControl?
我有一个 SettingsWindow
,里面有一个带有上下文菜单的音频文件选择器。某些代码在获得 AudioFileSelector
之前访问 MyAudioFileSelector
计算的 属性,因为 AudioFileSelector
就在 [=20= 中某个项目的 DataTemplate
内] 那时候还没有生成它的容器。我尝试使用 Dispatcher.BeginInvoke
和 DispatcherPrority.Loaded
来延迟对 MyAudioFileSelector
的访问,但此时仍未生成项目容器。
访问 MyAudioFileSelector
的代码是在用户选择的数据文件中应用众多设置之一的方法。对于程序数据文件架构中的每个设置,从 Window
的 Loaded
事件处理程序同步调用此方法。
我是异步等待编程的新手,我已阅读 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 但唯一未被接受的答案似乎与我在下面使用的答案相似:
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
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# 注释中,空引用异常。
我想我可以在 MainWindow
中附加一个处理程序到 SettingsWindow
的 ItemsControl
的 ItemContainerGenerator
的 StatusChanged
事件然后继续window的初始化,包括所有设置的加载,但是不知道有没有更orderly/correct的方式
谢谢。
如果您可以在变量名称 MyItemsControl
下的代码隐藏中访问您的 ItemsControl
,那么您可以为 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
}
}
如果您只是想 save/load 文件中的数据,我真的建议不要使用它,因为它们完全不相关。
我有一个 SettingsWindow
,里面有一个带有上下文菜单的音频文件选择器。某些代码在获得 AudioFileSelector
之前访问 MyAudioFileSelector
计算的 属性,因为 AudioFileSelector
就在 [=20= 中某个项目的 DataTemplate
内] 那时候还没有生成它的容器。我尝试使用 Dispatcher.BeginInvoke
和 DispatcherPrority.Loaded
来延迟对 MyAudioFileSelector
的访问,但此时仍未生成项目容器。
访问 MyAudioFileSelector
的代码是在用户选择的数据文件中应用众多设置之一的方法。对于程序数据文件架构中的每个设置,从 Window
的 Loaded
事件处理程序同步调用此方法。
我是异步等待编程的新手,我已阅读 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 但唯一未被接受的答案似乎与我在下面使用的答案相似:
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
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# 注释中,空引用异常。
我想我可以在 MainWindow
中附加一个处理程序到 SettingsWindow
的 ItemsControl
的 ItemContainerGenerator
的 StatusChanged
事件然后继续window的初始化,包括所有设置的加载,但是不知道有没有更orderly/correct的方式
谢谢。
如果您可以在变量名称 MyItemsControl
下的代码隐藏中访问您的 ItemsControl
,那么您可以为 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
}
}
如果您只是想 save/load 文件中的数据,我真的建议不要使用它,因为它们完全不相关。