Как дождаться создания контейнеров в ItemsControl?
У меня есть SettingsWindow
, в нем есть селектор аудиофайлов, у которого есть контекстное меню. Некоторый код обращается кMyAudioFileSelector
вычисляемое свойство, прежде чем оно сможет получить AudioFileSelector
поскольку AudioFileSelector
просто внутри DataTemplate
элемента в ItemsControl
который на тот момент еще не сгенерировал свои контейнеры. Я пытался отложить доступ кMyAudioFileSelector
с помощью Dispatcher.BeginInvoke
с 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# в одном из приведенных выше фрагментов кода, исключения для нулевых ссылок.
Думаю, я мог бы прикрепить MainWindow
обработчик SettingsWindow
с ItemsControl
с ItemContainerGenerator
с StatusChanged
событие, а затем продолжить инициализацию окна, включая загрузку всех настроек, но мне интересно, есть ли более упорядоченный / правильный способ.
Спасибо.
1 ответ
Если у вас есть доступ к вашему ItemsControl
в коде программной части под именем переменной 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
}
}
Я действительно рекомендую не использовать это, если вам нужно просто сохранить / загрузить данные из файла, поскольку они совершенно не связаны.