1
votes

I use a WPF DataGrid whose ItemsSource is bound to some MyResults property, where MyResults is the DefaultView of a DataTable instance. As I need some flexibility in labeling the column headers in the DataGrid, I subscribe the AutoGeneratingColumn event to adjust these headers.

View/XAML:

<DataGrid x:Name="ResultTable" ItemsSource="{Binding MyResults, Mode=OneWay}"
  cal:Message.Attach="[Event AutoGeneratingColumn] = [Action CustomizeAutoColumn($this, $eventArgs)]"
  AutoGenerateColumns="True"/>

ViewModel/CS:

public DataView MyResults { get { return MyDataTable.DefaultView; } }

public void CustomizeAutoColumn(object sender, DataGridAutoGeneratingColumnEventArgs e) {
    e.Column.Header = AdjustHeaderText(e.Column.Header);
}

The number of columns in MyDataTable may change dynamically, requiring the DataGrid to update its auto-generated columns. In that case, the following function in the ViewModel (Caliburn.Micro.Screen subclass) is triggered:

private void UpdateResultTable() { // not working
    NotifyOfPropertyChange(() => MyResults);
}

My problem is that the AutoGeneratingColumn event is never fired with this implementation (or at least CustomizeAutoColumn is never called). Neither during program initialization (which is my first problem), nor on calling UpdateResultTable (the second problem). While new row content is added to the view as expected, the columns remain as they are.

I found a workaround by getting a handle to the View object in my ViewModel, explicitly resetting the items source. However this is evidently quite ugly, especially since it breaks View/ViewModel separation. Note that since I'm using Caliburn Micro, I don't have any code-behind for the view XAML.

private void UpdateResultTable() { // working
    var datagrid = VisualTree.FindChild<DataGrid>((DependencyObject)GetView(), "ResultTable");
    datagrid.ItemsSource = null;
    datagrid.ItemsSource = MyResults;
}

Any ideas how to get the initial approach to work? Thanks in advance for any suggestions!

1

1 Answers

0
votes

explicitly resetting the items source. However this is evidently quite ugly, especially since it breaks View/ViewModel separation.

Not ugly per-se but required.

But to adhere to such separation, have the VM provide a command (ICommand) that requests that the view reset the datacontext, which is subscribed to by the view. Hence separate.

I have done it for the same reasons and here is the code to do a reset from a requested change by the VM:

public MainWindow()
{
    DataContext = VM = new CertifyingVM();

    VM.CommandRefreshBindings = new OperationCommand(o =>
    {
        MainAccessionHeader.DataContext =
        MainHeader.DataContext = null;

        MainAccessionHeader.DataContext =
        MainHeader.DataContext = VM;

        var currentBatch = VM.CurrentBatch;
        MainAccessionHeader.CurrentBatch = null;
        MainAccessionHeader.CurrentBatch = currentBatch;

    });

    VM.LockBatchGui = new OperationCommand(o =>
    { ... }

OperationCommand is my ICommand operation which is demonstrated on my blog post entitled Xaml: MVVM Example for Easier Binding.

How you trigger it is up to you, just remove the value of the datacontext to null then set it back.