Home > database >  Force WPF Immediate UI update from UI thread
Force WPF Immediate UI update from UI thread

Time:05-28

(Note: Every question I can find related to this "Forcing WPF UI updates" seems to be about triggering one from a background thread. That's not what I want; All my code is in the UI thread.)

My view-model command-handler changes a public, boolean Failed property bound to a TextBlock's Visibility. This makes the bound TextBlock become visible. This part is all standard, WPF stuff and works fine; My UI changes and the label appears.

<TextBlock Text="Failed" Visibility="{Binding Failed, Converter="{StaticResource CvtBoolToVisibility}}" Foreground="Red" />

But I added code so save a screen-shot of the application window immediately after setting this property. The code produces a perfect image of the screen. Unfortunately it is of the screen as it was before I changed the property because WPF has not yet had a chance to render the changes.

var window    = Application.Current.MainWindow ?? throw new InvalidOperationException("No window");
var bm        = window.CopyAsBitmap()          ?? throw new InvalidOperationException("Unable to copy bitmap");
var pngData   = bm.Encode(new PngBitmapEncoder());
File.WriteAllBytes(Path.Combine(SaveFolder, "TestOutput.png"), pngData);

Is there a way to force WPF to force it to process all property changes, do layout and rendering before I continue? Or maybe some type of "update complete" event I could hook up to?

Currently I'm using a hack that looks bad even to me: I made the command handler async and preceded my writing code with this, using an arbitrary background delay before the continuing on to write

await Task.Delay(500).ConfigureAwait(true);  // Give WPF a chance to update.

var window    = Application.Current.MainWindow ?? throw new InvalidOperationException("No window");
var bm        = window.CopyAsBitmap()          ?? throw new InvalidOperationException("Unable to copy bitmap");
var pngData   = bm.Encode(new PngBitmapEncoder());
await File.WriteAllBytesAsync(Path.Combine(SaveFolder, "TestOutput.png"), pngData);

But this doesn't seem to me to be a very robust way to do this. Is there something better?

CodePudding user response:

Before taking screenshot call this GridParent.UpdateLayout(); here GridParent can be parent control or like you are doing Application.Current.MainWindow.UpdateLayout();.

I've tried it. it will make sure UI is rendered before taking screenshot.

Also, i used this method to take screenshot.

private void SaveSnap()
{
    RenderTargetBitmap renderTargetBitmap =
        new RenderTargetBitmap((int) GridParent.ActualWidth, (int) GridParent.ActualHeight, 96, 96,
            PixelFormats.Pbgra32);
    renderTargetBitmap.Render(GridParent);
    PngBitmapEncoder pngImage = new PngBitmapEncoder();
    pngImage.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
    using (Stream fileStream = File.Create("Img.png"))
    {
        pngImage.Save(fileStream);
    }
}
  • Related