views:

227

answers:

1

Problem

I have a WPF Toolkit DataGrid, and I'd like to be able to switch among several preset column orders. This is an MVVM project, so the column orders are stored in a ViewModel. The problem is, I can't get bindings to work for the DisplayIndex property. No matter what I try, including the sweet method in this Josh Smith tutorial, I get:

The DisplayIndex for the DataGridColumn with Header 'ID' is out of range. DisplayIndex must be greater than or equal to 0 and less than Columns.Count. Parameter name: displayIndex. Actual value was -1.

Is there any workaround for this?

I'm including my test code below. Please let me know if you see any problems with it.


ViewModel code

public class MainViewModel
{
    public List<Plan> Plans { get; set; }
    public int IdDisplayIndex { get; set; }
    public int NameDisplayIndex { get; set; }
    public int DescriptionDisplayIndex { get; set; }

    public MainViewModel()
    {
        Initialize();
    }

    private void Initialize()
    {
        IdDisplayIndex = 1;
        NameDisplayIndex = 2;
        DescriptionDisplayIndex = 0;
        Plans = new List<Plan>
        {
            new Plan { Id = 1, Name = "Primary", Description = "Likely to work." },
            new Plan { Id = 2, Name = "Plan B", Description = "Backup plan." },
            new Plan { Id = 3, Name = "Plan C", Description = "Last resort." }
        };
    }
}

Plan Class

public class Plan
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}

Window code - this uses Josh Smith's DataContextSpy

<Window
    x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication1"
    xmlns:mwc="http://schemas.microsoft.com/wpf/2008/toolkit"
    Title="Main Window" Height="300" Width="300">
    <Grid>
        <mwc:DataGrid ItemsSource="{Binding Plans}" AutoGenerateColumns="False">
            <mwc:DataGrid.Resources>
                <local:DataContextSpy x:Key="spy" />
            </mwc:DataGrid.Resources>
            <mwc:DataGrid.Columns>
                <mwc:DataGridTextColumn
                    Header="ID" 
                    Binding="{Binding Id}"
                    DisplayIndex="{Binding Source={StaticResource spy}, Path=DataContext.IdDisplayIndex}" />
                <mwc:DataGridTextColumn
                    Header="Name"
                    Binding="{Binding Name}"
                    DisplayIndex="{Binding Source={StaticResource spy}, Path=DataContext.NameDisplayIndex}" />
                <mwc:DataGridTextColumn
                    Header="Description"
                    Binding="{Binding Description}"
                    DisplayIndex="{Binding Source={StaticResource spy}, Path=DataContext.DescriptionDisplayIndex}" />
            </mwc:DataGrid.Columns>
        </mwc:DataGrid>
    </Grid>
</Window>

Note: If I just use plain numbers for DisplayIndex, everything works fine, so the problem is definitely with the bindings.


Update 5/1/2010

I was just doing a little maintenance on my project, and I noticed that when I ran it, the problem I discuss in this post had returned. I knew that it worked last time I ran it, so I eventually narrowed the problem down to the fact that I had installed a newer version of the WPF Toolkit (Feb '10). When I reverted to the June '09 version, everything worked fine again. So, I'm now doing something I should have done in the first place: I'm including the WPFToolkit.dll that works in my solution folder and checking it into version control. It's unfortunate, though, that the newer toolkit has a breaking change.

+2  A: 

(My previous answer was way off track - so I deleted it - I'll try to answer again after reproducing the error on my machine)

The problem you encounter stems from the fact that when the bindings are evaluated for the first time, the DataContext property is still set to null. This, for some strange reason, yet unknown to me, causes the binding to evaluate at -1. However if you make sure to set the DataContext before the bindings are evaluated you'll be ok. Unfortunately this might only be possible in code-behind - hence during run-time, and not in design time. So in design time I still don't know how to circumvent the error.

To do that, set the DataContext property of the window before the call to InitializeComponent thus:

public MainWindow()
{
    DataContext = new MainViewModel();
    InitializeComponent();
}

Hope this helps.

Aviad P.
Well this is interesting. If I run my test app, your fix works perfectly. In my real app, the `DataGrid` in question is actually in a `UserControl`, and for some reason, if I apply the `DataContextSpy` (which I didn't try until just now since it had failed for my test app), it works even if I don't postpone the call to `InitializeComponent()`. I think this must be because I'm just setting the `Content` on a `ContentControl` rather than doing `window.Show()`. In any case, thanks for taking the time to solve this for me (+1 and √).
DanM