Buffered Graphics Class
Buffered Graphics Class
Buffered Graphics Class
Buffered Graphics Class
Buffered Graphics Class
Buffered Graphics Class Buffered Graphics Class Buffered Graphics Class Buffered Graphics Class Buffered Graphics Class Buffered Graphics Class Buffered Graphics Class Buffered Graphics Class
Buffered Graphics Class Buffered Graphics Class
Buffered Graphics Class
Go Back  Xtreme Visual Basic Talk > > > Buffered Graphics Class


Reply
 
Thread Tools Display Modes
  #1  
Old 02-21-2013, 02:40 AM
CodeCruncher CodeCruncher is offline
Junior Contributor
 
Join Date: Jul 2006
Posts: 355
Default Buffered Graphics Class


I downloaded the MS BufferedGraphics class code example and have spent a few hours going through it. I have trimmed out all the code that allows you to alternate through the different buffering options, as I am only interested in the Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True) section of the code.

I got that working without any problems, but I wanted to see if I could get the buffered graphics working on a Groupbox or Picturebox instead of Me.

I have changed all the points where I can see it hooking into Me but it is still drawing on the form. Can someone please point out what I have missed.

Code:
Imports System.ComponentModel
Imports System.Windows.Forms
Imports System.Drawing
Imports System

Public Class BufferingExample

    Inherits Form
    Private context As BufferedGraphicsContext
    Private grafx As BufferedGraphics
    Private count As Byte

    Public Sub New()

        Me.SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.UserPaint, True)
        context = BufferedGraphicsManager.Current
        InitializeComponent()

    End Sub

    Private Sub MouseDownHandler(sender As Object, e As MouseEventArgs) Handles Me.MouseDown

        If e.Button = MouseButtons.Right Then
            Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
            ClearBuffer()
            DrawToBuffer(grafx.Graphics)
            Me.Refresh()
        Else
            If Timer1.Enabled Then
                Timer1.Stop()
            Else
                Timer1.Start()
            End If
        End If

    End Sub

    Private Sub ResizeHandler(sender As Object, e As EventArgs) Handles Me.Resize

        context.MaximumBuffer = New Size(GroupBox1.Width + 1, GroupBox1.Height + 1)
        If (grafx IsNot Nothing) Then
            grafx.Dispose()
            grafx = Nothing
        End If
        grafx = context.Allocate(GroupBox1.CreateGraphics(), New Rectangle(0, 0, GroupBox1.Width, GroupBox1.Height))
        ClearBuffer()
        DrawToBuffer(grafx.Graphics)
        GroupBox1.Refresh()

    End Sub

    Private Sub DrawToBuffer(g As Graphics)

        If count = 6 Then ClearBuffer()
        count += 1
        Dim rnd As New Random()
        For i As Integer = 0 To 21
            Dim px As Integer = rnd.Next(20, GroupBox1.Width - 40)
            Dim py As Integer = rnd.Next(20, GroupBox1.Height - 40)
            g.DrawEllipse(New Pen(Color.FromArgb(rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255)), 1) _
            , px, py, px + rnd.Next(0, GroupBox1.Width - px - 20), py + rnd.Next(0, GroupBox1.Height - py - 20))
        Next

    End Sub

    Private Sub OnTimer(sender As Object, e As EventArgs) Handles Timer1.Tick
        DrawToBuffer(grafx.Graphics)
        GroupBox1.Refresh()
    End Sub

    Protected Overrides Sub OnPaint(e As PaintEventArgs)
        grafx.Render(e.Graphics)
    End Sub

    Private Sub ClearBuffer()
        grafx.Graphics.FillRectangle(Brushes.Black, 0, 0, GroupBox1.Width, GroupBox1.Height)
        count = 0
    End Sub

    <STAThread()> Public Shared Sub Main(args() As String)
        Application.Run(New BufferingExample())
    End Sub

End Class
Attached Files
File Type: zip DblBuffered.zip (16.7 KB, 3 views)
Reply With Quote
  #2  
Old 02-21-2013, 03:42 AM
PlausiblyDamp's Avatar
PlausiblyDampBuffered Graphics Class PlausiblyDamp is offline
Ultimate Contributor

Forum Leader
* Expert *
 
Join Date: Nov 2003
Location: Newport, Wales
Posts: 2,058
Default

The class is a form, the second line of the class is
Code:
Inherits Form
if you wanted to use it as a buffered picturebox you could try inheriting from picturebox and therefore creating a customer control for yourself.
__________________
Intellectuals solve problems; geniuses prevent them.
-- Albert Einstein

Posting Guidelines Forum Rules Use the code tags
Reply With Quote
  #3  
Old 02-21-2013, 04:49 AM
CodeCruncher CodeCruncher is offline
Junior Contributor
 
Join Date: Jul 2006
Posts: 355
Default

Ok I’ve never used Inherit before (although recently I’ve been meaning to ask about it). If I understand what you’re saying I need to create another class called Picturebox or Groupbox and call it through the “Inherit Groupbox” or "Inherit Picturebox". Am I in the ball park or way off?

Found this http://msdn.microsoft.com/en-us/library/c8shwxa5.aspx going to see if I can make some sense of it.

Last edited by CodeCruncher; 02-21-2013 at 05:10 AM.
Reply With Quote
  #4  
Old 02-21-2013, 10:51 AM
passel's Avatar
passelBuffered Graphics Class passel is offline
Sinecure Expert

Super Moderator
* Guru *
 
Join Date: Jun 2003
Location: Upstate New York, usa
Posts: 8,028
Default

Looks like you have a mix of things there.
It appears you are creating an object, perhaps a control, but then you have hardcoded in your use of the BufferedGraphics object a control, GroupBox1.
If you just want to use a BufferedGraphics object for GroupBox1, then you don't need to create control or a class for that.
If you want to create a class that can be reused for different controls, then I would think the class would be a little simpler.
I would think you would pass the control you wanted to use your class on to the constructor. It could then instantiate and allocate a buffer for that object, and perhaps add handlers, etc. but I don't know precisely since I haven't tried to create a class that you would pass a control to and have it set up handlers, etc.

The minimum effort test of using the BufferedGraphics object to handle the drawing of the screen area of a particular control would be to create the context and graphics objects, initialize them, then add code in the particular control's event handlers, i.e. resize and paint, to modify or use the BufferedGraphics object.

I'm not sure that trying to push too much of that down into a class object makes sense, in particular, the DrawToBuffer code would probably be unique for each instance so would likely live outside of the class itself. The class would probably provide the graphics object to use, but not have the drawing logic itself contained within it.
The Picturebox and GroupBox controls don't contain a method that will draw stuff. They provide a graphics object in the paint event for you to use to draw.
On the otherhand, if you were creating a specific control, to draw graphs, charts, calendars, etc. then that would be a specific purpose control, not a generic backbuffer drawing class.

[edit:]
"since I haven't tried to create a class that you would pass a control to and have it set up handlers, etc..."

I went ahead and gave it try for myself, not that I would probably approach it this way as I don't really see the need for adding extra levels of abstraction for the most part, but perhaps someday this might fit a need.
So, I did a quick code up of what I've quoted above, plus added a bit of your drawing test into the class.
The example form has a groupbox and a picturebox on it (the picturebox I went ahead and put it inside the groupbox).
I set the anchors so the groupbox and picturebox will resize when you resize the form, just so the resize events will happen.
The timer will call the DrawToBuffer method for each class added to the list.
{p.s. Noticed after posting that "Dim t1 as GenBufferedGraphics" is still in the code. That was from my initial test with a single groupbox. I then added the "ControlList" to handle more than one control, so the "t1" declaration is unused code that should be removed.}

The form code:
Code:
Public Class Form1

  Dim t1 As GenBufferedGraphics
  Dim ControlList As New List(Of GenBufferedGraphics)

  Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    ControlList.Add(New GenBufferedGraphics(GroupBox1))
    ControlList.Add(New GenBufferedGraphics(PictureBox1))
    Timer1.Start()
  End Sub

  Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick
    For Each c As GenBufferedGraphics In ControlList
      c.DrawToBuffer()
    Next
  End Sub
End Class
The GenBufferedGraphics class code:
Code:
Imports System.Drawing

Public Class GenBufferedGraphics

  Private gContext As BufferedGraphicsContext
  Public gfx As BufferedGraphics
  Private WithEvents _c As Control
  Private rnd As New Random()

  Public Sub New(ByRef c As Control)
    gContext = BufferedGraphicsManager.Current
    _c = c
    gfx = gContext.Allocate(_c.CreateGraphics, _c.DisplayRectangle)

    AddHandler _c.Resize, AddressOf ResizeHandler
    AddHandler _c.Paint, AddressOf PaintHandler
  End Sub

  Private Sub ResizeHandler(sender As Object, e As System.EventArgs)
    If gfx IsNot Nothing Then
      gfx.Dispose()
    End If
    gfx = gContext.Allocate(_c.CreateGraphics, _c.DisplayRectangle)
  End Sub

  Public Sub DrawToBuffer()
    Dim w As Single = gfx.Graphics.VisibleClipBounds.Width
    Dim h As Single = gfx.Graphics.VisibleClipBounds.Height
    For i As Integer = 0 To 21
      Dim px As Integer = rnd.Next(20, w - 40)
      Dim py As Integer = rnd.Next(20, h - 40)
      gfx.Graphics.DrawEllipse(New Pen(Color.FromArgb(rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255)), 1) _
      , px, py, px + rnd.Next(0, w - px - 20), py + rnd.Next(0, h - py - 20))
    Next
    gfx.Render()
  End Sub

  Private Sub PaintHandler(sender As Object, e As System.Windows.Forms.PaintEventArgs)
    gfx.Render()
  End Sub
End Class
p.p.s Since the "rnd" objects are created at almost the same time, they end up with the same seed, so sequence in the same series, so you'll see that you get the same drawing synchronized in both the Groupbox and the picturebox.
You would ideally only want to instantiate your random number generator once and have the various instances use it so none of them are going to replicate the other.
So, in the GenBufferedGraphics class I would change:

Private rnd as New Random()
to:
Private Shared rnd as New Random()

so that all instances of the class use the same random number generator, and you will see that backgrounds will no longer draw the same synchronized image.
Also note that I used your drawing code, and you create the Pen when you pass the parameter, but I would not normally do that as it doesn't give you the opportunity to dispose of the pen.
I would declare the Pen outside the loop
Dim myPen As New Pen(Color.FromArgb(rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255)))

Would use it, then change the color without recreating a new pen in the loop
gfx.Graphics.DrawEllipse(myPen, px, py, px + rnd.Next(0, w - px - 20), py + rnd.Next(0, h - py - 20))
myPen.Color = Color.FromArgb(rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255))

And finally dispose of it after I'm done with it after the loop.
..Next
myPen.Dispose()
Attached Files
File Type: zip BufferedGraphicGenericClassTest.zip (17.3 KB, 13 views)
__________________
There Is An Island Of Opportunity In The Middle of Every Difficulty.
Miss That, Though, And You're Pretty Much Doomed.

Last edited by passel; 02-21-2013 at 01:30 PM. Reason: Update
Reply With Quote
  #5  
Old 02-21-2013, 05:30 PM
CodeCruncher CodeCruncher is offline
Junior Contributor
 
Join Date: Jul 2006
Posts: 355
Default

Thanks passel, unfortunately most of what you said went way over my head, but your code works, so I’m sure if I step through it slowly I will be able to see the logic of what you said in the code.

As per my first post that is just MS sample code I was primarily trying to work out how to render the graphic to a picturebox or groupbox instead of the form. Your code does exactly that, so thank you for your assistance.
Reply With Quote
  #6  
Old 02-21-2013, 07:58 PM
CodeCruncher CodeCruncher is offline
Junior Contributor
 
Join Date: Jul 2006
Posts: 355
Default

I rewrote the Form1 part of the code as I only need either a picturebox or a groupbox displaying the graphic, can you please take a look at the changes below and confirm if I have done it right.

It works but that doesn't mean I've done it right.

Code:
Public Class Form1

    'Dim t1 As GenBufferedGraphics
    'Dim ControlList As New List(Of GenBufferedGraphics)
    Dim _gbg As GenBufferedGraphics

    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load

        'ControlList.Add(New GenBufferedGraphics(GroupBox1))
        'ControlList.Add(New GenBufferedGraphics(PictureBox1))
        _gbg = New GenBufferedGraphics(GroupBox1)
        Timer1.Start()

    End Sub

  Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick

        'For Each c As GenBufferedGraphics In ControlList
        '    c.DrawToBuffer()
        'Next
        _gbg.DrawToBuffer()

  End Sub
End Class

Last edited by CodeCruncher; 02-21-2013 at 08:08 PM.
Reply With Quote
  #7  
Old 02-21-2013, 09:50 PM
CodeCruncher CodeCruncher is offline
Junior Contributor
 
Join Date: Jul 2006
Posts: 355
Default

I have changed the object in focus to the PictureBox1 to make sure i could get both to work...

I have a bit of a question around this bit of code where all the magic seems to take place.

I can see with the ByRef a copy of the Picturebox is being passed to this class, and then that is being passed to the locally declared Control here... Ctrl = control

Code:
    Private WithEvents Ctrl As Control

    Public Sub New(ByRef control As Control) 'Passing all the properties from the Me.Control (in this case the Picturebox1) to "New" for use in this class.

        Ctrl = control
        gContext = BufferedGraphicsManager.Current
        BuffGrafx = gContext.Allocate(Ctrl.CreateGraphics, Ctrl.DisplayRectangle)
        AddHandler Ctrl.Resize, AddressOf ResizeHandler 'Note: needs to be here and not on the end of Method. Why? because on resize it will ignore it.
        AddHandler Ctrl.Paint, AddressOf PaintHandler

    End Sub
But when it finally gets told to Render the BuffGrafx how does it transfer that back to the "real" Picturebox1? Is there somekind of link being established between the two that BuffGrafx.Render() knows it is going to draw on the Me.Picturebox1?

Code:
    Private Sub PaintHandler(sender As Object, e As System.Windows.Forms.PaintEventArgs)
        BuffGrafx.Render()
    End Sub
Reply With Quote
  #8  
Old 02-21-2013, 09:56 PM
PlausiblyDamp's Avatar
PlausiblyDampBuffered Graphics Class PlausiblyDamp is offline
Ultimate Contributor

Forum Leader
* Expert *
 
Join Date: Nov 2003
Location: Newport, Wales
Posts: 2,058
Default

Quote:
I can see with the ByRef a copy of the Picturebox is being passed to this class
Quote:
But when it finally gets told to Render the BuffGrafx how does it transfer that back to the "real" Picturebox1?
The ByRef means it is passing a reference to the picturebox, that means all of the code that refers to Ctrl is operating on the picturebox you passed into the constructor.
__________________
Intellectuals solve problems; geniuses prevent them.
-- Albert Einstein

Posting Guidelines Forum Rules Use the code tags
Reply With Quote
  #9  
Old 02-21-2013, 10:15 PM
CodeCruncher CodeCruncher is offline
Junior Contributor
 
Join Date: Jul 2006
Posts: 355
Default

Ahh ok I thought it was passing the reference the other way into the class.

Just thinking back to many moons ago to VB6 days about passing things as ByVal (value) and ByRef (reference) and the difference between them, and then realised that it is a two way transaction I was thinking it was only one way, but then I remembered what happens when you pass something like a boolean to a Function.

Last edited by CodeCruncher; 02-21-2013 at 10:43 PM.
Reply With Quote
  #10  
Old 02-21-2013, 11:21 PM
passel's Avatar
passelBuffered Graphics Class passel is offline
Sinecure Expert

Super Moderator
* Guru *
 
Join Date: Jun 2003
Location: Upstate New York, usa
Posts: 8,028
Default

The word "to" can be interpreted different ways here.
A reference "to" the picturebox is being passed "to" the class.
A reference is a pointer to an object, not the object itself.
All class objects instantiated with "new" are reference types.
Me.Picturebox1 is a reference to the underlying structure that makes up the control Picturebox1.
This reference is passed to the genBufferedGraphics constructor which makes a copy of it in the _c variable, so now you have two references to the Picturebox1 object. You can manipulate the picturebox through the Picturebox1 reference in the form1 class, or through the _c reference in the genBufferedGraphics class.

Quote:
Is there somekind of link being established between the two that BuffGrafx.Render() knows it is going to draw on the Me.Picturebox1?
Actually, the BufferedGraphics class establishes a separate "link", but not to the Picturebox1 object.
It maintains a link to a default Graphics object which you passed it when allocating the buffer.

gfx = gContext.Allocate(_c.CreateGraphics, _c.ClientRectangle)

_c points to the Picturebox1 (or GroupBox1 in the other instantiation), and the CreateGraphics function call returns a Graphics object that allows you to draw on the screen area of the control.
If you don't specify a graphics object in the render method,

gfx.render()

it renders using the default graphics object you gave it.
But, you can also pass a graphics object to the render method to render the same image elsewhere, like another control, or printer.

gfx.render(e.Graphics)

I have a second version of the previously posted example, and this time I added more comments to help explain in more detail what various lines are doing.
It so happens that this example uses both the default (no parameter) render call and a render call with a parameter. The call with a parameter was needed to properly handle the paint event of the picturebox. It wasn't too noticable in the first example because the timer continually cause the graphics to be updated.
In this example, the timer has been removed and the drawing is updated when you drag the mouse back and forth across the control.

What the BufferedGraphics class does when you render is write directly to the screen area of the control whose Graphics object you passed it. But, since the picturebox is double buffered, the Paint event passes you a different Graphics object, pointing to a back buffer. You would normally do your drawing using that graphics object and when you exit the paint event, the Picturebox control will "flip" the final image to the screen. In our case, since we're using a BufferedGraphics object, when we get a Paint event, we would render the buffer to the screen in the paint event (using the default no parameter call), and then the Paint event would complete and flip its back buffer to the screen, wiping out what we just rendered.
Since you have a timer always re-rendering to the screen, that wipe out is barely noticable, since the screen area of the picturebox would be re-rendered on the next tick.

Since this example is not continually re-rendering, I changed the Paint event to properly use the graphics object passed to the Paint event, but in all other cases, i.e. mouseMove and ReSize events, we render directly to the screen speeding the update significantly.

So, hopefully the comments in this example, along with the different drawing objective, will give another view into the world of the BufferedGraphics object.

Hopefully the code will explain itself for the most part, just resize the form and drag horizontally on either control to see all that this example does.

p.s. I didn't double check, but I got the impression in your code you were calculating the rectangles for the ellipses like you would in VB6, determing the upper-left and lower-right corners (x1,y1)-(x2,y2) .
In .Net, rectangles are defined by the upper-left corner and width and height(x,y, w,h).
Like I said, I didn't double check so you may have been doing it correctly, but my impression was that if this was in VB6 all the ellipses would have been contained in the box, where as in .Net the ellipses were contained on the left and top, but were drawing off the right and bottom of the window, which is part of what gave me that impression.

p.p.s I did a double check, and the code does look like it should be calculating a proper w,h so I must have been seeing things. It just seems to me it was leaving a 20 pixel black clear area at the left and top, but not at the right and bottom. I guess I'll have to re-download the code at some point since I don't have that version any longer, but I'm up too late as it is, so not now (and I do see that the code came from a Microsoft example so you would be the one to blame in any case).
Attached Files
File Type: zip BufferedGraphicGenericClassTest2.zip (19.7 KB, 13 views)
__________________
There Is An Island Of Opportunity In The Middle of Every Difficulty.
Miss That, Though, And You're Pretty Much Doomed.

Last edited by passel; 02-22-2013 at 12:16 AM.
Reply With Quote
  #11  
Old 02-22-2013, 12:24 AM
CodeCruncher CodeCruncher is offline
Junior Contributor
 
Join Date: Jul 2006
Posts: 355
Default

"p.s. I didn't double check, but I got the impression in your code you were calculating the rectangles for the ellipses like you would in VB6, determing the upper-left and lower-right corners (x1,y1)-(x2,y2) ."

It would be Microsoft that would be right or wrong, as it is part of their sample code not mine

I just trimmed their code to learn how to use double buffer better...

BTW this is a very cool way to get a random colour
myBrush.Color = Color.FromArgb(rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255)) 'Choose a new random color for the next one


I'm not using a timer as the program I want to use this for is refreshed based on mouse movements so your second example will probably be very much on the mark of what I need to do.

Thanks once again the comments help understand what is going on and I plan to keep this for reference. One question in the comments you said
"'The the Ellipse three times," did you mean to type 'Draw the Ellipse three times,...?
Reply With Quote
  #12  
Old 02-22-2013, 06:21 AM
passel's Avatar
passelBuffered Graphics Class passel is offline
Sinecure Expert

Super Moderator
* Guru *
 
Join Date: Jun 2003
Location: Upstate New York, usa
Posts: 8,028
Default

Yes, Draw, not The. I noticed that later just before going to bed so "fixed" it in my code, but since the code was already posted for a 1/2 hour I didn't go through the effort of deleting it and reposting. It was during that 1/2 hour is when I also saw your other thread and followed the link and realized that code was from Microsoft and on closer inspection looks like it should be correct (hence the p.p.s in the previous thread at 1:16am (my time)). You were obviously looking at the code during this same period so even if I had reposted "the "The/Draw" fix" you wouldn't have seen it having already downloaded the example.
__________________
There Is An Island Of Opportunity In The Middle of Every Difficulty.
Miss That, Though, And You're Pretty Much Doomed.
Reply With Quote
  #13  
Old 02-22-2013, 07:25 PM
CodeCruncher CodeCruncher is offline
Junior Contributor
 
Join Date: Jul 2006
Posts: 355
Default

Many thanks I have updated the copy I downloaded. Next step is to try incorporate what I have learned into my existing project. Should be fairly straight forward (famous last words) I most need to change the FillBuffer() section.
Reply With Quote
  #14  
Old 02-22-2013, 07:39 PM
surfR2911 surfR2911 is offline
Contributor
 
Join Date: Oct 2009
Posts: 719
Default Small tweaks..

I would note that in both attachments, this line in GenBufferedGraphics.vb:
Code:
Public gfx As BufferedGraphics
..can be changed to this:
Quote:
Private gfx As BufferedGraphics
..and it still works.

In fact, in the second attachment you can change this GenBufferedGraphics.vb sub line:
Code:
Public Sub DrawToBuffer()
..to a more limited (tighter) scope:
Code:
Private Sub DrawToBuffer()
..and it still works.

This is one of the "special-ness" involved in using the buffered graphics class (of which passel is the forum's BufferedGraphics expert).

You are not "handing off" anything on the form (like setting a control's image to something).
There is simply nothing like that in the form's code:
Code:
Public Class Form1
  Dim ControlList As New List(Of GenBufferedGraphics)
  Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    ControlList.Add(New GenBufferedGraphics(GroupBox1))
    ControlList.Add(New GenBufferedGraphics(PictureBox1))
  End Sub
End Class
The actual "hookup" (between class buffer and the form’s control) is created in the GenBufferedGraphics.vb's New constructor:
Code:
Public Sub New(ByRef c As Control)
    gContext = BufferedGraphicsManager.Current
    _c = c
    gfx = gContext.Allocate(_c.CreateGraphics, _c.DisplayRectangle)
    AddHandler _c.Resize, AddressOf ResizeHandler
    AddHandler _c.Paint, AddressOf PaintHandler
End Sub
I know to some people this is like:
*yawn* "Why do you even need to point out this out?"

It's just that if you look around the internet,
at hundreds (if not thousands) of examples of the way graphics are handled using VB.Net code,
this technique is not (in any way) "commonplace".

The other thing, looking at the second attachment:
"Why only horizontal drag scrolling?"
Adding just a few lines and you have a working example of drag scrolling both horizontally, vertically, or both (diagonally).

Last edited by surfR2911; 02-22-2013 at 07:57 PM.
Reply With Quote
  #15  
Old 02-22-2013, 08:03 PM
CodeCruncher CodeCruncher is offline
Junior Contributor
 
Join Date: Jul 2006
Posts: 355
Default

Thanks SurfR2911 for the optimisations, I tend to get things to work first then see if I can narrow it down afterwards (and remove any variables I created along the way that are no longer needed).

I knew I should have tried fitting the code in before saying it would be easy...

I copied across the code to my project and called the class BuffGrafx and copy and pasted the form code as well.

Now being hit by an error message "Option Strict On disallows narrowing from type 'System.Windows.Forms.Control' to type 'System.Windows.Forms.GroupBox' in copying the value of 'ByRef' parameter 'c' back to the matching argument."

Option Strict On is set at VS2012 level not on the form, so not sure why it thinks I am norrowing now and not in the other project.

Edit:
Fixed the problem by changing from a Control to Picturebox / Groupbox Public Sub New(ByRef c As PictureBox)
Still works like before...

Last edited by CodeCruncher; 02-22-2013 at 08:27 PM.
Reply With Quote
  #16  
Old 02-22-2013, 09:31 PM
surfR2911 surfR2911 is offline
Contributor
 
Join Date: Oct 2009
Posts: 719
Default Re: Scoping and multi-directionality in drag scrolling..

Quote:
Thanks SurfR2911 for the optimisations..
Uh..maybe I should explain a little more...

In regard to scoping (Public versus Private) its not really a "performance" optimization as much as it is a desirable code practice.
It's a good thing to try to make variables/objects as local as possible.

You've started to explore OOP principles, and scoping is related to the object oriented principle called encapsulation.

Even if you are not seriously into "OO", the smaller the scoping of variables it usually leads to:
a.) less code that uses a variable or object (good - see #2)
b.) less code that has to be stepped through (debugging) as the variable/object is used (undergoes value changes?) at runtime.
c.) less confusion when using local variables and public variables with the same name (something to be avoided).

The other issue that comes up - lifetimes.
This article goes into scoping lifetimes a little.

Basically, though, as long as something (variable/object) is still use (does not go out of scope)
it generally has to stay in memory (and can't be garbage collected).

Or as the MSDN Scope page "officially" puts it:
Quote:
Memory Consumption.
Local variables consume memory only while their procedure is running.
Their memory is released when the procedure returns to the calling code.

By contrast, Shared (Visual Basic) and Static (Visual Basic) variables consume memory resources until your application stops running, so use them only when necessary.

Instance variables consume memory while their instance continues to exist, which makes them less efficient than local variables,
but potentially more efficient than Shared or Static variables.

The other issue is with something like File I/O.
It a filestream is left open (due to too large a scope) then it leaves the file open (and can't be moved or altered by other operating system routines).


I would also note on the MSDN Using statement page it says in the Remarks section:
Quote:
Sometimes your code requires an unmanaged resource, such as a file handle, a COM wrapper, or a SQL connection.

A Using block guarantees the disposal of one or more such resources when your code is finished with them.
This makes them available for other code to use.
Knowing how much you appreciate getting expert knowledge from AtmaWeapon,
you might also want to read this AW's post regarding "Using" --and that way it saves having to manually Dispose().

The attachment I posted in my last post is not really a performance upgrade,
just want to show how to adjust passel's code to be able to drag-scroll in both directions.

Last edited by surfR2911; 02-22-2013 at 09:46 PM.
Reply With Quote
  #17  
Old 02-22-2013, 09:44 PM
passel's Avatar
passelBuffered Graphics Class passel is offline
Sinecure Expert

Super Moderator
* Guru *
 
Join Date: Jun 2003
Location: Upstate New York, usa
Posts: 8,028
Default

Yeah, the original example this was cut down from was calling those routines from the GUI timer at one point or another which is why they were Public. I didn't think to change them when I removed the calls from the GUI.

As a matter of fact I did add vertical scrolling as well for the heck of it. Also increased the size of the bitmap to 3x so that rather than have it look as if there was one "tile" that was being scrolled, i.e. as the pixels scrolled out one side they are immediately scrolling in on the other side, to looking like the "scroll" area was 2 times the size of the window.

The one thing you didn't do is make the vertical scroll also seamless.
To make the horizontal seamless we had to draw the ellipses three times.
To make the vertical seamless, we have to draw those three ellipses, three times vertically as well, a total of nine ellipses, representing one ellipse at different scroll positions.
So, copy the first set of three and paste twice, and add +/- offsets to the y position.
Code:
'
      g.FillEllipse(myBrush, px, py, sw, sh)
      g.FillEllipse(myBrush, px + w, py, sw, sh)
      g.FillEllipse(myBrush, px - w, py, sw, sh)

      g.FillEllipse(myBrush, px, py - h, sw, sh)
      g.FillEllipse(myBrush, px + w, py - h, sw, sh)
      g.FillEllipse(myBrush, px - w, py - h, sw, sh)

      g.FillEllipse(myBrush, px, py + h, sw, sh)
      g.FillEllipse(myBrush, px + w, py + h, sw, sh)
      g.FillEllipse(myBrush, px - w, py + h, sw, sh)
Now you have seamless scrolling both vertically and horizontally.

As for the Opton Strict On error, I think I know what is going on.
I thought about this yesterday. Since the control is already a reference type, and we don't plan on modifying the reference itself, we probably shouldn't really pass the reference by reference which would allow us to do that.
With "Option Explict On" in VB 2010, it doesn't cause an issue, whcih might be because the paradigm used in the way parameters are passed might be different, or they just don't check for the narrowing scope unless you actually try to modify the value.
Regardless, I suspect if you pass a value by reference in 2012 it either always copies the value back or at least treats it like it would so raises the error.
Since we don't intend to modify the reference, only the control being reference, I changed the New call to pass the parameter by Value.

Public Sub New(ByVal c As Control)

This still works as expected in 2010.
I just spent 10 minutes downloading VS 2012 express and burning it to a CD while writing this post, so will post this and take a wack at installing VS 2012 to try it without the change and with it. I'm pretty sure it should "fix" the issue.
__________________
There Is An Island Of Opportunity In The Middle of Every Difficulty.
Miss That, Though, And You're Pretty Much Doomed.
Reply With Quote
  #18  
Old 02-22-2013, 10:16 PM
CodeCruncher CodeCruncher is offline
Junior Contributor
 
Join Date: Jul 2006
Posts: 355
Default

Yep your correct I just plugged in Public Sub New(pb As Control) 'ByRef pb As PictureBox)

As you probably already know with VS2012 you don't need the ByVal it defaults to it automatically, and it ran without issue.

Being that I only want to use a picture box do I need to pass the bigger brother Control?

Thanks for the tip guys on going both directions, I am actually going in the other direction and bringing the bmp back to h x w because I dont need to scroll off the picturebox.

Do I still need the bmp that being the case, or is the bmp still being used as a canvas?

Edit:
I updated SurfR2911's modification version with passels 3x3 modification and it is seemless in all directions. Can see me using that as the basis for a game one of these days.

I do recall several years back perhaps it might have even been some of passels code where it did a similar thing but worked on one section at a time... I am starting to see how those other graphics examples might have worked along a similar lines.

But I can't afford to side track myself as I still have several cans of worms open already.

Last edited by CodeCruncher; 02-22-2013 at 10:30 PM.
Reply With Quote
  #19  
Old 02-22-2013, 10:33 PM
surfR2911 surfR2911 is offline
Contributor
 
Join Date: Oct 2009
Posts: 719
Default pbs and bmps and Option Strict being on all the time..

Quote:
Originally Posted by CodeCruncher
Being that I only want to use a picture box do I need to pass the bigger brother Control?
No.
In the first attachment if you comment out this line in the Form Load:
Code:
'ControlList.Add(New GenBufferedGraphics(GroupBox1))
..then you change the New constructor to just use a Picturebox:
Code:
Public Sub New(ByVal c As PictureBox)
  gContext = BufferedGraphicsManager.Current
  _c = c
  gfx = gContext.Allocate(_c.CreateGraphics, _c.DisplayRectangle)
  AddHandler _c.Resize, AddressOf ResizeHandler
  AddHandler _c.Paint, AddressOf PaintHandler
End Sub

Quote:
Originally Posted by CodeCruncher
Do I still need the bmp that being the case, or is the bmp still being used as a canvas?
No.
The second attachment just uses the bmp as a backbuffer so the controls can act as a viewport.
If you don't need the "viewport effect" then the first attachment shows how to use the control itself as the canvas.

By the way, even though having Option Strict turned on is usually a good idea for most of the time
there are some cases where it might now be desirable.

When even I'm translating C#.Net to VB.Net it's usually done by way of passing code snippets
through the online translators a sub or a function at a time.

I always leave the Option Strict off until the translators errors are cleaned up then correct the variable typing
before turning it back on (rather than having the IDE spit out 107 errors at me).
Reply With Quote
  #20  
Old 02-22-2013, 10:50 PM
CodeCruncher CodeCruncher is offline
Junior Contributor
 
Join Date: Jul 2006
Posts: 355
Default

Yeah I am only using the picturebox in mine so I will swap it, but in passel original example I am going to leave it as a Control, because it is good to have there as a reference should I want to paint to multiple items.

See your point if your translating but I am yet to start on C. I have it on to stop me doing lazy code, even though it causes me frustration trying to get around the errors. Developing good habits are worth more than a bit of occasional grief.
Reply With Quote
Reply


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off

Forum Jump

Advertisement:





Free Publications
The ASP.NET 2.0 Anthology
101 Essential Tips, Tricks & Hacks - Free 156 Page Preview. Learn the most practical features and best approaches for ASP.NET.
subscribe
Programmers Heaven C# School Book -Free 338 Page eBook
The Programmers Heaven C# School book covers the .NET framework and the C# language.
subscribe
Build Your Own ASP.NET 3.5 Web Site Using C# & VB, 3rd Edition - Free 219 Page Preview!
This comprehensive step-by-step guide will help get your database-driven ASP.NET web site up and running in no time..
subscribe
Buffered Graphics Class
Buffered Graphics Class
Buffered Graphics Class Buffered Graphics Class
Buffered Graphics Class
Buffered Graphics Class
Buffered Graphics Class Buffered Graphics Class Buffered Graphics Class Buffered Graphics Class Buffered Graphics Class Buffered Graphics Class Buffered Graphics Class
Buffered Graphics Class
Buffered Graphics Class
 
Buffered Graphics Class
Buffered Graphics Class
 
-->