绑定到ColumnDefinition的宽度

时间:2011-09-05 05:37:10

标签: c# wpf xaml layout grid

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

要构造所有元素,比如文本框等。我使用宽度定义了所有列。堆叠面板中将有几个网格对齐。

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')

所以我需要转换“宽度”,但是我该怎么做呢?为什么默认转换器不能像在XAML中那样执行?

我对WPF很新,可能没有理解每个概念,所以请给我一个提示。

提前致谢 本尼

编辑:

我尝试了第一个答案的提示。它不起作用,因为GridLengthConverter不是来自IValueConverter类型。

所以我编写了一个DoubleToGridLengthConverter,它将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();
           }
        }
    }
}

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

再次感谢 尼

4 个答案:

答案 0 :(得分: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();

这是未经测试的。让我知道它是怎么回事。

答案 1 :(得分:1)

我想我误解了这个问题,我没有意识到你在两个网格之间有约束力。但是,我已经离开了我之前的答案,因为它可能是相关的。值得考虑的是,如果你需要经常在代码中添加列,那么你做错了。但每种情况都不同: - )

无论如何,您的问题的答案是需要转换器。你刚刚绑定了这一行中的错误属性

loopIdColumn.SetBinding(Grid.WidthProperty, loopIdBinding);

将其更改为

loopIdColumn.SetBinding(ColumnDefinition.WidthProperty, loopIdBinding);

这一切都很好。

答案 2 :(得分: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>

答案 3 :(得分:0)

为什么你需要在后面的代码中执行此操作?我怀疑如果你需要一个变量no列,那么你应该使用某种物品控制。你可以这样做:

    <ItemsControl>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal"></StackPanel>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>

您可以使用画布作为itemspaneltemplate,我认为您可以使用网格(我自己没有尝试过网格)。然后使用数据模板调整项目的大小。如果这有用,请告诉我,我可以提供更多详细信息。