Изменение сетки ItemsPanel RowDefinitionCollection
Это продолжение для 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());
Согласно Matt Hamilton становится доступен, когда itemsControl1. ItemContainerGenerator.StatusChanged запускает с состоянием GeneratorStatus.ContainersGenerated.
Однако при попытке изменить сетку из обработчика событий возникает исключение "Невозможно изменить" RowDefinitionCollection "в состоянии только для чтения".
Итак, как я могу установить наборы строк и столбцов theGrid до того, как окно будет показано пользователю?
редактировать: я изменяю свойства сетки из itemsControl1.ItemContainerGenerator.StatusChanged обработчик события:
if (itemsControl1.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
return;
itemsControl1.ItemContainerGenerator.StatusChanged -= ItemContainerGeneratorStatusChanged;
SetGridRowsAndColumns(InitialNumberOfRows, InitialMaxNumberOfCols);
Обратите внимание, что SetGridRowsAndColumns (numberOfRows, numberOfCols) работает позже, в ответ на нажатие кнопки.
2 ответа
Вот как вы можете создать матрицу без использования ItemsControl, обратите внимание, что вы относитесь к главному - умению задавать шаблоны для элементов.
Код:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace GridDefs
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
//
this.DataContext = this; // alterantively use RelativeSource
}
public IEnumerable<Item> MyItems
{
get
{
List<Item> items = new List<Item>(3);
items.Add(Item.Create("A1", new Point(0, 0)));
items.Add(Item.Create("B2", new Point(1, 1)));
items.Add(Item.Create("C3", new Point(2, 2)));
return items;
}
}
}
public interface IMatrixItem
{
Point Position { get; }
}
// Model, note - it has to implement INotifyPropertyChanged if
// you want to propagate its changes up to the UI
public class Item: IMatrixItem
{
public static Item Create(string text,
Point position)
{
Item item = new Item();
item.Text = text;
item.Position = position;
return item;
}
public string Text
{
get;
private set;
}
public Point Position
{
get;
private set;
}
}
public class GridEx
{
public static DependencyProperty DimensionProperty =
DependencyProperty.RegisterAttached("Dimension",
typeof(Size),
typeof(GridEx),
new PropertyMetadata(new Size(0, 0),
(o, e) =>
{
GridEx.OnDimensionChanged((Grid)o, (Size)e.NewValue);
}));
public static DependencyProperty PositionProperty =
DependencyProperty.RegisterAttached("Position",
typeof(Point),
typeof(GridEx),
new FrameworkPropertyMetadata(new Point(-1, -1),
(o, e) =>
{
GridEx.OnPostionChanged((UIElement)o, (Point)e.NewValue);
}));
public static DependencyProperty ItemStyleProperty =
DependencyProperty.RegisterAttached("ItemStyle",
typeof(Style),
typeof(GridEx));
public static DependencyProperty ItemsProperty =
DependencyProperty.RegisterAttached("Items",
typeof(IEnumerable<IMatrixItem>),
typeof(GridEx),
new PropertyMetadata((o, e) =>
{
GridEx.OnItemsChanged((Grid)o, (IEnumerable<IMatrixItem>)e.NewValue);
}));
#region "Dimension"
private static void OnDimensionChanged(Grid grid, Size resolution)
{
grid.RowDefinitions.Clear();
grid.ColumnDefinitions.Clear();
for (int i = 0; i < resolution.Width; i++)
{
grid.ColumnDefinitions.Add(new ColumnDefinition());
}
for (int i = 0; i < resolution.Height; i++)
{
grid.RowDefinitions.Add(new RowDefinition());
}
}
public static void SetDimension(Grid grid, Size dimension)
{
grid.SetValue(GridEx.DimensionProperty, dimension);
}
public static Size GetDimension(Grid grid)
{
return (Size)grid.GetValue(GridEx.DimensionProperty);
}
#endregion
#region "Position"
private static void OnPostionChanged(UIElement item, Point position)
{
item.SetValue(Grid.ColumnProperty, Convert.ToInt32(position.X));
item.SetValue(Grid.RowProperty, Convert.ToInt32(position.Y));
}
private static T GetParentOfType<T>(DependencyObject current)
where T : DependencyObject
{
for (DependencyObject parent = VisualTreeHelper.GetParent(current);
parent != null;
parent = VisualTreeHelper.GetParent(parent))
{
T result = parent as T;
if (result != null)
return result;
}
return null;
}
public static void SetPosition(UIElement item, Point position)
{
item.SetValue(GridEx.PositionProperty, position);
}
public static Point GetPosition(UIElement grid)
{
return (Point)grid.GetValue(GridEx.PositionProperty);
}
#endregion
#region "ItemStyle"
public static void SetItemStyle(Grid item, Style style)
{
item.SetValue(GridEx.ItemStyleProperty, style);
}
public static Style GetItemStyle(Grid grid)
{
return (Style)grid.GetValue(GridEx.ItemStyleProperty);
}
#endregion
#region "Items"
private static void OnItemsChanged(Grid grid, IEnumerable<IMatrixItem> items)
{
grid.Children.Clear();
// template
Style style = GetItemStyle(grid);
foreach (IMatrixItem item in items)
{
Control itemControl = new Control();
grid.Children.Add(itemControl);
itemControl.Style = style;
itemControl.DataContext = item;
}
}
public static void SetItems(Grid grid, IEnumerable<IMatrixItem> items)
{
grid.SetValue(GridEx.ItemsProperty, items);
}
public static IEnumerable<IMatrixItem> GetItems(Grid grid)
{
return (IEnumerable<IMatrixItem>)grid.GetValue(GridEx.ItemsProperty);
}
#endregion
}
}
Разметка:
<Window x:Class="GridDefs.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:GridDefs"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style TargetType="Control" x:Key="t">
<Setter Property="local:GridEx.Position" Value="{Binding Position}"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Button Content="{Binding Text}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid local:GridEx.Dimension="3,3"
local:GridEx.ItemStyle="{StaticResource t}"
local:GridEx.Items="{Binding MyItems}">
</Grid>
</Window>
Я бы использовал прикрепленное поведение, а не низкоуровневую настройку ItemsControl.
Если все, что вам нужно, это матричный элемент управления - вы можете рассмотреть возможность использования Grid-строки вместо ItemsControl (с этим мы и столкнулись). ItemsControl - безгранично мощное существо, но иногда это может быть проблемой, чтобы выжать маленькое, но полезное дополнительное будущее благодаря своему звуковому дизайну.
Следующие изменения, которые вы должны будете сделать, используя этот подход: 1. Используйте Dimension и привяжите его к размеру, который вы хотите. 2. Создайте пользовательский шаблон ItemTemplate и добавьте GridEx.Position к его корневому визуальному объекту, привязанному к соответствующему свойству Point.
На этих двух, просто напишите нам, и я обновлю свой ответ с более подробной информацией.
вот класс:
public class GridEx
{
public static DependencyProperty DimensionProperty =
DependencyProperty.Register("Dimension",
typeof(Size),
typeof(Grid),
new PropertyMetadata((o, e) =>
{
GridEx.OnDimensionChanged((Grid)o, (Size)e.NewValue);
}));
public static DependencyProperty PositionProperty =
DependencyProperty.Register("Position",
typeof(Point),
typeof(UIElement),
new PropertyMetadata((o, e) =>
{
GridEx.OnPostionChanged((UIElement)o, (Point)e.NewValue);
}));
private static void OnDimensionChanged(Grid grid, Size resolution)
{
grid.RowDefinitions.Clear();
grid.ColumnDefinitions.Clear();
for (int i = 0; i < resolution.Width; i++)
{
grid.ColumnDefinitions.Add(new ColumnDefinition());
}
for (int i = 0; i < resolution.Height; i++)
{
grid.RowDefinitions.Add(new RowDefinition());
}
}
private static void OnPostionChanged(UIElement item, Point position)
{
Grid.SetColumn(item, Convert.ToInt32((position.X)));
Grid.SetRow(item, Convert.ToInt32(position.Y));
}
public static void SetDimension(Grid grid, Size dimension)
{
grid.SetValue(GridEx.DimensionProperty, dimension);
}
public static Size GetDimension(Grid grid)
{
return (Size)grid.GetValue(GridEx.DimensionProperty);
}
public static void SetPosition(UIElement item, Point position)
{
item.SetValue(GridEx.PositionProperty, position);
}
public static Point GetPosition(Grid grid)
{
return (Point)grid.GetValue(GridEx.PositionProperty);
}
}
И вот как мы это используем:
<Window x:Class="GridDefs.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:GridDefs"
Title="MainWindow" Height="350" Width="525">
<Grid local:GridEx.Dimension="3,3">
<Button local:GridEx.Position="0,0">A</Button>
<Button local:GridEx.Position="1,1">A</Button>
<Button local:GridEx.Position="2,2">A</Button>
</Grid>
</Window>