Создание скриншота окна WPF - PullRequest
10 голосов
/ 26 февраля 2011

В winforms мы можем использовать DrawToBitmap.Есть ли подобный метод в WPF?

Ответы [ 2 ]

11 голосов
/ 18 июня 2015

Вы пробовали RenderTargetBitmap?https://msdn.microsoft.com/en-us/library/system.windows.media.imaging.rendertargetbitmap.aspx

Существует несколько методов «скриншотов», которые используют этот метод, например, этот * взят 100 * * :

    public static void CreateBitmapFromVisual(Visual target, string fileName)
    {
        if (target == null || string.IsNullOrEmpty(fileName))
        {
            return;
        }

        Rect bounds = VisualTreeHelper.GetDescendantBounds(target);

        RenderTargetBitmap renderTarget = new RenderTargetBitmap((Int32)bounds.Width, (Int32)bounds.Height, 96, 96, PixelFormats.Pbgra32);

        DrawingVisual visual = new DrawingVisual();

        using (DrawingContext context = visual.RenderOpen())
        {
            VisualBrush visualBrush = new VisualBrush(target);
            context.DrawRectangle(visualBrush, null, new Rect(new Point(), bounds.Size));
        }

        renderTarget.Render(visual);
        PngBitmapEncoder bitmapEncoder = new PngBitmapEncoder();
        bitmapEncoder.Frames.Add(BitmapFrame.Create(renderTarget));
        using (Stream stm = File.Create(fileName))
        {
            bitmapEncoder.Save(stm);
        }
    }
0 голосов
/ 03 апреля 2019

Проверено:

  • Используется в корпоративном приложении WPF.
  • Проверено на небольшой части всего экрана (можно сделать снимок экрана с любым элементом на экране).
  • Протестировано с несколькими мониторами.
  • Протестировано в окне с WindowState = Normal и WindowState = Maximized.
  • Протестировано с 96 DPI.
  • Протестировано с 120 DPI(установите размер шрифта «125%» в Windows 10, затем выйдите и войдите в систему).
  • Предотвращает любое использование вычислений пикселей в стиле Windows, которые имеют проблемы с масштабированием при различных настройках DPI.
  • Работает, даже если часть окна находится за пределами экрана.
  • Работает, даже если другое мошенническое окно покрывает часть текущего окна.
  • Использует ClipToBounds, поэтому он совместим с несколькими закрепленнымиWindows в Infragistics.

Функция:

/// <summary>
/// Take screenshot of a Window.
/// </summary>
/// <remarks>
/// - Usage example: screenshot icon in every window header.                
/// - Keep well away from any Windows Forms based methods that involve screen pixels. You will run into scaling issues at different
///   monitor DPI values. Quote: "Keep in mind though that WPF units aren't pixels, they're device-independent @ 96DPI
///   "pixelish-units"; so really what you want, is the scale factor between 96DPI and the current screen DPI (so like 1.5 for
///   144DPI) - Paul Betts."
/// </remarks>
public async Task<bool> TryScreenshotToClipboardAsync(FrameworkElement frameworkElement)
{
    frameworkElement.ClipToBounds = true; // Can remove if everything still works when the screen is maximised.

    Rect relativeBounds = VisualTreeHelper.GetDescendantBounds(frameworkElement);
    double areaWidth = frameworkElement.RenderSize.Width; // Cannot use relativeBounds.Width as this may be incorrect if a window is maximised.
    double areaHeight = frameworkElement.RenderSize.Height; // Cannot use relativeBounds.Height for same reason.
    double XLeft = relativeBounds.X;
    double XRight = XLeft + areaWidth;
    double YTop = relativeBounds.Y;
    double YBottom = YTop + areaHeight;
    var bitmap = new RenderTargetBitmap((int)Math.Round(XRight, MidpointRounding.AwayFromZero),
                                        (int)Math.Round(YBottom, MidpointRounding.AwayFromZero),
                                        96, 96, PixelFormats.Default);

    // Render framework element to a bitmap. This works better than any screen-pixel-scraping methods which will pick up unwanted
    // artifacts such as the taskbar or another window covering the current window.
    var dv = new DrawingVisual();
    using (DrawingContext ctx = dv.RenderOpen())
    {
        var vb = new VisualBrush(frameworkElement);
        ctx.DrawRectangle(vb, null, new Rect(new Point(XLeft, YTop), new Point(XRight, YBottom)));
    }
    bitmap.Render(dv);
    return await TryCopyBitmapToClipboard(bitmap);         
}        

private static async Task<bool> TryCopyBitmapToClipboard(BitmapSource bmpCopied)
{
    var tries = 3;
    while (tries-- > 0)
    {
        try
        {
            // This must be executed on the calling dispatcher.
            Clipboard.SetImage(bmpCopied);
            return true;
        }
        catch (COMException)
        {
            // Windows clipboard is optimistic concurrency. On fail (as in use by another process), retry.
            await Task.Delay(TimeSpan.FromMilliseconds(100));
        }
    }
    return false;
}   

В ViewModel:

 public ICommand ScreenShotCommand { get; set; }

Команда:

 private async void OnScreenShotCommandAsync(FrameworkElement frameworkElement)
 {
     var result = await this.TryScreenshotToClipboardAsync(frameworkElement); 
     if (result == true)
     {
        // Success.
     }
 }

В конструкторе:

// See: https://stackoverflow.com/questions/22285866/why-relaycommand
// Or use MVVM Light to obtain RelayCommand.
this.ScreenShotCommand = new RelayCommand<FrameworkElement>(this.OnScreenShotCommandAsync);

И в XAML:

<Button Command="{Binding ScreenShotCommand, Mode=OneWay}"
        CommandParameter="{Binding ElementName=Content}"                                                        
        ToolTip="Save screenshot to clipboard">    
</Button>

ElementName=Content указывает на именованныйэлемент где-то еще на той же странице XAML.Если вы хотите сделать снимок всего окна целиком, вы не можете передать окно (так как мы не можем установить ClipToBounds для окна), но мы можем передать <Grid> внутри окна.

<Grid x:Name="Content">
    <!-- Content to take a screenshot of. -->
</Grid>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...