How to bind datagrid columns width in WPF (MVVM)

2019-08-03 06:24发布

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?

标签: c# wpf c#-4.0 mvvm
3条回答
劳资没心,怎么记你
2楼-- · 2019-08-03 06:51

Your GridNameWidth should be of the type DataGridLength if you want this to work.

查看更多
Fickle 薄情
3楼-- · 2019-08-03 06:56

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

查看更多
混吃等死
4楼-- · 2019-08-03 07:13

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.

查看更多
登录 后发表回答