Как создать многоразовую схему сетки WPF
У меня есть окно с контролем вкладок и количеством страниц - вкладками. Каждый элемент табуляции имеет одинаковый макет сетки - 6 строк и 4 столбца. Теперь каждый элемент табуляции содержит сетку с определениями строк и столбцов, поэтому почти половина XAML - это определение сеток.
Как я могу определить эту сетку в одном месте и повторно использовать это определение в своем приложении? Шаблон? Пользовательский контроль?
Помимо 6x4, у меня есть только два дополнительных размера сетки, которые повторяются: 8x4 и 6x6.
Edit:
Забытые упоминания: элементы управления в сетке различны для каждой вкладки. Я просто хочу, чтобы сетка определялась один раз в некотором ресурсе, чтобы я мог повторно использовать их на разных вкладках. Теперь XAML выглядит так:
<TabControl>
<TabItem Header="Property">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<!-- some controls here -->
</Grid>
</TabItem>
<TabItem Header="Style">
<Grid >
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<!-- some controls here -->
</Grid>
</TabItem>
... and this repeats for several more tab items
</TabControl>
Это определение сетки повторяется для каждого элемента табуляции в форме. Меня раздражает, что половина XAML - это определение сетки.
Есть ли способ определить эту сетку в одном месте и затем повторно использовать это определение?
Ответы
Ответ 1
Лучшим способом, на мой взгляд, было бы использовать ItemsControl
с ItemsPanelTemplate
, так как вам нужен контейнер для нескольких элементов:
<FrameworkElement.Resources>
<Style x:Key="GridItemsStyle"
TargetType="ItemsControl">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
</FrameworkElement.Resources>
<TabControl>
<TabItem>
<ItemsControl Style="{StaticResource GridItemsStyle}">
<TextBlock Grid.Row="1" Text="R1" />
<TextBlock Grid.Column="1"
Text="C1" />
</ItemsControl>
</TabItem>
<TabItem>
<ItemsControl Style="{StaticResource GridItemsStyle}">
<TextBlock Grid.Row="2"
Text="R2" />
<TextBlock Grid.Column="2"
Text="C2" />
</ItemsControl>
</TabItem>
</TabControl>
Ответ 2
Или вы можете просто наследовать из Grid...
using System.Windows.Controls;
public class AlreadySetupGrid:Grid
{
public AlreadySetupGrid()
{
for (int i = 0; i < 4; i++)
{
ColumnDefinitions.Add(new ColumnDefinition());
}
for (int i = 0; i < 6; i++)
{
RowDefinitions.Add(new RowDefinition());
}
}
}
И затем используйте это вместо обычной Сетки.
Ответ 3
У меня было что-то похожее. вопрос в том, как вы не хотите помещать свои данные в решетки?
Поскольку вы используете один и тот же макет снова и снова, я думаю, вы помещаете похожие вещи в каждую ячейку.
Я создал пользовательский элемент ItemsControl для каждой вкладки, чтобы поместить данные в, а затем создал стиль для ItemsControl, который показал сетку.
<Style x:Key="GridItemsStyle"
TargetType="ItemsControl">
<Setter Property="ItemsPanel">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ContentPresenter Content="{Binding ElementName=Cell00}" Grid.Row="0" Grid.Column="0" />
<ContentPresenter Content="{Binding ElementName=Cell01}" Grid.Row="0" Grid.Column="1" />
<ContentPresenter Content="{Binding ElementName=Cell10}" Grid.Row="1" Grid.Column="0" />
<!-- ...And so on -->
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
и в окне
<TabControl>
<TabItem>
<local:tab1 Style="{StaticResource GridItemsStyle}" />
</TabItem>
<TabItem>
<local:tab2 Style="{StaticResource GridItemsStyle}" />
</TabItem>
то каждый CustomControl, наследующий от ItemsControl
<ItemsControl x:Class="your_app.tab1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:your_app">
<ContentControl x:Name="Cell00">
<!-- Some stuff here -->
</ContentControl>
<ContentControl x:Name="Cell01">
<!-- Some stuff here -->
</ContentControl>
<ContentControl x:Name="Cell10">
<!-- Some stuff here -->
</ContentControl>
Это очень похоже на то, что делает Aelij, за исключением того, что я установил ContentPresenter и привязал его к имени и поместил itemsControl в свою собственную вещь (вы можете смешивать оба решения).
Это все еще много кода, но я бы сказал, что вы все время сохраняете все определения строк и столбцов, и вам также нужно менять сетку только в одном месте, если вам нужно немного ее изменить.
Ответ 4
Обычно можно записать DataTemplate для данных, которые входят в вкладки. Этот DataTemplate будет содержать Grid.
Ответ 5
Как я могу определить эту сетку в одном месте и повторно использовать это определение в своем приложении? Шаблон? Пользовательский контроль?
Если бы я был вами, я бы создал UserControl
или пользовательский Control
с повторяющимся кодом, а затем просто использовал этот элемент управления из текущего кода.
Другим подходом было бы использовать DataTemplate
или ControlTemplate
.
Создание ControlTemplate
для TabItem
также отлично работает.
Ответ 6
Вы можете сделать это, но это требует некоторой работы:
1) Создайте коллекцию и вложенное свойство, подобное этому:
public class ColumnDefinitions : Collection<ColumnDefinition>
{
public static readonly DependencyProperty SourceProperty = DependencyProperty.RegisterAttached(
"Source",
typeof(ColumnDefinitions),
typeof(ColumnDefinitions),
new PropertyMetadata(
default(ColumnDefinitions),
OnColumnDefinitionsChanged));
public static void SetSource(Grid element, ColumnDefinitions value)
{
element.SetValue(SourceProperty, value);
}
[AttachedPropertyBrowsableForChildren(IncludeDescendants = false)]
[AttachedPropertyBrowsableForType(typeof(Grid))]
public static ColumnDefinitions GetSource(Grid element)
{
return (ColumnDefinitions)element.GetValue(SourceProperty);
}
private static void OnColumnDefinitionsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var grid = (Grid)d;
grid.ColumnDefinitions.Clear();
var columnDefinitions = (ColumnDefinitions)e.NewValue;
if (columnDefinitions == null)
{
return;
}
foreach (var columnDefinition in columnDefinitions)
{
grid.ColumnDefinitions.Add(columnDefinition);
}
}
}
2) Затем вы можете использовать его как ресурс и в стиле для сетки:
Обратите внимание, что необходимо использовать x:Shared="False"
. Если не все то же определение будет добавлено ко многим сеткам, заставляющим WPF бросать.
<UserControl.Resources>
<demo:ColumnDefinitions x:Key="SomeColumnDefinitions" x:Shared="False">
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</demo:ColumnDefinitions>
<Style x:Key="SomeGridStyle" TargetType="{x:Type Grid}">
<Setter Property="demo:ColumnDefinitions.Source" Value="{StaticResource SomeColumnDefinitions}"></Setter>
</Style>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="5"/>
<RowDefinition />
</Grid.RowDefinitions>
<Grid Style="{StaticResource SomeGridStyle}">
<Rectangle Grid.Row="0"
Grid.Column="0"
Width="120"
Fill="Blue" />
<Rectangle Grid.Row="0"
Grid.Column="1"
Fill="Yellow" />
</Grid>
<Grid Grid.Row="2" demo:ColumnDefinitions.Source="{StaticResource SomeColumnDefinitions}">
<Rectangle Grid.Row="0"
Grid.Column="0"
Width="120"
Fill="Blue" />
<Rectangle Grid.Row="0"
Grid.Column="1"
Fill="Yellow" />
</Grid>
</Grid>