0

Goal:

My goal is to get a picture (a bmp file) into a RichTextBox, at a size I choose, say 400 pixels high, and width scaled to match, without it being ruined by Windows Scaling (125%, 150%, 200% etc.), or losing quality.

Here's my code:

<DllImport("user32.dll", EntryPoint:="OpenClipboard")> Private Shared Function OpenClipboard(ByVal hWnd As IntPtr) As Boolean
End Function
<DllImport("user32.dll", EntryPoint:="EmptyClipboard")> Private Shared Function EmptyClipboard() As Boolean
End Function
<DllImport("user32.dll", EntryPoint:="SetClipboardData")> Private Shared Function SetClipboardData(ByVal uFormat As Integer, ByVal hWnd As IntPtr) As IntPtr
End Function
<DllImport("user32.dll", EntryPoint:="CloseClipboard")> Private Shared Function CloseClipboard() As Boolean
End Function
<DllImport("gdi32.dll", EntryPoint:="CopyEnhMetaFileA")> Private Shared Function CopyEnhMetaFile(ByVal hemfSrc As IntPtr, ByVal hNULL As IntPtr) As IntPtr
End Function
<DllImport("gdi32.dll", EntryPoint:="DeleteEnhMetaFile")> Private Shared Function DeleteEnhMetaFile(ByVal hemfSrc As IntPtr) As Boolean
End Function

Private Sub PasteMeta(imgPathName As String)
    'Create a bitmap of the image you want to draw to the metafile
    Dim bm As New Bitmap(imgPathName)

    'Create a graphics object of the Form and get its hdc handle
    Dim me_grx As Graphics = Me.CreateGraphics()
    Dim me_hdc As IntPtr = me_grx.GetHdc()
    Dim scale = 400 / bm.Height
    'Create a new metafile the same size as the image and create a graphics object for it
    Dim emf As New Imaging.Metafile("Tmp.emf", me_hdc, New Rectangle(0, 0, bm.Width * scale, 400), Imaging.MetafileFrameUnit.Point)
    Dim emf_gr As Graphics = Graphics.FromImage(emf)


    emf_gr.DrawImage(bm, 0, 0, CInt(bm.Width * scale) * _windowsScaling, CInt(bm.Height * scale) * _windowsScaling)

    'Dispose the bitmap and the metafile graphics object
    emf_gr.Dispose()
    bm.Dispose()

    PutEnhMetafileOnClipboard(Me.Handle, emf)
    'Paste the new metafile in the RichTextBox
    JournalRichTextBox.Paste()
    'Dispose the rest of the objects we created
    me_grx.ReleaseHdc(me_hdc)
    me_grx.Dispose()
    emf.Dispose()
End Sub

Private Shared Function PutEnhMetafileOnClipboard(ByVal hWnd As IntPtr, ByVal mf As Imaging.Metafile) As Boolean
    Dim bResult As New Boolean()
    bResult = False
    Dim hEMF, hEMF2 As IntPtr
    hEMF = mf.GetHenhmetafile() ' invalidates mf
    If Not hEMF.Equals(New IntPtr(0)) Then
        hEMF2 = CopyEnhMetaFile(hEMF, New IntPtr(0))
        If Not hEMF2.Equals(New IntPtr(0)) Then
            If OpenClipboard(hWnd) Then
                If EmptyClipboard() Then
                    Dim hRes As IntPtr
                    hRes = SetClipboardData(14, hEMF2)    ' 14 == CF_ENHMETAFILE
                    bResult = hRes.Equals(hEMF2)
                    CloseClipboard()
                End If
            End If
        End If
        DeleteEnhMetaFile(hEMF)
    End If
    Return bResult
End Function

Here's what happens to the RTB when I run the sub: https://i.stack.imgur.com/QaC3b.png

Background and what I've tried:

My first go was to just use the bitmap size property, but that really ruins the quality - it's completely useless.

Then I found this magical way that sorted the quality. To be brief, it's the top answer here: https://social.msdn.microsoft.com/Forums/vstudio/en-US/355bfc59-cd0a-4e81-984e-9f066fd3a897/richtextbox-images-quality-loss?forum=vbgeneral

In summary, it makes a metafile and graphic, does the resizing with it and puts it into the clipboard and then pastes that into the richtextbox.

I got that working nicely. However, I have my 4K screen scaled at 200% and that means that the picture is the right size but the metafile it is put in is twice the size.

I have spent about 4 hours googling and trying everything I could find to work around this. No matter what I do, the DPI I get for the form, or controls within it, is always 96, so I can't use one of the DpiX solutions.

If I add:

<DllImport("User32.dll")>
Private Shared Sub SetProcessDPIAware()
End Sub

Public Sub New()
    SetProcessDPIAware()
    InitializeComponent()
End Sub

It just makes the form unscaled (some buttons and labels still are scaled though - weird!)

Trying to override the scalecontrol event always returns a factor.X, factor.Y = 1, never anything else.

What I'd like if possible:

Please note that the BEST solution would be to just get a number for the scale factor. 125% = 1.25, 150% = 1.5 etc. If I have that number, I can just multiply the dimensions of the graphic by it and it fits perfectly (for example)

I think my main problem is I've got this working using too much code that I just don't understand. I would really appreciate you could bare that in mind when answering, I might not understand your answer if it's giving me too much doubt-benefit. I think the solution might be here: https://msdn.microsoft.com/en-us/library/cc250585.aspx

But that's way over my head at the mo. So any help or advice would be REALLY appreciated.

FYI I am targeting 3.5 and would like to stick to that if possible.

Dr. Kaii
  • 15
  • 2
  • If you want to get the display scale [How to get Windows Display settings?](https://stackoverflow.com/questions/5977445/how-to-get-windows-display-settings) – γηράσκω δ' αεί πολλά διδασκόμε Aug 15 '18 at 21:01
  • About Dpi Awareness, [some notes I've written](https://stackoverflow.com/questions/50239138/dpi-awareness-unaware-in-one-release-system-aware-in-the-other?answertab=active#tab-top) and [this answer](https://stackoverflow.com/questions/13228185/how-to-configure-an-app-to-run-correctly-on-a-machine-with-a-high-dpi-setting-e?answertab=active#tab-top). Do you really need an Emf for this? If not, I posted [something related](https://stackoverflow.com/questions/51789730/image-size-when-capturing-a-screenshot-of-another-application-changes-if-applica?answertab=active#tab-top) the other day. – Jimi Aug 15 '18 at 21:45
  • Jimi - If I try SetProcessDpiAwarenessContext(-4) (for example) things start working, but is there any way to get it to still scale up? It makes the form small – Dr. Kaii Aug 15 '18 at 23:55
  • Have you setup your `app.manifest` as described in those notes? What is the scaling mode of your Form? Does it scale to Dpi? Are you using some anchoring model to resize your controls. Docking could work, as a wise combination of anchors and a remix of both. `TableLayoutPanels`/`FlowLayoutPanels` and such can help even more. – Jimi Aug 16 '18 at 00:05
  • Scale to Dpi, anchoring and docking are all fine, it's just small. The for itself isn't being scaled up to match the screen scaling, neither are any of the buttons etc. I didn't do the manifest because it's a clickOnce deployment – Dr. Kaii Aug 16 '18 at 08:50

0 Answers0