如何使用网格或其他控件在 WPF 中布局表单以实现可维护性
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2399234/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I layout a form in WPF using grid or other controls for maintainability
提问by Jason Coyne
I have a WPF form, I want to lay out a standard form onto it. Each form element will have a label, and then a control. Pretty standard stuff.
我有一个 WPF 表单,我想在它上面布置一个标准表单。每个表单元素都有一个标签,然后是一个控件。很标准的东西。
If I use a wrap panel, it can cause the label and the control to be separated, but I want them to stay together. Is there some WPF equivalent of <nobr/>
?
如果我使用环绕面板,它会导致标签和控件分离,但我希望它们保持在一起。是否有一些 WPF 等价物<nobr/>
?
Grid works, and allows for column spanning etc, however I really really hate that you specify the column and row on each control. This makes it extremely inconvenient to reorder or insert things into the list.
网格可以工作,并允许跨列等,但是我真的很讨厌您在每个控件上指定列和行。这使得在列表中重新排序或插入内容非常不方便。
Is there a way to get the grid to use more HTML style column/rows where the items are a child of the row they are in, so that I can re-order easily?
有没有办法让网格使用更多 HTML 样式的列/行,其中项目是它们所在行的子项,以便我可以轻松重新排序?
Is there some other control that will let me layout a form easily?
是否有其他控件可以让我轻松布局表单?
回答by Robert Rossney
is there some WPF equivalent of nobr?
是否有一些与 nobr 等效的 WPF?
Remember that you can nest panels:
请记住,您可以嵌套面板:
<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>
Also, for columnar layouts, the shared size scope of the Grid can coordinate any number of grids that use it:
此外,对于柱状布局,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>
I kind of hate how verbose the XAML for this is, especially that you have to repeat the column definitions. Though if you structure your classes properly and use templates it's not so terrible. And notice that you aren't keep track of row numbers anywhere in this scheme, so reordering fields is simple.
我有点讨厌 XAML 的冗长,尤其是您必须重复列定义。但是,如果您正确地构建类并使用模板,它就不会那么糟糕。请注意,您不会在此方案中的任何地方跟踪行号,因此重新排序字段很简单。
回答by Michael Brown
What you might be looking for is a stack panel. Using a vertical StackPanel will allow you to arrange your label and controls consistently. For each label and control you might want a horizontal stack panel like so
您可能正在寻找的是堆栈面板。使用垂直 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>
Now you can add, remove, edit, and reorder to your heart's content without worrying about columns and rows.
现在,您可以添加、删除、编辑和重新排列您心中的内容,而无需担心列和行。
回答by Rafa Castaneda
Try the UniformGrid control.
试试 UniformGrid 控件。
回答by Johan Larsson
Sample xaml:
示例 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>
...
It is a markupextension that returns a vanilla WPF Grid
for a nice shallow visual tree.
它是一个标记扩展,它Grid
为一个漂亮的浅层视觉树返回一个普通的 WPF 。
回答by CodeWarrior
If you can accord it, I recommend Expression Blend if you are going to be doing much UI design. It allows a simpler view of the items. Nesting controls into various containers is a good way to get the UI to be dynamic, but structured.
如果您可以接受,如果您要进行大量 UI 设计,我推荐 Expression Blend。它允许更简单地查看项目。将控件嵌套到各种容器中是使 UI 动态但结构化的好方法。
Typically I will use a Grid panel to break a window up into functional areas. Then I will use a series of StackPanels (often a vertical stack panel with horizontal StackPanels inside it, each with a label and textbox).
通常我会使用网格面板将窗口分解为功能区域。然后我将使用一系列 StackPanels(通常是一个垂直的堆栈面板,里面有水平的 StackPanels,每个都有一个标签和文本框)。
Unfortunately Grids only work as you stated. Elements in them specify the row and/or column that they reside in. If you used Blend, adding Grid Columns or Rows will have controls auto-magically change the row/column specification to stay in the position they were placed.
不幸的是,网格只能按您所说的那样工作。其中的元素指定它们所在的行和/或列。如果您使用 Blend,添加网格列或行将使控件自动神奇地更改行/列规范以保持在它们放置的位置。
Hope it helps.
希望能帮助到你。
Expression Blend Trialat Microsoft.
Microsoft 的Expression Blend 试用版。
UPDATE:
更新:
VS2012 has a lot of the Expression Blend functionality baked into the WPF designer. Much of the need for a copy of Blend is no longer there as developers have access to a lot of the cool tools from Blend.
VS2012 有很多嵌入到 WPF 设计器中的 Expression Blend 功能。由于开发人员可以访问 Blend 中的许多很酷的工具,因此不再需要大量的 Blend 副本。
回答by Ray
Check out Karl's stuff.
看看卡尔的东西。
Simple and clean xaml:
简单干净的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>
回答by SilverX
I came across this post today while having the same question, using the answers in this thread I came up with a manageable solution to simple text/text pairs. To add new fields simply expand the "FormItems" collection.
我今天遇到了这篇文章,同时遇到了同样的问题,使用这个线程中的答案,我想出了一个简单的文本/文本对的可管理解决方案。要添加新字段,只需展开“FormItems”集合。
xmlns:c="clr-namespace:System.Collections;assembly=mscorlib"
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>
There has been no consideration for margins and padding, it just shows the concept of using a DataTemplate to reuse the layout of each item. It can be adapted to include other data types and controls fairly easily. You could even use the ItemTemplateSelector to select a different template based on the type of the DictionaryEntry.Value
一直没有考虑margin和padding,它只是展示了使用DataTemplate来重用每个项目的布局的概念。它可以很容易地适应以包含其他数据类型和控件。您甚至可以使用 ItemTemplateSelector 根据 DictionaryEntry.Value 的类型选择不同的模板
EDIT
编辑
Another approach that I found that made it easier for DataBinding was to use Visual Studio to create a new WPF "Custom Control". Doing so will create a new file named Themes/Generic.xaml with a new default layout defined there. A few simple edits and we can use the ItemsControl to display our new control.
我发现使 DataBinding 更容易的另一种方法是使用 Visual Studio 创建一个新的 WPF“自定义控件”。这样做将创建一个名为 Themes/Generic.xaml 的新文件,其中定义了一个新的默认布局。一些简单的编辑,我们可以使用 ItemsControl 来显示我们的新控件。
New class:
新班级:
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());
}
Themes/Generic.xaml:
主题/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>
Usage example:
用法示例:
<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>
回答by dex3703
In our product we use a HeaderedContentControl to lay out forms in a grid. The control template has a label and padding/margins so that the control's content is always spaced appropriately. In the XAML we just add them down the columns.
在我们的产品中,我们使用 HeaderedContentControl 在网格中布置表单。控件模板有一个标签和填充/边距,以便控件的内容始终保持适当的间距。在 XAML 中,我们只是将它们添加到列中。
I'd post some XAML but I'm in the middle of getting a new computer set up. :| But from what I remember it would look something like this:
我会发布一些 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>
Then in the grid, you define your rows and columns, and put one of these in each cell, or just down each row:
然后在网格中,定义行和列,并在每个单元格中放置其中一个,或者在每一行中放置:
<HeaderedContentControl x:Name="username" Grid.Column=0 Content="User Name" Tag=textboxUserName>
<Textbox x:Name=textboxUserName>
</HeaderedContentControl>
I might be answering a different question but this is how we lay out our forms.
我可能会回答一个不同的问题,但这就是我们布置表格的方式。
回答by Nir
I had the same problem, reordering controls in a Grid based layout is a real pain.
我有同样的问题,在基于网格的布局中重新排序控件是一个真正的痛苦。
So I've wrote a custom panel that does "form layout" (groups of two columns, all labels same size, all control same size,everything aligned, etc.), it's on my blog at: http://www.nbdtech.com/Blog/archive/2010/07/27/easy-form-layout-in-wpf-part-1-ndash-introducing-formpanel.aspx
因此,我编写了一个执行“表单布局”的自定义面板(两列组,所有标签大小相同,所有控件大小相同,所有内容均对齐等),它在我的博客上:http://www.nbdtech .com/Blog/archive/2010/07/27/easy-form-layout-in-wpf-part-1-ndash-introducing-formpanel.aspx