【问题标题】:How do I layout a form in WPF using grid or other controls for maintainability [closed]如何使用网格或其他控件在 WPF 中布局表单以实现可维护性 [关闭]
【发布时间】:2011-01-24 20:21:41
【问题描述】:

我有一个 WPF 表单,我想在上面放置一个标准表单。每个表单元素都有一个标签,然后是一个控件。很标准的东西。

如果我使用换行面板,可能会导致标签和控件分离,但我希望它们保持在一起。是否有一些 WPF 等效于 <nobr/>

Grid 可以工作,并且允许跨列等,但是我真的很讨厌您在每个控件上指定列和行。这使得重新排序或将内容插入列表非常不方便。

有没有办法让网格使用更多 HTML 样式的列/行,其中项目是它们所在行的子项,以便我可以轻松地重新排序?

是否有其他控件可以让我轻松布局表单?

【问题讨论】:

    标签: wpf forms layout grid


    【解决方案1】:

    试试 UniformGrid 控件。

    【讨论】:

      【解决方案2】:

      您可能正在寻找的是堆栈面板。使用垂直 StackPanel 将允许您一致地排列标签和控件。对于每个标签和控件,您可能需要一个像这样的水平堆栈面板

      <StackPanel Orientation="Vertical">
        <StackPanel Orientation="Horizontal">
           <Label Width="150">Name</Label>
           <TextBox Width="200" />
        </StackPanel>
        <StackPanel Orientation="Horizontal">
           <Label Width="150">Date of Birth</Label>
           <DatePicker Width="200" />
        </StackPanel>
      </StackPanel>
      

      现在您可以根据自己的喜好添加、删除、编辑和重新排序,而不必担心列和行。

      【讨论】:

      • 可能是为了更好的语法,把宽度放在 StackPanel.Resources 中。
      【解决方案3】:

      是否有一些 WPF 等价于 nobr?

      记住你可以嵌套面板:

      <WrapPanel Orientation="Horizontal">
         <StackPanel Orientation="Horizontal">
            <Label>Some field</Label>
            <TextBox>Some value</TextBox>
         </StackPanel>
         <StackPanel Orientation="Horizontal">
            <Label>Another field</Label>
            <TextBox>Another value</TextBox>
         </StackPanel>
         ...
      </WrapPanel>
      

      此外,对于列式布局,Grid 的共享大小范围可以协调使用它的任意数量的网格:

      <StackPanel Orientation="Vertical" Grid.IsSharedSizeScope="True">
         <Grid>
            <Grid.ColumnDefinitions>
               <ColumnDefinition Width="Auto" SharedSizeGroup="Label"/>
               <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Label Grid.Column="0">Some field</Label>
            <TextBox Grid.Column="1">Some value</TextBox>
         </Grid>
         <Grid>
            <Grid.ColumnDefinitions>
               <ColumnDefinition Width="Auto" SharedSizeGroup="Label"/>
               <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Label Grid.Column="0">Another field</Label>
            <TextBox Grid.Column="1">Another value</TextBox>
         </Grid>
      </StackPanel>
      

      我有点讨厌 XAML 的冗长,尤其是您必须重复列定义。虽然如果你正确地构建你的类并使用模板,它就不会那么糟糕了。请注意,您不会在此方案中的任何位置跟踪行号,因此重新排序字段很简单。

      【讨论】:

      • 不错的解决方案,但您需要多少 xaml 是粗鲁的。还有性能呢?
      【解决方案4】:

      如果你同意的话,如果你打算做很多 UI 设计,我推荐 Expression Blend。它允许更简单地查看项目。将控件嵌套到各种容器中是使 UI 动态且结构化的好方法。

      通常我会使用网格面板将窗口分成功能区域。然后我将使用一系列 StackPanel(通常是一个垂直的堆栈面板,里面有水平的 StackPanel,每个都有一个标签和文本框)。

      不幸的是,网格只能按照您所说的那样工作。其中的元素指定了它们所在的行和/或列。如果您使用 Blend,添加 Grid Columns 或 Rows 将有控件自动神奇地更改行/列规范以保持它们所在的位置。

      希望对你有帮助。

      Expression Blend Trial 在微软。

      更新:

      VS2012 在 WPF 设计器中嵌入了很多 Expression Blend 功能。由于开发人员可以使用 Blend 提供的许多很酷的工具,因此不再需要 Blend 副本。

      【讨论】:

        【解决方案5】:

        看看卡尔的东西。

        http://web.archive.org/web/20150620104259/https://karlshifflett.wordpress.com/2008/10/23/wpf-silverlight-lob-form-layout-searching-for-a-better-solution/

        简单干净的xaml:

        <pt:Form x:Name="formMain" Style="{DynamicResource standardForm}" Grid.Row="1">
          <TextBox pt:FormItem.LabelContent="_First Name" />
          <TextBox pt:FormItem.LabelContent="_Last Name"  />
          <TextBox pt:FormItem.LabelContent="_Phone" Width="150" HorizontalAlignment="Left" />
          <CheckBox pt:FormItem.LabelContent="Is _Active" />    
        </pt:Form>
        

        【讨论】:

        • 漂亮的代码。但该控件是用 VB.NET 编写的。你知道同样的,但在 C# 中?
        • 上面的网站好像已经不存在了。
        • @AlexKlaus:如果它被编译成程序集并且你想按原样使用控件,那么混合 VB.NET 和 C# 有什么问题?
        【解决方案6】:

        在我们的产品中,我们使用 HeaderedContentControl 在网格中布置表单。控件模板具有标签和填充/边距,以便控件的内容始终保持适当的间距。在 XAML 中,我们只是将它们添加到列中。

        我会发布一些 XAML,但我正在设置新计算机。 :|但据我记忆,它看起来像这样:

        <Style x:Key="hccFormStyle" Targettype="{x:Type HeaderedContentControl}>
        ... some setters for colors, margin, padding, etc...
        <Setter Property="Template">
        <Setter.Value>
        <ControlTemplate>
          <Label Content={Binding Content} Target={Binding Tag}> <-- pass the control for the access key with the tag
          <ContentPresenter>
        </ControlTemplate>
        ...triggers if necessary - hover states, etc...
        </style>
        

        然后在网格中定义行和列,并将其中一个放在每个单元格中,或者放在每一行下方:

        <HeaderedContentControl x:Name="username" Grid.Column=0 Content="User Name" Tag=textboxUserName>
         <Textbox x:Name=textboxUserName>
        </HeaderedContentControl>
        

        我可能会回答一个不同的问题,但这就是我们布置表单的方式。

        【讨论】:

          【解决方案7】:

          我遇到了同样的问题,在基于网格的布局中重新排序控件真的很痛苦。

          所以我编写了一个自定义面板来执行“表单布局”(两列组、所有标签相同大小、所有控件相同大小、所有内容对齐等),它在我的博客上:http://www.nbdtech.com/Blog/archive/2010/07/27/easy-form-layout-in-wpf-part-1-ndash-introducing-formpanel.aspx

          【讨论】:

            【解决方案8】:

            Here is a library for it

            示例 xaml:

            <UserControl ...
                         xmlns:autoRowGrid="http://gu.se/AutoRowGrid"
                         ...>
                <autoRowGrid:Grid ColumnDefinitions="Auto *">
                    <autoRowGrid:Row Name="first row">
                        <TextBlock Text="foo1" />
                        <TextBox Text="{Binding Value1}" />
                    </autoRowGrid:Row>
            
                    <autoRowGrid:Row Name="second row">
                        <TextBlock Text="foo2" />
                        <TextBox Text="{Binding Value2}" />
                    </autoRowGrid:Row>
                </autoRowGrid:Grid>
                ...
            

            这是一个标记扩展,它返回一个普通的 WPF Grid 以获得漂亮的浅层可视化树。

            【讨论】:

              【解决方案9】:

              我今天遇到这个帖子时遇到了同样的问题,使用这个帖子中的答案,我想出了一个简单的文本/文本对的可管理解决方案。要添加新字段,只需展开“FormItems”集合即可。

              xmlns:c="clr-namespace:System.Collections;assembly=mscorlib"

              <Window.Resources>
                  <c:ArrayList x:Key="FormItems">
                      <c:DictionaryEntry Key="First        Name" Value="John"/>
                      <c:DictionaryEntry Key="Last Name" Value="Smith"/>
                  </c:ArrayList>
              </Window.Resources>
              
              <ItemsControl ItemsSource="{StaticResource FormItems}" Grid.IsSharedSizeScope="True">
                  <ItemsControl.ItemTemplate>
                      <DataTemplate>
                          <Grid>
                              <Grid.ColumnDefinitions>
                                  <ColumnDefinition Width="Auto" SharedSizeGroup="Label"/>
                                  <ColumnDefinition/>
                              </Grid.ColumnDefinitions>
                              <TextBlock>
                                  <Run Text="{Binding Key}"/><Run Text=": "/>
                              </TextBlock>
                              <TextBox Grid.Column="1" Text="{Binding Value}"/>
                          </Grid>
                      </DataTemplate>
                  </ItemsControl.ItemTemplate>
              </ItemsControl>
              

              没有考虑边距和内边距,它只是展示了使用DataTemplate 来重用每个项目的布局的概念。它可以很容易地适应包括其他数据类型和控件。您甚至可以使用 ItemTemplateSelector 根据 DictionaryEntry.Value 的类型选择不同的模板

              编辑

              我发现使 DataBinding 更容易的另一种方法是使用 Visual Studio 创建一个新的 WPF“自定义控件”。这样做将创建一个名为 Themes/Generic.xaml 的新文件,其中定义了新的默认布局。一些简单的编辑,我们可以使用 ItemsControl 来显示我们的新控件。

              新课程:

              public class FormControlItem : ContentControl
              {
                  public object Field {
                      get { return base.GetValue(FieldProperty); }
                      set { base.SetValue(FieldProperty, value); }
                  }
              
                  static FormControlItem() {
                      DefaultStyleKeyProperty.OverrideMetadata(
                          typeof(FormControlItem), 
                          new FrameworkPropertyMetadata(typeof(FormControlItem)));
                  }
              
                  public static readonly DependencyProperty FieldProperty =
                      DependencyProperty.Register(
                          "Field",
                          typeof(object),
                          typeof(FormControlItem),
                          new FrameworkPropertyMetadata());
              }
              

              主题/Generic.xaml:

              <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                                  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                                  xmlns:local="clr-namespace:MyApplication">
              
              
                  <Style TargetType="{x:Type local:FormControlItem}">
                      <Setter Property="Template">
                          <Setter.Value>
                              <ControlTemplate TargetType="{x:Type local:FormControlItem}">
                                  <Border Background="{TemplateBinding Background}"
                                          BorderBrush="{TemplateBinding BorderBrush}"
                                          BorderThickness="{TemplateBinding BorderThickness}">
                                      <Grid>
                                          <Grid.ColumnDefinitions>
                                              <ColumnDefinition Width="Auto" SharedSizeGroup="Label"/>
                                              <ColumnDefinition/>
                                          </Grid.ColumnDefinitions>
                                          <ContentPresenter ContentSource="Field"/>
                                          <ContentPresenter Grid.Column="1" ContentSource="Content"/>
                                      </Grid>
                                  </Border>
                              </ControlTemplate>
                          </Setter.Value>
                      </Setter>
                  </Style>
              
              </ResourceDictionary>
              

              用法示例:

              <ItemsControl Grid.IsSharedSizeScope="True">
                  <local:FormControlItem Field="Name: ">
                      <TextBox Text="{Binding Path=Name}"/>
                  </local:FormControlItem>
                  <local:FormControlItem Field="Type: ">
                      <ComboBox 
                          SelectedItem="{Binding Path=Type}"
                          ItemsSource="{Binding Path=TypeValues}"/>
                  </local:FormControlItem>
                  <local:FormControlItem Field="Category: ">
                      <TextBox Text="{Binding Path=Category}"/>
                  </local:FormControlItem>
              </ItemsControl>
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2011-06-14
                • 2012-05-29
                • 1970-01-01
                • 1970-01-01
                • 2021-08-03
                • 2022-12-03
                • 1970-01-01
                相关资源
                最近更新 更多