开发带集合属性的DataGridView控件:绑定失效问题求助
自定义DataGridView列Width绑定失效的解决方法
问题描述
我正在开发一个简易DataGridView控件,遇到绑定失效问题:列集合不在可视化树中,且DataGridViewTextBlockColumn仅继承自DependencyObject而非FrameworkElement,导致Width绑定无法生效。
相关代码
XAML代码
<UserControl Name="UC"> <DataGridView ItemsSource="{Binding Path=GridItems}"> <DataGridView.Columns> <DataGridViewTextBlockColumn Header="Name" Width="{Binding ElementName=UC, Path=DataContext.GridWidth}" /> <DataGridViewTextBlockColumn Header="Count" Width="{Binding ElementName=UC, Path=DataContext.GridWidth}" /> </DataGridView.Columns> </DataGridView>
C#代码
public class DataGridView : ListBox { ... public ObservableCollection<DataGridViewTextBlockColumn> Columns { get; } = new(); ... }
核心原因
列对象(DataGridViewTextBlockColumn)不在可视化树中,且未继承FrameworkElement/Freezable,无法自动获取数据上下文,常规的ElementName绑定找不到目标对象,导致绑定失效。
解决办法
1. 修改列类继承FrameworkElement(最简方案)
如果允许修改列的基类,直接让DataGridViewTextBlockColumn继承FrameworkElement,这样它就能纳入可视化树的上下文体系,原XAML中的ElementName绑定会自动生效:
public class DataGridViewTextBlockColumn : FrameworkElement { // 定义Width依赖属性(绑定依赖属性才能生效) public static readonly DependencyProperty WidthProperty = DependencyProperty.Register(nameof(Width), typeof(double), typeof(DataGridViewTextBlockColumn), new PropertyMetadata(100.0)); public double Width { get => (double)GetValue(WidthProperty); set => SetValue(WidthProperty, value); } // 保留Header等其他列属性 public string Header { get; set; } }
2. 手动通过BindingOperations设置绑定(不改动基类)
如果不能修改列的基类,就在DataGridView内部监听列集合变化和控件加载事件,手动为每一列的Width属性绑定数据:
首先确保DataGridViewTextBlockColumn的Width是依赖属性:
public class DataGridViewTextBlockColumn : DependencyObject { public static readonly DependencyProperty WidthProperty = DependencyProperty.Register(nameof(Width), typeof(double), typeof(DataGridViewTextBlockColumn), new PropertyMetadata(100.0)); public double Width { get => (double)GetValue(WidthProperty); set => SetValue(WidthProperty, value); } public string Header { get; set; } }
然后在DataGridView类中添加绑定逻辑:
public class DataGridView : ListBox { public ObservableCollection<DataGridViewTextBlockColumn> Columns { get; } = new(); public DataGridView() { Columns.CollectionChanged += Columns_CollectionChanged; Loaded += DataGridView_Loaded; } private void DataGridView_Loaded(object sender, RoutedEventArgs e) { UpdateColumnWidthBindings(); } private void Columns_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { UpdateColumnWidthBindings(); } private void UpdateColumnWidthBindings() { // 构建绑定,关联DataContext中的GridWidth属性 var widthBinding = new Binding { Source = this.DataContext, Path = new PropertyPath("GridWidth") }; foreach (var column in Columns) { BindingOperations.SetBinding(column, DataGridViewTextBlockColumn.WidthProperty, widthBinding); } } // 原有控件逻辑... }
3. 让列类继承Freezable(进阶上下文传递)
Freezable类型无需进入可视化树,就能自动继承父元素的DataContext,是WPF中传递上下文的常用技巧:
public class DataGridViewTextBlockColumn : Freezable { // Freezable必须重写该方法 protected override Freezable CreateInstanceCore() { return new DataGridViewTextBlockColumn(); } public static readonly DependencyProperty WidthProperty = DependencyProperty.Register(nameof(Width), typeof(double), typeof(DataGridViewTextBlockColumn), new PropertyMetadata(100.0)); public double Width { get => (double)GetValue(WidthProperty); set => SetValue(WidthProperty, value); } public string Header { get; set; } }
此时XAML中的绑定可以简化,无需ElementName,直接绑定DataContext属性:
<DataGridView ItemsSource="{Binding Path=GridItems}"> <DataGridView.Columns> <DataGridViewTextBlockColumn Header="Name" Width="{Binding GridWidth}" /> <DataGridViewTextBlockColumn Header="Count" Width="{Binding GridWidth}" /> </DataGridView.Columns> </DataGridView>
内容的提问来源于stack exchange,提问作者user2746029
相关产品推荐
相关产品推荐

