绑定到ColumnDefinition的宽度

6

我需要一种用户输入的表格。所以我创建了一个具有定义列和行的网格。 该表格不是用于显示数据,其目的只是为了结构化输入。

为了结构化所有元素(如文本框等),我定义了所有列的宽度。将会有几个网格在堆栈面板中对齐。

XAML中的列定义如下:

<Grid.ColumnDefinitions>
  <ColumnDefinition x:Name="widthLoopID" Width="55" />
</Grid.ColumnDefinition>

我参考了其他网格,比如:

<Grid.ColumnDefinitions>
  <ColumnDefinition Width="{Binding ElementName=widthLoopID, Path=Width}" />
</GridColumnDefinition>

那很好用。

现在是困难的部分。我如何在代码后端做到这一点?

我尝试了以下内容:

// define columns
ColumnDefinition loopIdColumn = new ColumnDefinition();
// setup databinding for columns
Binding loopIdBinding = new Binding();
// set binding for width
loopIdBinding.ElementName = "widthLoopID";
loopIdBinding.Path = new PropertyPath("Width");
loopIdColumn.SetBinding(Grid.WidthProperty, loopIdBinding);
// add definition of column
loopGrid.ColumnDefinitions.Add(loopIdColumn);

但是当执行时,我得到了以下错误:

System.Windows.Data Error: 1 : Cannot create default converter to perform 'one-way' conversions between types 'System.Windows.GridLength' and 'System.Double'. Consider using Converter property of Binding. BindingExpression:Path=Width; DataItem='ColumnDefinition' (HashCode=37457626); target element is 'ColumnDefinition' (HashCode=7871588); target property is 'Width' (type 'Double')

System.Windows.Data Error: 5 : Value produced by BindingExpression is not valid for target property.; Value='55' BindingExpression:Path=Width; DataItem='ColumnDefinition' (HashCode=37457626); target element is 'ColumnDefinition' (HashCode=7871588); target property is 'Width' (type 'Double')

所以我需要转换 “Width” ,但是我该如何做,为什么默认转换器不能像在 XAML 中那样执行?
我对 WPF 还不太了解,可能没有理解每个概念,请给我一点提示。
提前感谢您 Benny
编辑:
我尝试了第一个答案的提示。 它不起作用,因为 GridLengthConverter 不是 IValueConverter 类型。
所以我写了一个双向 GridLength 转 Double 的转换器。
现在值已经被转换了,但结果与代码在 XAML 中执行的结果不同。 我觉得它根本无法从代码中运行。 真奇怪。
这是转换器的代码:
using System;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using System.Globalization;
using System.Diagnostics;

namespace editor
{
   [ValueConversion(typeof(Double), typeof(GridLength))]
   class DoubleToGridLengthConverter : IValueConverter
   {
       public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
       {
           // check whether a value is given
           if (value != null)
           {
              Debug.WriteLine("value is: " + value + " type: " + value.GetType());
              return (Double)((GridLength)(value)).Value;
           }
           else
           {
               throw new ValueUnavailableException();
           }
       }

       public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
       {
           // check whether a value is given
           if (value != null)
           {
               return new GridLength((Double)value);
           }
           else
           {
               throw new ValueUnavailableException();
           }
        }
    }
}

还有什么线索吗?我可以想象其他人可能已经遇到过这个问题。

再次感谢 Benny


此链接将有所帮助LINK - Sampath
你尝试过我更新后的答案里的代码了吗,Benny? - Matt Hamilton
4个回答

2
你很接近正确了,错误已经给出了很大的提示。你需要使用GridLengthConverter将原始类型(System.Double)转换为GridLength类型(这是列宽的定义方式)。

尝试以下代码(未经测试):

loopIdBinding.Converter = new System.Windows.GridLengthConverter();

事实上,这就是您需要做的全部。这将告诉绑定通过转换器运行值以获取所需类型。
编辑
所以,是的,GridLengthConverter不是IValueConverter,因此您无法这样做。相反,您需要创建一个新类来实现IValueConverter,如下所示:
public class GridLengthValueConverter : IValueConverter
{
    GridLengthConverter _converter = new GridLengthConverter();

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return _converter.ConvertFrom(value);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return _converter.ConvertTo(value, targetType);
    }
}

...然后将这个新实例分配为您的转换器:

loopIdBinding.Converter = new System.Windows.GridLengthConverter();

再次强调,这是未经测试的。请告诉我结果如何。


这个不起作用,因为GridLengthConverter不属于IValueConverter类型。 - Benny
@Benny 啊!你说得对!你需要创建一个继承自IValueConverter的类,并使用GridLengthConverter进行转换。我会尽快更新答案。 - Matt Hamilton

1

我想我误解了问题,我没有意识到你正在绑定两个网格之间。然而,我保留了我的先前答案,因为它可能是相关的。如果您需要经常在代码中添加列,那么值得考虑的是,您正在做错事情。不过每种情况都是不同的 :-)

无论如何,您的问题的答案是,您不需要转换器。您只是在此行中绑定到了错误的属性

loopIdColumn.SetBinding(Grid.WidthProperty, loopIdBinding);

只需将其更改为

loopIdColumn.SetBinding(ColumnDefinition.WidthProperty, loopIdBinding);

而且一切都正常工作。


1

如果你只需要在不同的网格之间同步(共享)列大小,你是否已经查看了ColumnDefinition上的SharedSizeGroup属性?听起来这可能会让这个过程变得更容易。

SharedSizeGroup Property MSDN

来自MSDN链接的代码示例:

<StackPanel>
<Grid ShowGridLines="True" Margin="0,0,10,0">
  <Grid.ColumnDefinitions>
    <ColumnDefinition SharedSizeGroup="FirstColumn"/>
    <ColumnDefinition SharedSizeGroup="SecondColumn"/>
  </Grid.ColumnDefinitions>
  <Grid.RowDefinitions>
    <RowDefinition Height="Auto" SharedSizeGroup="FirstRow"/>
  </Grid.RowDefinitions>

    <Rectangle Fill="Silver" Grid.Column="0" Grid.Row="0" Width="200" Height="100"/>
    <Rectangle Fill="Blue" Grid.Column="1" Grid.Row="0" Width="150" Height="100"/>

    <TextBlock Grid.Column="0" Grid.Row="0" FontWeight="Bold">First Column</TextBlock>
    <TextBlock Grid.Column="1" Grid.Row="0" FontWeight="Bold">Second Column</TextBlock>
</Grid>

<Grid ShowGridLines="True">
  <Grid.ColumnDefinitions>
    <ColumnDefinition SharedSizeGroup="FirstColumn"/>
    <ColumnDefinition SharedSizeGroup="SecondColumn"/>
  </Grid.ColumnDefinitions>
  <Grid.RowDefinitions>        
    <RowDefinition Height="Auto" SharedSizeGroup="FirstRow"/>
  </Grid.RowDefinitions>

    <Rectangle Fill="Silver" Grid.Column="0" Grid.Row="0"/>
    <Rectangle Fill="Blue" Grid.Column="1" Grid.Row="0"/>

    <TextBlock Grid.Column="0" Grid.Row="0" FontWeight="Bold">First Column</TextBlock>
    <TextBlock Grid.Column="1" Grid.Row="0" FontWeight="Bold">Second Column</TextBlock>
</Grid>
</StackPanel>

0
为什么需要在代码后台执行此操作?我怀疑如果您需要一个可变列数,则应该使用某种项目控件。您可以使用以下内容之类的东西来实现此操作:
    <ItemsControl>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal"></StackPanel>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>

你可以将画布用作你的项面板模板,我想你也可以使用网格(我自己没有尝试过网格)。然后,你可以使用数据模板来调整项目的大小。如果这对你有用,请让我知道,我可以提供更多细节。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接