I am trying to bind a datagrid columns width but not working.
<DataGridTextColumn Binding="{Binding Name}" Width="{Binding GridNameWidth}" Header="Name" />
Here is the backend code:
public int GridNameWidth
{
get
{
return 300;
}
}
The backend code is never touched. There are no errors but the name
field has a default width. I'd like to make width bind to my property. I don't need two-way binding just need the width to be binded when the grid loads. Is this possible in wpf?
The DataGridTextColumn is an abstract object that isn't actually part of the VisualTree, so it doesn't make use of an inherited DataContext like you would expect with other controls.
WPF knows how to parse something like the Binding
property correctly and transfer the binding to each Cell, however something like Width simply gets evaluated as-is, and does not evaluate correctly because neither the DataContext nor VisualTree is there as expected.
A common solution is to write your binding using a static data source instead. Here's an example based on this answer, which uses x:Reference to refer to another control from the XAML markup :
{Binding Source={x:Reference MyDataGrid}, Path=DataContext.NameColumnWidth}
Alternatively, you can define your own DataGridCellTemplate
with a control that has it's Width bound to whatever the property is on your DataItem
As already mentioned the issue here is that DataGridTextColumn isn't in the logical or visual tree.
An alternative solution is this approach based on a binding proxy providing a binding.
https://stackoverflow.com/a/46787502/5381620
SourceCode
<DataGrid ItemsSource="{Binding Lines}" AutoGenerateColumns="False" >
<DataGrid.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding}"/>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="ProductId1" Binding="{Binding Path=Result[0]}" Width="{Binding Data.Columns[0].Width, Source={StaticResource proxy}, Mode=TwoWay}" />
<DataGridTextColumn Header="ProductId2" Binding="{Binding Path=Result[1]}" Width="{Binding Data.Columns[1].Width, Source={StaticResource proxy}, Mode=TwoWay}"/>
</DataGrid.Columns>
</DataGrid>
class BindingProxy : Freezable
{
//Override of Freezable
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty = DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
public class Column : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected internal void OnPropertyChanged(string propertyname)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
}
public DataGridLength Width
{
get { return m_width; }
set { m_width = value; OnPropertyChanged("Width"); }
}
DataGridLength m_width;
}
For this implementation use a List<Column>
or ObservableCollection directly in DataContext else adjust binding.
Your GridNameWidth should be of the type DataGridLength if you want this to work.