如何使用宽度可调的固定列自定义Datagrid控件?

时间:2016-11-08 22:54:52

标签: c# wpf xaml datagrid dependency-properties

我要做的是创建一个已经有2个固定列的CustomDatagrid。然后我可以重新使用这个CustomDatagrid并添加额外的列以满足我的最佳目的。但是当我添加额外的列时,我希望能够调整2个固定列的大小。我尝试使用依赖属性作为下面的示例,但无济于事。而且我没有得到任何任何有约束力的错误,这让我不知道出了什么问题。

=>这个小例子将阐明我想要做的事情

CustomDataGrid.Xaml

<DataGrid x:Class="DataGridWidthTestControl.CustomDataGrid"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:DataGridWidthTestControl"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<DataGrid.Columns>
    <DataGridTextColumn Width="{Binding Column1Width}" Header="Column1"/>
    <DataGridTextColumn Width="{Binding Column2Width}" Header="Column2"/>
</DataGrid.Columns>

CustomDataGrid.Xaml.CS - Codebehind

namespace DataGridWidthTestControl
{
    /// <summary>
    /// Interaction logic for CustomDataGrid.xaml
    /// </summary>
    public partial class CustomDataGrid : DataGrid
    {
        public CustomDataGrid()
        {
            InitializeComponent();
            DataContext = this;
        }

        public static readonly DependencyProperty Column1WidthProperty = DependencyProperty.Register( "Column1Width", typeof(DataGridLength), typeof(CustomDataGrid), new FrameworkPropertyMetadata(new DataGridLength(25, DataGridLengthUnitType.Star)));

        public DataGridLength Column1Width
        {
            get { return (DataGridLength)GetValue(Column1WidthProperty); }
            set { SetValue(Column1WidthProperty, value); }
        }

        public static readonly DependencyProperty Column2WidthProperty = DependencyProperty.Register("Column2Width", typeof(DataGridLength), typeof(CustomDataGrid), new FrameworkPropertyMetadata(new DataGridLength(15, DataGridLengthUnitType.Star)));

        public DataGridLength Column2Width
        {
            get { return (DataGridLength)GetValue(Column2WidthProperty); }
            set { SetValue(Column2WidthProperty, value); }
        }

    }
}

Mainwindow.xaml(除默认初始化调用外,代码隐藏为空)

<Window x:Class="DataGridWidthTestControl.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:DataGridWidthTestControl"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <local:CustomDataGrid Column1Width="1*" Column2Width="1*" >
            <DataGrid.Columns>
                <DataGridTextColumn Width="10*" Header="column3"/>
            </DataGrid.Columns>
        </local:CustomDataGrid>
    </Grid>
</Window>

2 个答案:

答案 0 :(得分:2)

您需要以编程方式在CustomDataGrid类中插入固定列,而不是在模板中指定它们。

类似的东西:

public override void OnApplyTemplate()
{
    if (!this.Columns.Any(c => c.Header.ToString() == "Column1"))
    {
        this.Columns.Insert(0,
                            new DataGridTextColumn
                            {
                                Width = this.Column1Width,
                                Header = "Column1"
                            });
    }

    if (!this.Columns.Any(c => c.Header.ToString() == "Column2"))
    {
        this.Columns.Insert(1,
                            new DataGridTextColumn
                            {
                                Width = this.Column2Width,
                                Header = "Column2"
                            });
    }

    base.OnApplyTemplate();
}

我不确定OnApplyTemplate()是否是正确的时间,可能有更好的方法来覆盖,但这是我努力实现这一目标的概念。

答案 1 :(得分:1)

首先,你需要知道没有指定任何来源的绑定(如你的情况),使用white-space的绑定和使用Binding.RelativeSource的绑定不能用于Binding.ElementName,因为它直接来自DataGridColumn,这是不够的。基本上,为了使那些工作,目标对象需要是从DependencyObjectFrameworkElement派生的类型,它应该是视觉或逻辑树的一部分(FrameworkContentElement s不是)。请注意,该要求的例外情况很少(例如资源字典中定义的DataGridColumn),以及框架即将推出的版本会有更多例外。

很遗憾,您需要明确指定Freezable(应该是您的Binding.Source类的实例)。我无法想到在 XAML中做任何事情CustomDataGrid不适用,因为你无法在其定义中引用某个对象),所以我认为你&#39;我需要回退到代码隐藏(在设计自定义控件时这并不罕见)。

最简单的方法是命名列:

Source={x:Reference (...)}

<DataGrid.Columns> <DataGridTextColumn x:Name="Column1" x:FieldModifier="private" Header="Column1" /> <DataGridTextColumn x:Name="Column2" x:FieldModifier="private" Header="Column2" /> </DataGrid.Columns> 是可选的)然后在控件初始化时设置绑定:

x:FieldModifier="private"

您还可能希望将public CustomDataGrid() { InitializeComponent(); BindingOperations.SetBinding(Column1, DataGridColumn.WidthProperty, new Binding { Path = new PropertyPath(Column1WidthProperty), Source = this, }); BindingOperations.SetBinding(Column2, DataGridColumn.WidthProperty, new Binding { Path = new PropertyPath(Column2WidthProperty), Source = this, }); } 添加到绑定中,以便在用户手动调整列大小时控件上的值保持同步。

请注意,我故意删除了Mode = BindingMode.TwoWay行,因为 a)没有必要和 b)在使用你时会给你带来很多麻烦控制(例如,绑定DataContext = this到视图模型属性将不会像通常那样工作。)