Results 1 to 8 of 8

Thread: A Control Paint (For .CreateGraphics) question

  1. #1

    Thread Starter
    Hyperactive Member pourkascheff's Avatar
    Join Date
    Apr 2020
    Location
    LocalHost
    Posts
    384

    A Control Paint (For .CreateGraphics) question

    Consider you are updating a bargraph value (Vertical ProgressBar or in this case a colorful PictureBox inside of a Panel) through a timer.

    By pressing a button (Or a CheckBox1.Checked = True condition) Timer code checks/decides whether to show the Percentage inside of the bargraph (.CreateGraphics.DrawString) or All 10% division ticks lines on bargraph (Many .CreateGraphics.DrawLine s)

    2 main problems:
    - In beginning the timer ui updating section, I worte .CreateGraphics.Dispose() to perform a sort of refresh/update in case of resizing the form. But due to timer interval ticks keep flickering. How can we avoid that?
    - I learned from John McIlhinney to maintain all graphic creating codes inside the control's Paint Event. How can we do so at first place (Not familiar with 'e' operator)? I'm suspected that flickering still happens because decision is still be making.

    Thanks in advance.

  2. #2

    Thread Starter
    Hyperactive Member pourkascheff's Avatar
    Join Date
    Apr 2020
    Location
    LocalHost
    Posts
    384

    Re: A Control Paint (For .CreateGraphics) question

    Update:
    I came up with this idea, randomly tested, which is totally separated with cycle-timer thing and also responsive to resizing form:
    Code:
        Private Sub Panel1_Paint(sender As Object, e As PaintEventArgs) Handles Panel1.Paint
            Panel1.Refresh() 'A
            If CheckBox2.Checked Then
                e.Graphics.DrawLine(Pens.Black, 0, Panel1.Height - 1, Panel1.Width \ 2, Panel1.Height - 1) '0%
                e.Graphics.DrawLine(Pens.Black, 0, 0, Panel1.Width \ 2, 0) '100%
                e.Graphics.DrawLine(Pens.Black, 0, Panel1.Height - ((10 * Panel1.Height) \ 100), Panel1.Width \ 4, Panel1.Height - ((10 * Panel1.Height) \ 100)) '10%
                e.Graphics.DrawLine(Pens.Black, 0, Panel1.Height - ((20 * Panel1.Height) \ 100), Panel1.Width \ 2, Panel1.Height - ((20 * Panel1.Height) \ 100)) '20%
                e.Graphics.DrawLine(Pens.Black, 0, Panel1.Height - ((30 * Panel1.Height) \ 100), Panel1.Width \ 4, Panel1.Height - ((30 * Panel1.Height) \ 100)) '30%
                e.Graphics.DrawLine(Pens.Black, 0, Panel1.Height - ((40 * Panel1.Height) \ 100), Panel1.Width \ 2, Panel1.Height - ((40 * Panel1.Height) \ 100)) '40%
                e.Graphics.DrawLine(Pens.Black, 0, Panel1.Height - ((50 * Panel1.Height) \ 100), Panel1.Width \ 1, Panel1.Height - ((50 * Panel1.Height) \ 100)) '50%
                e.Graphics.DrawLine(Pens.Black, 0, Panel1.Height - ((60 * Panel1.Height) \ 100), Panel1.Width \ 2, Panel1.Height - ((60 * Panel1.Height) \ 100)) '60%
                e.Graphics.DrawLine(Pens.Black, 0, Panel1.Height - ((70 * Panel1.Height) \ 100), Panel1.Width \ 4, Panel1.Height - ((70 * Panel1.Height) \ 100)) '70%
                e.Graphics.DrawLine(Pens.Black, 0, Panel1.Height - ((80 * Panel1.Height) \ 100), Panel1.Width \ 2, Panel1.Height - ((80 * Panel1.Height) \ 100)) '80%
                e.Graphics.DrawLine(Pens.Black, 0, Panel1.Height - ((90 * Panel1.Height) \ 100), Panel1.Width \ 4, Panel1.Height - ((90 * Panel1.Height) \ 100)) '90%
            Else
                e.Graphics.DrawString("0%", SystemFonts.DefaultFont, Brushes.Black, Panel1.Width \ 2, Panel1.Height \ 2) 'C
            End If
        End Sub
    BUT still flickering recognizably related to comment "A". When I preform e.Graphics.Dispose() in prior, following graphics are no longer able to being created. I only remember that refresh do the same but resets all control visual parameters

    B) By this code at once form showed, all labels and texboxes are empty like the app is going to crash soon or an exception is going to appear. Working with the app for few seconds will rectify the matter. How to avoid this thing too? Am I going a wrong way?

    C) About showing percentage (Of course there's a numeric variable which we call in CStr(MyPercent) & "%" as string but how can we justify it in the middle all the time? (Hint: I only heard somewhere there are StringAlignment and StringMeasure (Space occupying instead of length) to implement such thing but don't know how)
    Last edited by pourkascheff; Apr 21st, 2023 at 10:21 AM.

  3. #3
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    40,104

    Re: A Control Paint (For .CreateGraphics) question

    What is your timer interval?

    Flickering just means that something is changing too fast. Normally, this is due to drawing taking too long such that the control has a chance to draw the 'cleared' state before the new drawing is available. That seems unlikely, in this case, as your drawing code shouldn't take too long. However, flickering can also result from any other kind of drawing too fast. If your screen is refreshing at 60 Hz, and you are managing to put somewhat different images on the screen with each refresh, the screen will have a jittery appearance. Your eyes wouldn't fully recognize the intermediate images before they are replaced by the next one. You'd see it, just wouldn't see it completely, which would look like a flicker.

    Whether or not that is the case isn't quite clear, but that timer might have something to do with it. Something calling Refresh too often could do it, also.
    My usual boring signature: Nothing

  4. #4

    Thread Starter
    Hyperactive Member pourkascheff's Avatar
    Join Date
    Apr 2020
    Location
    LocalHost
    Posts
    384

    Re: A Control Paint (For .CreateGraphics) question

    D) How can we make new graphic being created be BringToFront as most as possible? I tried make all previous controls inside of Panel1 to be .SendToBack() but new graphics still being covered by previous controls.

    E) Is there a sort of default ready-to-use built in graphics in VS? I mean there are Default SystemFonts, SystemColors, SystemIcons and even SystemAudio bank. I want solid top/bottom targeted arrows (which are at least 7 points polygons) but nevertheless using defaults feels good and shorten coding.

  5. #5

    Thread Starter
    Hyperactive Member pourkascheff's Avatar
    Join Date
    Apr 2020
    Location
    LocalHost
    Posts
    384

    Re: A Control Paint (For .CreateGraphics) question

    Quote Originally Posted by Shaggy Hiker View Post
    What is your timer interval?
    Respectfully, there is no timers anymore in new code I provided in answer #2. I assumed that Paint event is an infinite loop itself therefore clearing (as you mentioned) then creating graphics leads such thing. I need to achieve as fluent as possible. Not removing previous elements is a huge mess.

  6. #6
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    40,104

    Re: A Control Paint (For .CreateGraphics) question

    It isn't an infinite loop, exactly. Anything that causes the control to become dirty will cause a paint event to be posted to the message queue. When the UI has time, it processes all the messages in the queue. If a paint message is in the queue, then the control gets painted, or part of it gets painted, depending on the dirty area. Therefore, anything that causes the control to become dirty will add a paint message. Invalidating the control will do that, shifting controls around in the z-order will probably do that, showing and hiding a form will do that, partially obscuring then revealing the form will do that, and probably other things, as well.

    Calling Refresh forces an immediate redraw without the paint message, which is a costly thing to do, because the UI can't wait until it isn't busy with anything else. I doubt you are calling Refresh anywhere, but perhaps you are calling Invalidate somewhere.
    My usual boring signature: Nothing

  7. #7

    Thread Starter
    Hyperactive Member pourkascheff's Avatar
    Join Date
    Apr 2020
    Location
    LocalHost
    Posts
    384

    Re: A Control Paint (For .CreateGraphics) question

    Sorry but I barely understand what are you saying mister hiker.
    Code:
    SomeControl.Invalidate()
    Literally in-validates it like you are never going to use it again. In another hand there is a
    Code:
    Panel1.CreateGraphics.Dispose()
    Which reacts better than before but I found a class source code, dedicating for a ProgressBar not only it contains value percentage with decimals in center-middle justified, but also vertical in progressing and so much more.

    But since it inherits Forms.ProgressBar, It doesn't have "Paint" event. "ChangeUICues" also not helping so I'm considering painting ticks right after percentage re-writing code in the class itself.

  8. #8
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    Re: A Control Paint (For .CreateGraphics) question

    Quote Originally Posted by pourkascheff View Post
    Sorry but I barely understand what are you saying mister hiker.
    Code:
    SomeControl.Invalidate()
    Literally in-validates it like you are never going to use it again.
    That could barely be more wrong. As Shaggy has already told you, the Invalidate method tells the system that what the control is displaying on the screen is no longer valid and it needs to be repainted. If you call Invalidate with no argument then you're saying that the whole control needs to be repainted. When you provide an argument, you are saying that only the area specified by that argument needs to be repainted. Painting pixels to the screen is an expensive operation so it is generally better to spend the time calculating the smallest possible area to invalidate - the area in which pixels have or may have changed - rather than just repainting everything.

    As has already been explained, The Invalidate method tells the system that the control needs to be repainted, so the system will do so when it is finished other queued tasks. The Update method tells the system to repaint all invalidated areas immediately. The Refresh method literally calls Invalidate with no argument and Refresh, so it immediately repaints the entire control. Look at this:
    Code:
        Private Sub Panel1_Paint(sender As Object, e As PaintEventArgs) Handles Panel1.Paint
            Panel1.Refresh()
    That's your code. You have a method that is executed every time the control is repainted and the first thing you do is tell it to immediately repaint in its entirety. Do you wonder why you are seeing flickering?

    This is ludicrous:
    Code:
    Panel1.CreateGraphics.Dispose()
    You are creating a Graphics object and immediately disposing it without ever using it. What possible use could that have?

    You need to start with the logic and then write code to implement that logic. Ifr you don;t understand what code has to do, what chance do you have of writing code to do it?

    In the Paint event handler, you use the Graphics object provided to draw on the control. That's it, that's all. You ALWAYS draw everything that needs to be drawn on the control. Drawing and painting are two different things. You create the entire drawing every time and then the system decides which part(s) of that drawing to paint to the screen. Drawing is fast but painting is slow.

    Whenever you do something in your code that will or could change what needs to be drawn on the control, you need to prompt a Paint event to execute your drawing code. You do that by calling Invalidate. Once you have changed the relevant data, you should perform an appropriate calculation to determine the smallest practical area that has or may have changed and pass that to Invalidate. Once the current code and any other queued event handlers have finished executing, your Paint event handler will be executed and your drawing code run, then the invalidated area will be repainted with that drawing. To see an example of that in action, check out this CodeBank thread of mine:

    https://www.vbforums.com/showthread....rawing-Program

    Note that I do use Update and Refresh in that code but I wrote that almost 17 years ago and didn't know as much. I would generally recommend to just call Invalidate, with or without arguments as appropriate, and let the system decide when to repaint. Only call Update or Refresh if it's really important that the repainting takes precedence over other UI changes, including the repainting of other controls.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width