Why do I need to call Dispatcher.BeginInvoke() to

2019-07-08 23:38发布

问题:

I have a UserControl with a fixed size of 850x1100 to give me the same aspect ratio as a letter-sized piece of paper. I display this in my window inside a Viewbox, and it acts much like a print preview. The control inherits my window's DataContext, and when it's displayed on the screen, all the bindings work and it looks wonderful.

I've written the code below in my window's code behind (I feel it's a totally view-oriented operation) to attempt to print it. If I execute this code as written, the control prints, but does not appear to be data bound.

private void PrintButton_Click(object sender, RoutedEventArgs e) {
    var dlg = new PrintDialog();
    var result = dlg.ShowDialog();
    if (result == null || !(bool)result)
        return;

    var page = new InspectionFormPrintView { DataContext = this.DataContext };

    page.Measure(new Size(dlg.PrintableAreaWidth, dlg.PrintableAreaHeight));
    page.Arrange(new Rect(new Point(0, 0), page.DesiredSize));

    dlg.PrintVisual(page, "Inspection Form");
}

If I modify the last line in that method to

Dispatcher.BeginInvoke(new Action(() => dlg.PrintVisual(page, "Inspection Form")), DispatcherPriority.ApplicationIdle, null);

it will print just fine. Why is this?

回答1:

As mentioned by LPL in the comments, this is required because WPF needs to perform the all the data bindings. Since WPF operations are queued on the Dispatcher, you need to queue your print operation to complete after DispatcherPriority.DataBind. Therefore, calling BeginInvoke with DispatcherPriority.Render or lower will give WPF time to process the bindings on the control, so they show up in your printed output.