Back when I wrote Code for Safety I gave one example of something that I’m sure we’ve all done from time to time. Saving the state of some flag and then returning and having possibly multiple places to reset the flag we saved.
There’s another thats is more common with the advent of HiDPI support for Windows; drawing in a listbox event like CellBackGroundPaint Saving the graphics state, disabling antialiasing, and then needing to restore that state.
This is especially true on Windows where you may need to disable anti-aliasing, change the foreground color & fill the background to achieve some custom effect. If you dont disable anti-aliasing you can end up with odd effects as noted in the documentation and this post.
Having a way to save the existing state and consisntenly and reliably restore it to what it was when the method was called is useful since you dont know if the graphics object is passed on to other events like CellTextPaint. Not restoring the state may lead to unwanted side effects.
So how to make it so you can simply save the state and never worry about it being restored properly ? As I mentioned before ; a class that has almost nothing to it but a constructor & a destructor.
My implementation is called GraphicsStatePreserver.
The constructor is simply
Sub Constructor(g as graphics)
mG = g
Me.mAntiAlias = g.AntiAlias
Me.mAntiAliasMode = g.AntiAliasMode
Me.mBold = g.Bold
Me.mCharacterSpacing = g.CharacterSpacing
Me.mForeColor = g.ForeColor
Me.mItalic = g.Italic
Me.mLastPage = g.LastPage
Me.mPenHeight = g.PenHeight
Me.mPenWidth = g.PenWidth
Me.mScaleX = g.ScaleX
Me.mScaleY = g.ScaleY
Me.mTextFont = g.TextFont
Me.mTextSize = g.TextSize
me.mtextunit = g.TextUnit
Me.mTransparency = g.Transparency
Me.mUnderline = g.Underline
End Sub
We save the current state of the various writeable properties of the graphics into locals, and a hard reference to the graphics object. We keep a hard reference to make sur it cannot go out of scope before we have done our thing to restore the various settings.
The destructor is equally simple
Sub Destructor()
mg.AntiAlias = Me.mAntiAlias
mg.AntiAliasMode = Me.mAntiAliasMode
mg.Bold = Me.mBold
mg.CharacterSpacing = Me.mCharacterSpacing
mg.ForeColor = Me.mForeColor
mg.Italic = Me.mItalic
mg.PenHeight = Me.mPenHeight
mg.PenWidth = Me.mPenWidth
mg.ScaleX = Me.mScaleX
mg.ScaleY = Me.mScaleY
mg.TextFont = Me.mTextFont
mg.TextSize = Me.mTextSize
mg.textunit = Me.mTextUnit
mg.Transparency = Me.mTransparency
mg.Underline = Me.mUnderline
End Sub
When this class is destroyed it will put back all the properties on the graphics just as they were when the instance was created.
And since an instance is used like
Dim statepreserver As New GraphicsStatePreserver(g)
#If targetwindows
g.AntiAlias = False
#EndIf
If row < 0 Then
Return True
End If
If column < 0 Then
Return True
End If
// whatever othe r code you need in cell background paint
Return True
This instance will ONLY go out of scope, and be destroyed, at the points where the event is being exited. And that happens to be the exact points we want to restore the graphics state.
Here’s the entire implementation
Class GraphicsStatePreserver
Sub Constructor(g as graphics)
mG = g
Me.mAntiAlias = g.AntiAlias
Me.mAntiAliasMode = g.AntiAliasMode
Me.mBold = g.Bold
Me.mCharacterSpacing = g.CharacterSpacing
Me.mForeColor = g.ForeColor
Me.mItalic = g.Italic
Me.mLastPage = g.LastPage
Me.mPenHeight = g.PenHeight
Me.mPenWidth = g.PenWidth
Me.mScaleX = g.ScaleX
Me.mScaleY = g.ScaleY
Me.mTextFont = g.TextFont
Me.mTextSize = g.TextSize
me.mtextunit = g.TextUnit
Me.mTransparency = g.Transparency
Me.mUnderline = g.Underline
End Sub
Sub Destructor()
mg.AntiAlias = Me.mAntiAlias
mg.AntiAliasMode = Me.mAntiAliasMode
mg.Bold = Me.mBold
mg.CharacterSpacing = Me.mCharacterSpacing
mg.ForeColor = Me.mForeColor
mg.Italic = Me.mItalic
mg.PenHeight = Me.mPenHeight
mg.PenWidth = Me.mPenWidth
mg.ScaleX = Me.mScaleX
mg.ScaleY = Me.mScaleY
mg.TextFont = Me.mTextFont
mg.TextSize = Me.mTextSize
mg.textunit = Me.mTextUnit
mg.Transparency = Me.mTransparency
mg.Underline = Me.mUnderline
End Sub
Private Property mAntiAlias As boolean
Private Property mAntiAliasMode As Graphics.AntiAliasModes
Private Property mBold As boolean
Private Property mCharacterSpacing As Integer
Private Property mForeColor As color
Private Property mG As graphics
Private Property mItalic As boolean
Private Property mLastPage As Integer
Private Property mPenHeight As double
Private Property mPenWidth As double
Private Property mScaleX As double
Private Property mScaleY As double
Private Property mTextFont As string
Private Property mTextSize As Single
Private Property mtextunit As FontUnits
Private Property mTransparency As double
Private Property mUnderline As boolean
End Class