ItemsControl не имеет дочерних элементов во время конструктора MainWindow
Основываясь на ответе на вопрос SO " WPF: размещение элементов коллекции в сетке ", я имею следующее:
<ItemsControl Name="itemsControl1" ItemsSource="{Binding MyItems}"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <Grid Name="theGrid" ShowGridLines="True" /> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemContainerStyle> <Style TargetType="{x:Type FrameworkElement}"> <Setter Property="Grid.Row" Value="{Binding RowIndex}" /> <Setter Property="Grid.Column" Value="{Binding ColumnIndex}" /> </Style> </ItemsControl.ItemContainerStyle> </ItemsControl>
Теперь я хочу установить количество строк и столбцов theGrid в коде:
theGrid.RowDefinitions.Clear();
theGrid.ColumnDefinitions.Clear();
for (uint i = 0; i < theNumberOfRows; i++)
theGrid.RowDefinitions.Add(new RowDefinition());
for (uint i = 0; i < theNumberOfCols; i++)
theGrid.ColumnDefinitions.Add(new ColumnDefinition());
Для этого, конечно, мне нужно найти сетку. Я использовал CrimsonX FindChild в так называемых WPF- вопросах, чтобы найти элементы управления, чтобы сначала найти itemsControl1, а затем, используя его в качестве родителя, найти theGrid.
Grid FindTheGrid()
{
ItemsControl ic = (ItemsControl)this.FindName("itemsControl1");
Grid theGrid = FindChild<Grid>(ic, "theGrid");
}
Это работает при вызове из обработчика события нажатия кнопки. Однако происходит сбой при вызове из конструктора MainWindow, потому что ic's childrenCount равен 0.
int childrenCount = VisualTreeHelper.GetChildrenCount(theParent);
Итак, как я могу установить наборы строк и столбцов theGrid до того, как окно будет показано пользователю?
2 ответа
WPF генерирует "контейнеры" для элементов ItemsControl (обычно DataTemplate) в фоновом режиме, и они не будут доступны сразу.
Единственный способ узнать, когда элементы доступны для использования, - подписаться на событие StatusChanged в свойстве ItemContainerGenerator ItemsControl:
itemsControl1.ItemContainerGenerator.StatusChanged += ic_GeneratorStatusChanged;
... и затем отмените подписку из обработчика событий, поскольку он нужен только для запуска
void ic_GeneratorStatusChanged(object sender, EventArgs e)
{
if (itemsControl1.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
return;
itemsControl1.ItemContainerGenerator.StatusChanged -= ic_GeneratorStatusChanged;
// your items are now generated
}
Конечно, это обходной путь, но это единственный способ узнать, что элементы ItemsControl существуют в визуальном дереве.
Я устал писать RowDefinitions
а также ColumnDefinitions
для моих гридов, поэтому создали некоторые пользовательские свойства DependencyProperties, которые позволяют вам указать количество строк / столбцов в определении сетки.
Код для свойств зависимости можно найти здесь и использовать так:
<Grid local:GridHelpers.RowCount="{Binding RowCount}"
local:GridHelpers.ColumnCount="{Binding ColumnCount}" />
Если он дает ошибку об привязке, измените typeof(Grid)
в определении DependencyProperty для typeof(GridHelpers)
, Моя первая версия вспомогательного класса не допускала привязок, и я не могу вспомнить, какую из них я опубликовал.
редактировать
Вот код, который я использую, который работает, включая правильное обновление интерфейса, когда SomeInt
изменения. Я тестировал с переключением SomeInt
от 2 до 3 при нажатии кнопки
XAML
<Grid ShowGridLines="True"
local:GridProperties.ColumnCount="{Binding SomeInt}"
local:GridProperties.RowCount="{Binding SomeInt}">
<TextBox Text="Test" Grid.Row="0" Grid.Column="0" />
<TextBox Text="Test" Grid.Row="0" Grid.Column="1" />
<TextBox Text="Test" Grid.Row="0" Grid.Column="2" />
<TextBox Text="Test" Grid.Row="1" Grid.Column="0" />
<TextBox Text="Test" Grid.Row="1" Grid.Column="1" />
<TextBox Text="Test" Grid.Row="1" Grid.Column="2" />
<TextBox Text="Test" Grid.Row="2" Grid.Column="0" />
<TextBox Text="Test" Grid.Row="2" Grid.Column="1" />
<TextBox Text="Test" Grid.Row="2" Grid.Column="2" />
</Grid>
DependencyProperty
public class GridProperties
{
#region RowCount Property
/// <summary>
/// Adds the specified number of Rows to RowDefinitions. Default Height is Auto
/// </summary>
public static readonly DependencyProperty RowCountProperty =
DependencyProperty.RegisterAttached("RowCount", typeof(int),
typeof(GridProperties),
new PropertyMetadata(-1, RowCountChanged));
// Get
public static int GetRowCount(DependencyObject obj)
{
return (int)obj.GetValue(RowCountProperty);
}
// Set
public static void SetRowCount(DependencyObject obj, int value)
{
obj.SetValue(RowCountProperty, value);
}
// Change Event - Adds the Rows
public static void RowCountChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
if (!(obj is Grid) || (int)e.NewValue < 0)
return;
Grid grid = (Grid)obj;
grid.RowDefinitions.Clear();
for (int i = 0; i < (int)e.NewValue; i++)
grid.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
//SetStarRows(grid);
}
#endregion
#region ColumnCount Property
/// <summary>
/// Adds the specified number of Columns to ColumnDefinitions. Default Width is Auto
/// </summary>
public static readonly DependencyProperty ColumnCountProperty =
DependencyProperty.RegisterAttached("ColumnCount", typeof(int),
typeof(GridProperties),
new PropertyMetadata(-1, ColumnCountChanged));
// Get
public static int GetColumnCount(DependencyObject obj)
{
return (int)obj.GetValue(ColumnCountProperty);
}
// Set
public static void SetColumnCount(DependencyObject obj, int value)
{
obj.SetValue(ColumnCountProperty, value);
}
// Change Event - Add the Columns
public static void ColumnCountChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
if (!(obj is Grid) || (int)e.NewValue < 0)
return;
Grid grid = (Grid)obj;
grid.ColumnDefinitions.Clear();
for (int i = 0; i < (int)e.NewValue; i++)
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Auto });
// SetStarColumns(grid);
}
#endregion
}
Результат