Hugo
Hugo

Reputation: 163

WPF XAML Trying to bind the Width of a DataGrid Column

I'd like to bind the Width of my Columns to a Property in my Model so I can save it if the user resize it. I'd like a solution with no code behind. This is what I have so far:

XAML:

<DataGrid x:Name="dgArticles"  AutoGenerateColumns="False" ItemsSource="{Binding Specifications.Articles}" RowDetailsVisibilityMode="Visible">
        <DataGrid.Columns>
            <DataGridTextColumn x:Name="Number" Header="Number" Binding="{Binding Number}" Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.Specifications.Config.NumberColumnWidth}" MinWidth="70" >

Model:

public class Specifications
{
    private ConfigurationGrid config
    public ConfigurationGrid Config { get { return config; } set { } }

    private ObservableCollection<Article> articles;
    public ObservableCollection<Article> Articles
    {
        get { return articles; }
        set { }
    }


public class ConfigurationGrid : INotifyPropertyChanged
{
    private double numberColumnWidth;
    public double NumberColumnWidth
    {
        get { return numberColumnWidth; }
        set { numberColumnWidth = value;  OnPropertyChanged("numberColumnWidth"); }
    }

    public ConfigurationGrid() { }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this,
                new System.ComponentModel.PropertyChangedEventArgs(propertyName));
    }
}

I managed to bind the Width of a sub Datagrid Column which is in my RowDetailsTemplate to the Width of another column like so:

<DataGridTextColumn Header="Quantity" CellStyle="{StaticResource QuantityStyle}" Binding="{Binding Quantity, UpdateSourceTrigger=PropertyChanged, StringFormat=\{0:n\}}" 
                                                    Width="{Binding Source={x:Reference Mesure}, Path=ActualWidth}"/>

This works fine but I don't know why it's not working on my main DataGrid. After debugging I noticed that it doesn't even reach the Getter of NumberColumnWidth. Does anyone know a way to make it work? Thank you

Edit

I tried the solution provided by @mm8 but it didn't work. It's still not reaching the Getter. Maybe I missed something. Here is what the code looks like right now:

XAML:

<UserControl x:Class="CachView.Views.GridView"
         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:sys="clr-namespace:System;assembly=mscorlib"
         xmlns:local="clr-namespace:CachView.ViewModels"
         xmlns:conv="clr-namespace:CachView.Converters"
         xmlns:util="clr-namespace:CachView.Util"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">

<UserControl.DataContext>
    <local:ArticleViewModel/>
</UserControl.DataContext>

<Grid Margin="10">
    <DataGrid x:Name="dgArticles"  AutoGenerateColumns="False" ItemsSource="{Binding Specifications.Articles}" RowDetailsVisibilityMode="Visible">
        <DataGrid.Resources>
            <util:BindingProxy x:Key="proxy" Data="{Binding}"/>
        </DataGrid.Resources>
        <DataGrid.Columns>
            <DataGridTextColumn x:Name="Number" Header="Number" Binding="{Binding Number}" Width="{Binding Data.Specifications.Config.NumberColumnWidth, Source={StaticResource proxy}}"
                                MinWidth="70">

            </DataGridTextColumn>

Code behind:

 public partial class GridView : UserControl
{
    public GridView(ArticleViewModel a)
    {            
        InitializeComponent();
        this.DataContext = a;
    }   
}

And my BindingProxy class is the same as in the example:

    class BindingProxy : 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));
}

My project is a UserControl that is meant to be used from a WinForm application. Here is how it is implemented and how I set the DataContext and properties. It is done from the Controller of my WinForm application.

class Controller
{

    private ArticleViewModel articleViewModel;
    private ElementHost elementHost;
    private MainWindow winformView;
    public ArticleViewModel ArticleViewModel { get { return articleViewModel; } }
    public Collection<Article> Articles { get; set; } 
    public Specifications Specs { get; set; }       

    public Controleur(MainWindow view) // The view is received from Program.cs
    {
        this.winformView = view;
        Articles = new Collection<Article>();
        populateArticles(); // This create hard coded articles for testing purpose

        ConfigurationGrid config= new ConfigurationGrid();
        config.NumberColumnWidth = 300;

        Specs = new Specifications(Articles);
        Specs.Config = config;

        articleViewModel = new ArticleViewModel(Specs);


        GridView gridView = new GridView(articleViewModel); //This is my WPF UserControl

        elementHost = new ElementHost();
        elementHost.Dock = DockStyle.Fill;
        this.winformView.Controls.Add(elementHost);
        elementHost.Child = gridView;           
    }

My ViewModel:

public class ArticleViewModel 
{
    private Specifications specifications;
    public Specifications Specifications { get { return specifications; } set { } }



    public ArticleViewModel() { }

    public ArticleViewModel(Specifications c)
    {
        this.specifications = c;
    }
}

Any help or suggestion is welcome.

Upvotes: 0

Views: 1571

Answers (1)

mm8
mm8

Reputation: 169200

A DataGridTextColumn is not a visual element that gets added to the element tree so you won't be able to bind to a RelativeSource since there are no ancestors to bind to.

If you want to be able to bind the Width property to a view model property you could use a BindingProxy object that captures the DataContext as suggested in the following blog post.

[WPF] How to bind to data when the DataContext is not inherited: https://www.thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/

Upvotes: 1

Related Questions