【问题标题】:How to create reusable WPF grid layout如何创建可重用的 WPF 网格布局
【发布时间】:2011-02-22 10:52:57
【问题描述】:

我有一个带有选项卡控件和页数的窗口 - 选项卡项。每个选项卡项具有相同的网格布局 - 6 行和 4 列。现在,每个选项卡项都包含带有行和列定义的网格,因此几乎一半的 XAML 是网格定义。

如何在一个地方定义此网格并在我的应用程序中重复使用该定义?模板?用户控制?

除了 6x4,我只有两个重复的网格尺寸:8x4 和 6x6。

编辑:
忘了提一下:每个选项卡的网格控件都不同。我只想在某些资源中定义一次网格,以便我可以在不同的标签页上重用它们。现在 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 的一半是网格定义。

有没有办法在一个地方定义这个网格,然后重复使用这个定义?

【问题讨论】:

    标签: wpf layout grid


    【解决方案1】:

    我认为最好的方法是使用ItemsControlItemsPanelTemplate,因为您需要一个容器来容纳多个项目:

    <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 将包含网格。

      【讨论】:

        【解决方案5】:

        如何在一个地方定义此网格并在我的应用程序中重复使用该定义?模板?用户控制?

        如果我是你,我会使用重复的代码创建一个 UserControl 或自定义 Control,然后从当前代码中使用该控件。

        另一种方法是使用DataTemplateControlTemplate

        TabItems 创建一个ControlTemplate 也很有效。

        【讨论】:

          【解决方案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>
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-07-16
            • 2011-11-17
            • 2022-01-23
            • 2019-02-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-12-06
            相关资源
            最近更新 更多