【问题标题】:How to reference a row/column definition in Grid.Row/Grid.Column?如何在 Grid.Row/Grid.Column 中引用行/列定义?
【发布时间】:2011-09-20 19:47:51
【问题描述】:

这可能很明显...以后如何在同一个 XAML 文件中引用 XAML 元素?

例子:

<Grid.RowDefinitions>
    <RowDefinition Height="661*" Name="someGridRow" />
    <RowDefinition Height="230*" Name="someOtherGridRow"/>
</Grid.RowDefinitions>

然后我在网格内定义各种控件,我想按名称引用这些行,而不是按编号:

<RichTextBox Grid.Row="someGridRow" ... />

因为如果我在许多控件上使用Grid.Row="0",那么当我在第一行之前添加一行时,我必须手动更改所有对Grid.Row="1" 的引用。

编辑

感谢我在 XAML 上阅读的一些答案。

毕竟,显然可以通过名称引用前一个元素:

Grid.Row="{Binding ElementName=someGridRow}"

Grid.Row="{x:Reference someGridRow}"

但这并不能完全解决问题,因为 Grid.Row 需要一个 int,而 someGridRow 不是一个 int,它是一个 System.Windows.Controls.RowDefinition。

所以需要的是与

等效的 XAML
Grid.Row = grid.RowDefinitions.IndexOf(someGridRow)

后面的代码会写成哪个

Grid.SetRow(richTextBox, grid.RowDefinitions.IndexOf(someGridRow))

或将Grid.Row绑定到属性,在对象grid上,该对象的路径为"RowDefinitions.IndexOf",参数为someGridRow

PropertyPath path = new PropertyPath("RowDefinitions.IndexOf", someGridRow);
Binding binding = new Binding() { ElementName = "grid", Path = path };
richTextBox.SetBinding(Grid.RowProperty, binding);

(这实际上在 C# 中不起作用,所以我一定做错了,尽管上面的 Grid.SetRow 确实有效)

XAML 2009 定义 &lt;x:Arguments&gt; 来调用具有参数的构造函数。如果这在 WPF XAML 中有效,那么我想类似的方法会有效吗?

<Grid.Row>
  <Binding ElementName="grid">
    <Binding.Path>
      <PropertyPath>
        <x:Arguments>
          RowDefinitions.IndexOf
          <Binding ElementName="someGridRow"/>
        </x:Arguments>
      </PropertyPath>
    </Binding.Path>
  </Binding>
</Grid.Row>

在 XAML 2009 中,&lt;Binding ElementName="someGridRow"/&gt; 也可以替换为 &lt;x:Reference Name="someGridRow"/&gt;

【问题讨论】:

  • 我认为所有这些都不会起作用,因为属性路径不允许调用像 IndexOf 这样的方法。

标签: wpf xaml reference


【解决方案1】:

对于 lulz:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Markup;
using System.Windows.Controls;
using System.Windows;

namespace Test.MarkupExtensions
{
    class GridDefinitionExtension : MarkupExtension
    {
        public string Name { get; set; }

        public GridDefinitionExtension(string name)
        {
            Name = name;
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            var refExt = new Reference(Name);
            var definition = refExt.ProvideValue(serviceProvider);
            if (definition is DefinitionBase)
            {
                var grid = (definition as FrameworkContentElement).Parent as Grid;
                if (definition is RowDefinition)
                {
                    return grid.RowDefinitions.IndexOf(definition as RowDefinition);
                }
                else
                {
                    return grid.ColumnDefinitions.IndexOf(definition as ColumnDefinition);
                }
            }
            else
            {
                throw new Exception("Found object is neither a RowDefinition nor a ColumnDefinition");
            }
        }
    }
}
<Grid Width="200" Height="200"
      xmlns:me="clr-namespace:Test.MarkupExtensions">
    <Grid.RowDefinitions>
        <RowDefinition Name="row1" />
        <RowDefinition Name="row2" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Name="col1" />
        <ColumnDefinition Name="col2" />
    </Grid.ColumnDefinitions>
    <Border Background="Lime" Grid.Row="{me:GridDefinition row1}" Grid.Column="{me:GridDefinition col1}" />
    <Border Background="Red" Grid.Row="{me:GridDefinition row2}" Grid.Column="{me:GridDefinition col1}" />
    <Border Background="Yellow" Grid.Row="{me:GridDefinition row1}" Grid.Column="{me:GridDefinition col2}" />
    <Border Background="Blue" Grid.Row="{me:GridDefinition row2}" Grid.Column="{me:GridDefinition col2}" />
</Grid>

【讨论】:

  • 通过 MarkupExtensions 解决此限制的好方法。
  • 非常感谢!我根本不知道标记扩展或类型转换器,我现在正在 MSDN 上阅读它们。我想我实际上会使用这种东西(不仅仅是“为 lulz”),它让我更喜欢 XAML...
  • @SemMike:我认为我应该提到这可能会对较大的网格产生一些性能影响,因为Reference 查找行为相当复杂,并且行数是通过IndexOf 检索的取决于实施也可能很慢。这就是为什么我的答案以该行为前缀。
  • @SemMike: 刚看了一下,根据反编译器IndexOf 应该有 O(1),我猜可能不应该过早地考虑优化。
  • 谢谢,我最终可能会使用你的方法,但我现在对 XAML 很好奇,我阅读并编辑了原始问题...
【解决方案2】:

不幸的是,这不起作用。

有问题的附加属性(即:Grid.Row)被网格用来处理自己的布局,它的设计方式,你必须输入数字。

不幸的是,插入行时更改数字在 XAML 开发中很常见。一种选择 - 如果您知道要添加行,您可以添加未使用的额外“零高度”行,然后使用它们。

【讨论】:

  • 谢谢。为什么这么简单的事情不可能?应该实现像Grid.Row = "{x:Name someGridRow}" 这样的东西......
  • @SemMike:我怀疑这是因为它是如何工作的。 Grid 为 Grid.Row 调查其子级,并且不涉及真正的绑定......任何命名都必须在非类型安全标识符上完成,并且可能导致奇怪的问题。
  • 好吧,有了Name="someName",您已经可以在代码隐藏中引用该项目,那么为什么不在 XAML 本身中呢? XAML 中的“类型安全”并不比 C# 中的要差……无论如何这是不可能的,所以这一点没有实际意义,感谢您让我迅速摆脱痛苦!
  • 对不起,我不得不让其他人的回答被接受!不过,在原版 XAML 中这是不可能的(令人惊讶)所以你是对的。
  • @SemMike:是的,没问题。他的标记扩展是解决它的好方法。
【解决方案3】:

获取 Grid.Column 的另一种方法是绑定到 ViewModel,如下所示:

<TextBlock Text="Nummer: " Grid.Column="{Binding Cols.C1}" />

在 ViewModel 中:

public ColumnDefs Cols { get; }

最后是 ColumnWidth 值的定义:

public class ColumnDefs
{
    public int C1 => 0; // or however you retrieve the columns index
    public int C2 => 1;
}

知道,如果您必须插入列(也适用于行),您只需更改 ViewModel 中的索引值。与更改 xaml 中的每个 Grid.Column 属性相比,这种方法应该没有那么详尽。

【讨论】:

    猜你喜欢
    • 2012-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-27
    相关资源
    最近更新 更多