如何在 XAML 中构建控件网格? [英] How to build a grid of controls in XAML?

查看:25
本文介绍了如何在 XAML 中构建控件网格?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试根据规范在 WPF 中构建 UI.UI 用于编辑项目集合.每个项目都有一个可编辑的字符串属性,以及 UI 需要显示的可变数量的只读字符串.它可能看起来像这样:

I am trying to build a UI in WPF to a specification. The UI is for editing a collection of items. Each item has an editable string property, and also a variable number of read-only strings which the UI needs to display. It might look something like this:

                 

                                 

或者,根据数据,可能有不同数量的文本标签列:

or, depending on data, might have a different number of text label columns:

                   

                                     

文本列的数量是完全可变的,可以从一到很多"不等. 规范要求列的大小适合最长的条目(它们总是很短),整个事情应该看起来像一个网格.该网格将包含在一个窗口中,水平拉伸文本框以适合该窗口.

The number of text columns is completely variable and can vary from one to "lots". The specification calls for the columns to be sized to fit the longest entry (they are invariably very short), and the whole thing should look like a grid. This grid will be contained in a window, stretching the text box horizontally to fit the window.

重要的是,文本框可以包含多行文本,并且会自动增长以适应文本.如果发生这种情况,需要将下面的行推开.

Importantly, the text boxes can contain multi-line text and will grow automatically to fit the text. The rows below need to be pushed out of the way if that happens.

问题:在 WPF 中这样做的好方法是什么?

Question: what would be a good way of doing this in WPF?

来自 WinForms 背景,我想到了一个 TableLayoutPanel,它由我编写的代码直接填充.但是,我需要在 WPF 中执行此操作.虽然我仍然可以给自己一个 Grid 并在代码中填充它,但我真的更喜欢一种更符合 WPF 中工作方式的方式:即,定义一个 ViewModel,填充它,然后完全用 XAML 描述视图.但是,我想不出在 XAML 中描述这种视图的方法.

Coming from a WinForms background, I am thinking of a TableLayoutPanel, which gets populated directly by code I write. However, I need to do this in WPF. While I could still just get myself a Grid and populate it in code, I would really rather prefer a way that's more in line with how things are done in WPF: namely, define a ViewModel, populate it, and then describe the View entirely in XAML. However, I can't think of a way of describing such a view in XAML.

我最接近使用 MVVM 和 XAML 的方法是使用 ItemsControl 每行一个项目,并使用数据模板,而数据模板又使用另一个 ItemsControl(这次是水平堆叠)为可变数量的标签,后面是文本框.不幸的是,这不能像规范要求的那样以网格模式垂直对齐.

The closest I can get to this using MVVM and XAML is to use an ItemsControl with one item per row, and use a data template which, in turn, uses another ItemsControl (stacked horizontally this time) for the variable number of labels, followed by the text box. Unfortunately, this can't be made to align vertically in a grid pattern like the spec requires.

推荐答案

在代码隐藏中做真的不是 WPFish(wpf way).在这里,我为您提供我的解决方案,看起来不错.

Doing it in the code-behind is really not a WPFish(wpf way). Here I offer you my solution, which looks nice imo.

0) 在开始之前,您需要 GridHelpers.这些确保您可以动态更改行/列.你可以用一点谷歌找到它:

0) Before starting, you need GridHelpers. Those make sure you can have dynamically changing rows/columns. You can find it with a little bit of google:

我该怎么做向 ItemsPanelTemplate 中的 Grid 动态添加 RowDefinition?

在实际实现某些东西之前,您需要稍微重构一下您的程序.您需要新的结构CustomCollection",它将具有:

Before actually implementing something, you need to restructure your program a little. You need new structure "CustomCollection", which will have:

  • RowCount - 有多少行(使用 INotifyPropertyChanged 实现)
  • ColumnCount - 有多少列(使用 INotifyPropertyChanged 实现)
  • ActualItems - 您自己的行/项目"集合(ObservableCollection)

1) 首先创建一个包含 Grid 的 ItemsControl.确保 Grid RowDefinitions/ColumnDefinitions 是动态的.应用 ItemContainerStyle.

1) Start by creating an ItemsControl that holds Grid. Make sure Grid RowDefinitions/ColumnDefinitions are dynamic. Apply ItemContainerStyle.

    <ItemsControl 
      ItemsSource="{Binding Collection.ActualItems, 
        Converter={StaticResource presentationConverter}">
      <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
          <Grid
             local:GridHelpers.RowCount="{Binding Collection.RowCount}"
             local:GridHelpers.StarColumns="{Binding Collection.ColumnCount, 
               Converter={StaticResource subtractOneConverter}"
             local:GridHelpers.ColumnCount="{Binding Collection.ColumnCount}" />
        </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>

剩下要做的唯一一件事:实现presentationConverter,它将您的Viewmodel 演示文稿转换为View 演示文稿.(阅读:http://wpftutorial.net/ValueConverters.html)

The only thing left to do: implement presentationConverter which converts your Viewmodel presentation to View presentation. (Read: http://wpftutorial.net/ValueConverters.html)

转换器应该返回一组项目,其中每个标签"或文本框"都是一个单独的实体.每个实体都应该有 RowIndex 和 ColumnIndex.

The converter should give back a collection of items where each "label" or "textbox" is a seperate entity. Each entity should have RowIndex and ColumnIndex.

这里是实体类:

public class SingleEntity
{
   ..RowIndex property..
   ..ColumnIndex property..
   ..ContentProperty..  <-- This will either hold label string or TextBox binded property.
   ..ContentType..
}

请注意,ContentType 是一个枚举,您将在 ItemsTemplate 中绑定它以决定是否应该创建 TextBox 或 Label.

Note that ContentType is an enum which you will bind against in ItemsTemplate to decide if you should create TextBox or Label.

这似乎是一个相当冗长的解决方案,但实际上它很好,原因如下:

This might seem like a quite lengthy solution, but it actually is nice for few reasons:

  • ViewModel 不知道发生了什么.这纯粹是视图问题.
  • 一切都是动态的.一旦您在 ViewModel 中添加/或删除某些内容(假设一切都已正确实现),您的 ItemsControl 将重新触发转换器并再次绑定.如果不是这种情况,您可以设置 ActualItems=null,然后返回.

如果您有任何问题,请告诉我.

If you have any questions, let me know.

这篇关于如何在 XAML 中构建控件网格?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆