user1229658
user1229658

Reputation: 97

Asynchronous DataGridTextColumn formatter

I have a dataset with an unknown number of columns that I am populating into a DataGrid. I would like to be able to asynchronously format cell contents using different formatters. For example, a complex object that might take some time-bound processing. Note these requirements exclude use of IConverter.

I have defined a class AsyncFormatter that wraps INotifyTask (see this link).

public class AsyncFormatter
{
    public NotifyTask<string?> FormattedResult { get; private set; }

    public AsyncFormatter()
    {
    }

    public void Start()
    {
        var t = new Task<string?>(() => ExecuteAsync().Result);
        FormattedResult = NotifyTask.Create(t);
        t.Start();
    }

    private async Task<string?> ExecuteAsync()
    {
        return await DoAsyncFormattingWork();
    }
}

In the DataGrid's AutoGeneratingColumn callback, I am binding a given cell to the asynchronous result of a given formatter. I have seen a few approaches in somewhat related SO questions on how to bind the async formatter, but using ElementStyle and TextBlock seem to be most promising:

private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
    var textColumn = e.Column as DataGridTextColumn;
    var asyncFormatter = new AsyncFormatter();

    textColumn.ElementStyle = new Style(typeof(TextBlock));
    textColumn.ElementStyle.Setters.Add(new Setter(TextBlock.TagProperty, asyncFormatter));
    textColumn.ElementStyle.Setters.Add(new Setter(TextBlock.TextProperty, new Binding
    {
        Source = asyncFormatter,
        Path = new PropertyPath("FormattedResult.Result"),
        FallbackValue = "<Please wait...>",
        IsAsync = true,
    }));
    textColumn.ElementStyle.Setters.Add(new EventSetter(
        TextBlock.LoadedEvent, new RoutedEventHandler((s, args) =>
    {
        var textBlock = s as TextBlock;
        var asyncFormatter = textBlock.Tag as AsyncFormatter;
        if (asyncFormatter == null)
        {
            Debug.Assert(false);
            return;
        }
        var contents = textBlock.Text;
        asyncFormatter.Start(evt, contents, colName);
    })));

What is not working in the above approach is the binding of TextBlock.TextProperty. It appears to be completely blown away by whatever is setting the TextBlock's initial Text value. So while the async formatter runs, the value is never propagated back to the TextBlock.

Is there a better approach?

Upvotes: 0

Views: 37

Answers (0)

Related Questions