WPF Formular in Bitmap speichern

  • WPF

Es gibt 2 Antworten in diesem Thema. Der letzte Beitrag () ist von PadreSperanza.

    WPF Formular in Bitmap speichern

    Hallo Leute,

    ich bin neu hier - und auch relativ neu bei VB.Net. Habe damit im November letzten Jahres angefangen (zuerst WinForms) und bin nun dabei, mein Projekt und Wissen zu erweitern und auf WPF umzustellen (alles in Eigenarbeit und nur mittels Büchern und Web-Recherche). Doch leider stoße ich nun auf ein Problem, bei dem mir einfach keine Web-Recherche mehr helfen kann. Vielleicht kann es ja einer hier.

    Zur kurzen Übersicht: Ich schreibe für Freunde ein Programm, um Game-Statistiken zu verfolgen und sie möchten diese gerne auch im Stream anzeigen. Deshalb habe ich eine Oberfläche gestaltet, die der User benutzt und über diese werden auch alle Eingaben und Aktivitäten ausgeführt. Darüber hinaus gibt es zwei verschiedene Anzeige-Controls, die das Resultat anzeigen (je nach gewähltem Modus). Da diese aber auch im Stream über andere Dinge eingeblendet werden sollen, schreibe ich diese Controls in eine PNG-Datei, welche wiederum eingeblendet wird im Stream, sodass ich mit Transparenzen arbeiten kann.

    Das kleine Anzeigefenster funktioniert tatsächlich auch komplett problemlos, hier der Code für die Bitmap:


    VB.NET-Quellcode

    1. Public Shared Sub CreateBitmap(ByVal reference As RankedPanel)
    2. Try
    3. Dim frmRankedBitmap As New RankedPanel()
    4. 'frmRankedBitmap.Measure(New Windows.Size(reference.ActualWidth, reference.ActualHeight))
    5. 'frmRankedBitmap.Arrange(New Rect(New Windows.Size(reference.ActualWidth, reference.ActualHeight)))
    6. 'frmRankedBitmap.UpdateLayout()
    7. Dim path = My.Settings.BitmapextractionPath
    8. 'frmRankedBitmap.Width = reference.Width
    9. 'frmRankedBitmap.Height = reference.Height
    10. If Not IO.Directory.Exists(path) Then IO.Directory.CreateDirectory(path)
    11. 'If Not IO.File.Exists(path) Then IO.File.Create(path)
    12. path += "/RankedOverlay.png"
    13. frmRankedBitmap.Margin = New Thickness(0, 0, 0, 0)
    14. frmRankedBitmap.OuttaBorder.BorderBrush = New BrushConverter().ConvertFromString("Transparent")
    15. frmRankedBitmap.OuttaBorder.Background = New BrushConverter().ConvertFromString("Transparent")
    16. frmRankedBitmap.Copyright.Content = reference.Copyright.Content
    17. frmRankedBitmap.LblWonHead.Content = reference.LblWonHead.Content
    18. frmRankedBitmap.LblLossHead.Content = reference.LblLossHead.Content
    19. frmRankedBitmap.LblRatioHead.Content = reference.LblRatioHead.Content
    20. frmRankedBitmap.Won.Content = reference.Won.Content
    21. frmRankedBitmap.Won.RenderSize = reference.Won.RenderSize
    22. frmRankedBitmap.Loss.Content = reference.Loss.Content
    23. frmRankedBitmap.Ratio.Content = reference.Ratio.Content
    24. frmRankedBitmap.Foreground = reference.Foreground
    25. frmRankedBitmap.FontFamily = reference.FontFamily
    26. frmRankedBitmap.ResizeMode = ResizeMode.NoResize
    27. frmRankedBitmap.WindowState = WindowState.Minimized
    28. frmRankedBitmap.Show()
    29. Dim DrawVis As DrawingVisual = New DrawingVisual()
    30. Dim rectangles As Rect = VisualTreeHelper.GetDescendantBounds(frmRankedBitmap)
    31. Dim drawContext As DrawingContext = DrawVis.RenderOpen()
    32. Using drawContext
    33. Dim conBrush As VisualBrush = New VisualBrush(frmRankedBitmap)
    34. drawContext.DrawRectangle(conBrush, Nothing, New Rect(rectangles.Size))
    35. End Using
    36. Dim renderBitmap As RenderTargetBitmap = New RenderTargetBitmap(10 * frmRankedBitmap.Width, 10 * frmRankedBitmap.Height, 10 * 120, 10 * 120, PixelFormats.Pbgra32)
    37. renderBitmap.Render(frmRankedBitmap)
    38. Dim encoder As PngBitmapEncoder = New PngBitmapEncoder()
    39. encoder.Frames.Add(BitmapFrame.Create(renderBitmap))
    40. Dim fS As New IO.FileStream(path, IO.FileMode.Create)
    41. encoder.Save(fS)
    42. fS.Dispose()
    43. fS = Nothing
    44. frmRankedBitmap.Close()
    45. 'Using fS As IO.FileStream = New IO.FileStream(path, IO.FileMode.Create)
    46. ' Dim wbitmap As WriteableBitmap = New WriteableBitmap(frmRankedBitmap.Width, frmRankedBitmap.Height, 300, 300, PixelFormats.Bgra32, BitmapPalettes.Halftone256Transparent)
    47. ' wbitmap.WritePixels()
    48. ' Dim encoder As PngBitmapEncoder = New PngBitmapEncoder()
    49. ' encoder.Frames.Add(BitmapFrame.Create())
    50. 'End Using
    51. Catch ex As Exception
    52. Dim err As New OK("Ranked Bitmap Creation Error", "An error has occured while trying to create a Bitmap-File. " & vbNewLine & ex.Message)
    53. End Try
    54. End sub


    zur kurzen Erläuterung:
    Es wird das angezeigt Referenz-Panel mitgegeben und ein neues Panel erzeugt. Nun weise ich diesem Panel alle Werte zu (kopiere die Werte in das neue Panel) und erstelle den Pfad zur Abspeicherung.

    Hiernach setze ich die Window-State auf Minimized, da ich nicht möchte, dass das kopierte Window angezeigt wird. Das Window wird in die Datei geschrieben und wieder geschlossen. Das funktioniert auch komplett problemlos. Die Datei enthält ein komplettes Abbild des Fensters.

    Bei dem größeren Anzeigebild verfahre ich eigentlich genauso. Hier ist aber das Problem, dass ich in der PNG-Datei nur eine kleine Anzeige erhalte, wenn ich Window-State auf minimized setze, während ich das richtige Resultat erhalte, wenn ich window-state auf normal stehen habe. Ich weiß nicht, woran das liegt... und langsam komme ich ans Verzweifeln. Hier der Code des anderen Fensters (er ist noch nicht fertig, da ich alle weiteren Werte erst kopieren wollte, wenn das Exportieren einwandfrei funktioniert):

    VB.NET-Quellcode

    1. Public Shared Sub CreateBitmap(ByVal reference As EventPanel)
    2. Try
    3. Dim frmEventPanel As New EventPanel(True)
    4. frmEventPanel.Measure(New Windows.Size(reference.ActualWidth, reference.ActualHeight))
    5. frmEventPanel.Arrange(New Rect(New Windows.Size(reference.ActualWidth, reference.ActualHeight)))
    6. frmEventPanel.UpdateLayout()
    7. frmEventPanel.RenderSize = reference.RenderSize
    8. frmEventPanel.Width = reference.ActualWidth
    9. frmEventPanel.Height = reference.ActualHeight
    10. frmEventPanel.FontFamily = reference.FontFamily
    11. frmEventPanel.Foreground = reference.Foreground
    12. frmEventPanel.txBWH.RenderSize = reference.txBWH.RenderSize
    13. frmEventPanel.txBWH.Foreground = reference.Foreground
    14. Dim path = My.Settings.BitmapextractionPath
    15. If Not IO.Directory.Exists(path) Then IO.Directory.CreateDirectory(path)
    16. path += "/EventOverlay.png"
    17. frmEventPanel.Margin = New Thickness(0, 0, 0, 0)
    18. frmEventPanel.OuttaBorder.BorderBrush = New BrushConverter().ConvertFromString("Transparent")
    19. frmEventPanel.OuttaBorder.Background = New BrushConverter().ConvertFromString("Transparent")
    20. frmEventPanel.Copyright.Text = reference.Copyright.Text
    21. frmEventPanel.ResizeMode = ResizeMode.NoResize
    22. frmEventPanel.WindowState = WindowState.Minimized
    23. 'Headliner
    24. frmEventPanel.txBWH.Text = reference.txBWH.Text
    25. frmEventPanel.txBLH.Text = reference.txBLH.Text
    26. frmEventPanel.txBVH.Text = reference.txBVH.Text
    27. Console.WriteLine(frmEventPanel.txBVH.Text)
    28. 'For i As Integer = 1 To My.Settings.NumberOfRuns - 1
    29. ' 'TxBWin
    30. ' Dim cpytxB, txB As TextBlock
    31. ' cpytxB = frmEventPanel.FindName("txBW" & i)
    32. ' txB = reference.FindName("txBW" & i)
    33. ' cpytxB.Inlines.Clear()
    34. ' 'TxBLose
    35. ' Dim cpytxL, txL As TextBlock
    36. ' cpytxL = frmEventPanel.FindName("txBL" & i)
    37. ' txL = reference.FindName("txBL" & i)
    38. ' cpytxL.Inlines.Clear()
    39. ' 'TxBValue
    40. ' Dim cpytxV, txV As TextBlock
    41. ' cpytxV = frmEventPanel.FindName("txBV" & i)
    42. ' txV = reference.FindName("txBV" & i)
    43. ' cpytxV.Inlines.Clear()
    44. ' For k As Integer = 0 To txB.Inlines.Count - 1
    45. ' 'wins
    46. ' Dim s As String = XamlWriter.Save(txB.Inlines.ElementAt(k))
    47. ' Dim st As New MemoryStream(ASCIIEncoding.Default.GetBytes(s))
    48. ' cpytxB.Inlines.Add(XamlReader.Load(st))
    49. ' Next
    50. ' For k As Integer = 0 To txL.Inlines.Count - 1
    51. ' 'losses
    52. ' Dim s As String = XamlWriter.Save(txL.Inlines.ElementAt(k))
    53. ' Dim st As New MemoryStream(ASCIIEncoding.Default.GetBytes(s))
    54. ' cpytxL.Inlines.Add(XamlReader.Load(st))
    55. ' Next
    56. ' For k As Integer = 0 To txV.inlines.Count - 1
    57. ' Dim s As String = XamlWriter.Save(txV.Inlines.ElementAt(k))
    58. ' Dim st As New MemoryStream(ASCIIEncoding.Default.GetBytes(s))
    59. ' cpytxV.Inlines.Add(XamlReader.Load(st))
    60. ' Next
    61. 'Next
    62. Debug.WriteLine(frmEventPanel.IsLoaded)
    63. frmEventPanel.UpdateLayout()
    64. frmEventPanel.Show()
    65. frmEventPanel.UpdateLayout()
    66. Debug.WriteLine(frmEventPanel.IsLoaded)
    67. 'frmEventPanel.OuttaBorder.BorderBrush = New BrushConverter().ConvertFromString("Transparent")
    68. 'frmEventPanel.OuttaBorder.Background = New BrushConverter().ConvertFromString("Transparent")
    69. 'System.Threading.Thread.Sleep(10000)
    70. Dim DrawVis As DrawingVisual = New DrawingVisual()
    71. Dim rectangles As Rect = VisualTreeHelper.GetDescendantBounds(frmEventPanel)
    72. Dim drawContext As DrawingContext = DrawVis.RenderOpen()
    73. Using drawContext
    74. Dim conBrush As VisualBrush = New VisualBrush(frmEventPanel)
    75. drawContext.DrawRectangle(conBrush, Nothing, New Rect(rectangles.Size))
    76. End Using
    77. Dim renderBitmap As RenderTargetBitmap = New RenderTargetBitmap(10 * frmEventPanel.Width, 10 * frmEventPanel.Height, 10 * 120, 10 * 120, PixelFormats.Pbgra32)
    78. renderBitmap.Render(frmEventPanel)
    79. Dim encoder As PngBitmapEncoder = New PngBitmapEncoder()
    80. encoder.Frames.Add(BitmapFrame.Create(renderBitmap))
    81. Dim fs As New IO.FileStream(path, IO.FileMode.Create)
    82. encoder.Save(fs)
    83. Debug.WriteLine(frmEventPanel.IsLoaded)
    84. fs.Dispose()
    85. fs = Nothing
    86. ' System.Threading.Thread.Sleep(10000)
    87. frmEventPanel.Close()
    88. Catch ex As Exception
    89. Debug.WriteLine(ex.Message)
    90. Dim err As New OK("EventBitmapCreationError", "An error has occured while trying to create a Bitmap-File. " & vbNewLine & ex.Message)
    91. End Try
    92. End Sub





    Vielleicht hat ja der eine oder andere, eine Ahnung, was das Problem ist oder wie man das besser lösen kann.
    Mein Problem ist, dass ich es in einem Extra-Fenster machen muss (faktisch das Panel kopieren), da ich nicht das angezeigte Panel mit Transparenz haben kann und auch das andere Bild nicht bei jedem Update ne angezeigt haben möchte. Ich wäre für jede Hilfe, jeden Einwand und jede Unterstützung dankbar - vor allem, da ich noch nicht so lange dabei bin :)
    Dateien
    • minimized.png

      (183,21 kB, 65 mal heruntergeladen, zuletzt: )
    • normal (2).png

      (323,96 kB, 69 mal heruntergeladen, zuletzt: )
    Hallo und willkommen im Forum

    PadreSperanza schrieb:

    mein Projekt und Wissen zu erweitern und auf WPF umzustellen

    WPF ist schon um einiges anders aufgebaut als WinForms und hier gibt es ein paar Dinge zu beachten.
    Ich kann dir hier meine Signatur ans Herz legen.

    PadreSperanza schrieb:

    schreibe ich diese Controls in eine PNG-Datei, welche wiederum eingeblendet wird im Stream, sodass ich mit Transparenzen arbeiten kann.

    Warum mittels Bild. In WPF hast du im Gegensatz zu WinForms den Vorteil das du mit Transparenzen arbeiten kannst, du kannst dir also diesen Umweg komplett sparen.

    PadreSperanza schrieb:

    da ich nicht das angezeigte Panel mit Transparenz haben kann

    Warum? Hast du es unter WinForms probiert oder unter WPF auch? Unter WPF ist das kein Problem.

    Wenn du auf WPF umsteigst kann ich dir empfehlen das Projekt neu zu machen. ein "umstellen" oder ein Migrieren von WinForms zu WPF ist nie eine gute Idee, dazu sind die beiden UI Framework einfach zu Unterschiedlich.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    ja, ich habe mit WinForms gearbeitet und dort auch das Panel in eine PNG-Datei geschrieben, um Transparenz zu erhalten. Ich weiß auch, und das habe ich auch schon hinbekommen, dass WPF Transparenzen bietet und ich das Panel selbst transparent bekomme.
    Darum geht es leider nicht. Denn ich möchte, dass dieses Panel im Stream angezeigt werden kann. Hier kommt nun aber das Problem dazu, dass ein Fenster, welches Transparenz enthält von der Streaming-Software lediglich als schwarzes Bild erkannt wird und gar nicht angezeigt werden kann, weshalb ich wieder über den Umweg gehen muss, eine Bilddatei zu erstellen, in der die notwendigen Transparenzen zu sehen sind. Ansonsten wäre das kein Problem gewesen.
    Und nun musste ich mir leider eine Lösung einfallen lassen. Während diese Lösung mit dem "Windowstate.minimized" auch bei dem kleinen Panel voll umfänglich funktioniert, wird bei dem größeren Panel lediglich die erste Zeile in die PNG-Datei geschrieben. Der Rest leider nicht. Der Rest wird aber auch nicht angezeigt oder kann in irgendeiner Form verändert werden. Und hier stehe ich etwas auf dem Schlauch - zumal es wieder korrekt in die Bild-Datei eingefügt wird, sobald ich auf "windowstate.normal" wechsle. Leider dann mit dem unschönen Nebeneffekt, dass das Panel (mit Transparenzen) kurz aufflackert.

    Ich habe mich schon durch das Web gewühlt und auch ein recht gutes Buch neben mir liegen, das mir WPF näher bringt. Aber ich schaue definitiv in dein Tutorial nochmals rein :)

    Und damit keine Missverständnisse entstehen: Das alte WinForm Projekt existiert noch als altes, eigenständiges Projekt. Ich habe beim Umstieg auf WPF auch nochmal von ganz von vorne angefangen. Also diesen Schritt habe ich schon getätigt. Nur leider bietet WinForms den Vorteil, das Control samt seiner Children direkt in eine Datei zu schreiben. WPF hat dieses Verhalten m.W.n. nicht.

    Gruß Padre