Code for safety II

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