【问题标题】:Quality of screenshot截图质量
【发布时间】:2020-11-18 23:59:38
【问题描述】:

在我之前的一个问题中,我询问了如何截取ScrollViewer 中的所有内容,而不仅仅是ViewPort 中可见的内容。经过多次尝试,我找到了自己的答案,但不幸的是,这个答案引发了另一个问题。

看看这个示例代码:

ItemViewModel.vb(我的TreeViewItems 只是一个简单的视图模型):

Public Class ItemViewModel
    Public Property Text As String
    Public Property Items As New ObservableCollection(Of ItemViewModel)
End Class

ViewModel.vb(我的主窗口的视图模型,在构造函数中创建了一些项目):

Public Class ViewModel
    Public Property Items As New ObservableCollection(Of ItemViewModel)

    Private Const levels As Integer = 3

    Public Sub New()
        Dim item1 As ItemViewModel
        Dim item2 As ItemViewModel
        Dim item3 As ItemViewModel

        For index1 As Integer = 1 To levels
            item1 = New ItemViewModel With {.Text = String.Format("Node {0} - {1}", index1, Guid.NewGuid.ToString)}
            Me.Items.Add(item1)

            For index2 As Integer = 1 To levels
                item2 = New ItemViewModel With {.Text = String.Format("Node {0}.{1} - {2}", index1, index2, Guid.NewGuid.ToString)}
                item1.Items.Add(item2)

                For index3 As Integer = 1 To levels
                    item3 = New ItemViewModel With {.Text = String.Format("Node {0}.{1}.{2} - {3}", index1, index2, index3, Guid.NewGuid.ToString)}
                    item2.Items.Add(item3)
                Next index3
            Next index2
        Next index1
    End Sub

End Class

MainWindow.xaml(主窗口本身,显示TreeView):

<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ScreenshotTest"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="200" Width="300" KeyDown="Window_KeyDown">

    <Window.DataContext>
        <local:ViewModel />
    </Window.DataContext>

    <TreeView x:Name="Tree" ItemsSource="{Binding Items}">
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate DataType="{x:Type local:ItemViewModel}" ItemsSource="{Binding Items}">
                <Border BorderThickness="1" BorderBrush="Black" Margin="1">
                    <TextBlock Text="{Binding Text}" Margin="2" />
                </Border>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
        <TreeView.ItemContainerStyle>
            <Style TargetType="{x:Type TreeViewItem}">
                <Setter Property="IsExpanded" Value="True" />
            </Style>
        </TreeView.ItemContainerStyle>
    </TreeView>
</Window>

MainWindow.xaml.vb(主窗口的代码隐藏,这里是“有用”代码所在的位置):

Class MainWindow

    Private Sub Window_KeyDown(sender As Object, e As KeyEventArgs)
        Dim panel As StackPanel

        If e.Key <> Key.P Then
            Exit Sub
        End If

        panel = GetFirstChildOfType(Of StackPanel)(Me.Tree)
        panel.Background = Brushes.White
        panel.UpdateLayout()

        Me.CaptureScreen(panel, "C:\Temp\Screenshot.png")
    End Sub

    Private Shared Function GetFirstChildOfType(Of T As DependencyObject)(obj As DependencyObject) As T
        Dim result As T
        Dim child As DependencyObject

        If (obj Is Nothing) Then
            Return Nothing
        End If

        If (VisualTreeHelper.GetChildrenCount(obj) = 0) Then
            Return Nothing
        End If

        For index As Integer = 0 To VisualTreeHelper.GetChildrenCount(obj)
            child = VisualTreeHelper.GetChild(obj, index)
            result = TryCast(child, T)

            If (result Is Nothing) Then
                result = GetFirstChildOfType(Of T)(child)
            End If

            If (result IsNot Nothing) Then
                Return result
            End If
        Next index

        Return Nothing
    End Function

    Private Sub CaptureScreen(source As UIElement, filename As String)
        Dim height As Double
        Dim renderHeight As Double
        Dim width As Double
        Dim renderWidth As Double
        Dim renderTarget As RenderTargetBitmap
        Dim vb As VisualBrush
        Dim dv As DrawingVisual
        Dim encoder As PngBitmapEncoder

        Try
            height = source.RenderSize.Height
            renderHeight = height
            width = source.RenderSize.Width
            renderWidth = width

            renderTarget = New RenderTargetBitmap(Convert.ToInt32(renderWidth), Convert.ToInt32(renderHeight), 96, 96, PixelFormats.Pbgra32)
            vb = New VisualBrush(source)

            dv = New DrawingVisual

            Using dc As DrawingContext = dv.RenderOpen
                dc.DrawRectangle(vb, Nothing, New Rect(New Point(0, 0), New Point(width, height)))
            End Using

            renderTarget.Render(dv)

            encoder = New PngBitmapEncoder
            encoder.Frames.Add(BitmapFrame.Create(renderTarget))

            Using fs As IO.FileStream = New IO.FileStream(filename, IO.FileMode.Create, IO.FileAccess.Write)
                encoder.Save(fs)
            End Using
        Catch ex As Exception
            Stop
        End Try
    End Sub

End Class

如果你运行这个程序,你会看到一个带有一些项目的TreeView,如果你按P,一个TreeView的内容的屏幕截图会被截取并保存为“C:\Temp\Screenshot. png"。

到目前为止,一切都很好,屏幕截图大致看起来就像屏幕上看到的一样(质量方面)。例如,如果您在 IrfanView 中查看创建的 png 文件(大小为 100%),所有内容都是可读的。

但是如果我创建越来越多的项目(将levels 常量增加到 5,然后增加到 7),图片的质量会越来越差,在 levels=7 屏幕截图上看不到任何内容.

有谁知道这里发生了什么以及我该如何解决?我对 C# 中的答案也很好。

这里有一些屏幕截图可以可视化我的问题:

屏幕上的主窗口

level=3 的屏幕截图

level=5 的屏幕截图

level=7 的屏幕截图

【问题讨论】:

    标签: .net wpf vb.net


    【解决方案1】:

    我对 WPF 不是很精通,但是创建 VisualBrush 来绘制项目似乎是错误的。因此,不要使用VisualBrushDrawingVisual 的代码,而是使用renderTarget.Render(source)

    Private Sub CaptureScreen(source As UIElement, filename As String)
      Dim height As Double
      Dim renderHeight As Double
      Dim width As Double
      Dim renderWidth As Double
      Dim renderTarget As RenderTargetBitmap
      Dim vb As VisualBrush
      Dim dv As DrawingVisual
      Dim encoder As PngBitmapEncoder
    
      Try
        height = source.RenderSize.Height
        Debug.Print(height.ToString)
        renderHeight = height
        width = source.RenderSize.Width
        renderWidth = width
    
        renderTarget = New RenderTargetBitmap(Convert.ToInt32(renderWidth), Convert.ToInt32(renderHeight), 96, 96, PixelFormats.Pbgra32)
        renderTarget.Render(source)
    
        encoder = New PngBitmapEncoder
        encoder.Frames.Add(BitmapFrame.Create(renderTarget))
    
        Using fs As IO.FileStream = New IO.FileStream(filename, IO.FileMode.Create, IO.FileAccess.Write)
          encoder.Save(fs)
        End Using
      Catch ex As Exception
        Stop
      End Try
    End Sub
    

    这似乎对我很有效。

    【讨论】:

    • 你是对的,摆脱了VisualBrushDrawingContext 工作。我不知道为什么我首先使用它们。这可能是我无数次试验的遗留物。谢谢。
    • 当渲染控件具有非零 VisualOffset 时,可能需要使用 DrawingVisual,例如通过使用边距。参见例如这里:stackoverflow.com/a/55546728/1136211.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-13
    • 2012-06-16
    • 2020-11-14
    相关资源
    最近更新 更多