Search code examples
vb.netwinformsgdi+metafile

Picturebox throws "Argument is not valid" when assigning a MetaFile to an image


.Net 4.7.2. Winforms.

Rendering a complex page full of text and graphics. I used a Bitmap. I then show it in a preview window by sizing a PictureBox and placing the picturebox into a panel that scrolls.

It works perfectly to a Bitmap, except of course for the 2GB of RAM it takes. So now I'm trying to go to a MetaFile to save RAM and hopefully make something scalable. However when I do this the preview code blows up with "Parameter is not valid" on Width.GET. This happens on ShowDialog for the Preview window.
I verified that the Metafile was not accidentally disposed. It's still there and passed into the preview window as a byref parameter.

Here is the code:

    ' In the button event handler to create the file
    Dim gr As Graphics = Me.CreateGraphics()
    Dim hdc As IntPtr = gr.GetHdc()
    ' [snip irrelevant code]
    Try
        ' [snip irrelevant code]
        ' [pagewidth and pageheight are paper size in inches * DPI chosen = pixels]
        _pedWMF = New Metafile(hdc, 
                               New Rectangle(0, 0, pageWidth, pageHeight), 
                               MetafileFrameUnit.Pixel)
        _PedigreeCanvas = Graphics.FromImage(_pedWMF)
        _PedigreeCanvas.PageUnit = GraphicsUnit.Pixel

    ' [snip irrelevant code]
    ' Text and images rendered in this call
    RenderPedigree(PedigreeCanvas)
    ' [snip irrelevant code]
    ' Call to preview window to show dialog.

Here is the code in the Preview...

Private Sub LayoutPreviewImage()
    _iPageWidth = Convert.ToInt32(Me.DeviceDpi * (Convert.ToDouble(_drvPaperSize("PS_WIDTH")) / 100))
    _iPageHeight = Convert.ToInt32(Me.DeviceDpi * (Convert.ToDouble(_drvPaperSize("PS_HEIGHT")) / 100))
    SuspendLayout()
    Dim p As New PictureBox()
    p.Name = "pbPreview"
    p.Width = _iPageWidth
    p.Height = _iPageHeight
    '*** The _wmfPreview variable is properly initialized with the Metafile created above.
    If _wmfPreview IsNot Nothing Then p.Image = _wmfPreview
    p.SizeMode = PictureBoxSizeMode.Zoom
    pPagePreview.Controls.Add(p)
    pPagePreview.Location = New Point(0, 0)
    pPagePreview.Width = _iPageWidth + Convert.ToInt32(Math.Round(DeviceDpi * 0.5))
    pPagePreview.Height = _iPageHeight + Convert.ToInt32(Math.Round(DeviceDpi * 0.5))
    p.Left = Convert.ToInt32(Math.Round(DeviceDpi * 0.25))
    p.Top = Convert.ToInt32(Math.Round(DeviceDpi * 0.25))
    p.BackColor = Color.White
    ResumeLayout()
End Sub

Then when I showDialog(), I get the exception.

If I look in the debugger, the MetaFile throws exceptions for width and height and numerous other properties. I know I have something woefully improperly coded, but I cannot figure out what. I used to easily do this with Delphi back in the day. I am missing something; please help me to find it.

Thanks for any help.


Solution

  • OK, the answer was found. Not in the MS Documentation but in a different Stack Overflow answer.

    Whenever you finish working on a Graphics object to draw into a Metafile, you then need to Flush and or Dispose the Graphics object to commit those changes into the MetaFile. Like so:

            RenderPedigree(PedigreeCanvas, PedigreeCanvas)
            PedigreeCanvas.Flush()
            PedigreeCanvas.Dispose()
            PedigreeCanvas = Nothing
    

    That will do the trick.