1

In my WPF application, I have a DataGrid which is bound to a collection in the viewmodel, but I want the width of the first column to be equal to a property on the viewmodel itself, like so:

public ObservableCollection<Booking> Bookings
{
    return repository.Bookings();
}

public int MaxWidth
{
    return 100;
}

(I realise there's no point in binding to a fixed property like this - it's for demo purposes)

<UserControl>
    <DataGrid DataContext="{Binding Bookings}">
        <DataGrid.Columns>
            <DataGridTextColumn Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.MaxWidth}" Header="Provider" Binding="{Binding Name}" />
        </DataGrid.Columns>
    </DataGrid>
</UserControl>

But if I do this, the width of the column remains at the default value - i.e. just wide enough to accommodate its value.

What am I doing wrong?

EDIT: Attempted this, to see what happened:

<Label Name="tricky" Content="500" Visibility="Collapsed"></Label>
...
<DataGridTextColumn Width="{Binding ElementName=tricky, Path=Content}" Header="Provider" Binding="{Binding Name}" />

Which did nothing. Is it not possible to set the width dynamically?

Bob Tway
  • 9,301
  • 17
  • 80
  • 162
  • Shouldn't your RelativeSource binding be finding a `DataGrid` instead of a `UserControl`? – Rachel Mar 24 '14 at 14:51
  • I don't think so - the property I'm looking for belongs to the overall parent ViewModel, not the DataGrid. A quick test confirms it makes no difference. – Bob Tway Mar 24 '14 at 14:55
  • @MattThrower that's correct. You want the DataContext scope of the UserControl, not the Booking object. When does the DataGrid Binding happen? – d.moncada Mar 24 '14 at 14:56
  • Hmm. This is MVVM based so I'm not controlling the timing of the bindings. Bookings is populated before MaxWidth, if that's what you mean. – Bob Tway Mar 24 '14 at 14:59
  • Have you seen this? You may need to try using Reference instead http://stackoverflow.com/questions/9313586/binding-datagrid-column-width – d.moncada Mar 24 '14 at 15:04
  • Oh yeah, sorry I was reading that wrong. Does it work if you give your UserControl a name and use an ElementName binding instead? It's possible it's picking up a different UserControl in the hierarchy than the one you expect. Also, does binding MaxWidth instead of Width work? – Rachel Mar 24 '14 at 15:06
  • @d.moncada Sorry to be dense, but I can't see how to get that syntax to work. Every time I try it, I get "object reference not set" errors. – Bob Tway Mar 24 '14 at 15:13
  • @Rachel No, that compiles, but is never called. – Bob Tway Mar 24 '14 at 15:13
  • @d.moncada Seems like x:Reference is not properly supported in WPF? http://stackoverflow.com/questions/14644924/when-is-xreference-in-wpf-resolved-and-why-does-xaml-element-order-affect-it – Bob Tway Mar 24 '14 at 15:20
  • 1
    I think the problem is the `DataGridTextColum` is not part of the Visual Tree (can verify with [Snoop](https://snoopwpf.codeplex.com/)), so the binding never gets resolved. The data obtained from the `DataGridTextColumn` is used to build the DataGridCell template, and at the time the template is built, the DataContext isn't set. My suggestion would be to use a `DataGridTemplateColumn`, and specify your own CellTemplate that has the Width binding you need. – Rachel Mar 24 '14 at 15:34
  • @Rachel You are correct, thank you! If you'd like to re-post that as the answer, I can amend with an example and I'll accept it. Nice blog, BTW - been invaluable in helping me get to grips with WPF. – Bob Tway Mar 24 '14 at 15:40
  • @MattThrower Its posted below, and thanks :) – Rachel Mar 24 '14 at 15:42

3 Answers3

2

I think the problem is the DataGridTextColum is not part of the Visual Tree (can verify with Snoop), so the binding never gets resolved.

The data obtained from the DataGridTextColumn is used to build the DataGridCell template, and at the time the template is built, the DataContext isn't set.

My suggestion would be to use a DataGridTemplateColumn, and specify your own CellTemplate that has the Width binding you need.

Rachel
  • 130,264
  • 66
  • 304
  • 490
0

MaxWidth and Bookings are siblings aren't they? So binding expressions for them should be identical.

Looking at your first XAML: Path=DataContext.MaxWidth - remove DataContext.
Looking at the second XAML: no need to specify Path twice, the first Path is enough, I'm not even sure if the actual construct is valid.

Generally, a lot of low level UI properties are POCOs rather than DPs, just make sure your target one is a DP.

Update - what you might want to do is to use FallbackValue = 900 as part of your binding expression, just to narrow the problem down - whether it's a binding problem or the target property isn't suitable for using as binding target.

Update 2 - most times you'll find yourself running out of steam rather quickly when going for that low level of managing your UI, it's often beneficial to create MVColum, managing headers, widths etc and create a behavior, which can apply all these settings in one go. Also that way you won't be dependent on a flavor of your target properties as you'll be setting them up right in your code.

newfurniturey
  • 37,556
  • 9
  • 94
  • 102
user3455395
  • 161
  • 10
0

Alternatively a useful feature I use is as follow

IsEnabled="{Binding DataContext.IsEnabled,RelativeSource={RelativeSource AncestorType={x:Type Window}}}"  />

This allows you to link to properties outside of the itemssource of your grid.

Chris
  • 915
  • 8
  • 28