Control OnPaint

Doggo120

Newcomer
Joined
Oct 7, 2011
Messages
23
Location
Colombia
Hi hi, yo yo!

Hi everybody, I'm using some code to make a transparent label, but as soon as I change the text or the text align properties the previous text remains intact! (like the image)

When I move it around though everything goes back to normal, but I'm wondering if there's something I can do to fix this issue.

The OnPaint method is overriden like this:

Code:
    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        Try
            MyBase.OnPaint(e)
            e.Graphics.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAlias
            e.Graphics.DrawString(Texto, MyBase.Font, vPincelDeColorDeTextoLabel, vPropiedadesDeRectánguloF, vFormatoDeTexto)
        Catch vError As Exception
            Exit Sub
        End Try
    End Sub

I tried using e.Graphics.Clear method but it doesn't work the way a want to.

Any ideas or solutions?

Thanks again!
 

Attachments

How are you achieving transparency? Are you setting the BackColor to Transparent? Are you setting any style? What is the base class? Control?

If you make a control transparent by setting the "Opaque" style and not clearing the background, you might see the behavior you're getting. If you set the "SupportsTransparentBackColor" style and use a transparent backcolor, it should work correctly.

I'm a little curious as to why you are creating a transparent label when .NET labels already support transparency.
 
Hi snarfblam! Yes, I'm trying to achivieve transparency.

The thing is, labels and controls support transparency but only at a certain level. If I set two or more labels on top of each other the only transparent one is the one at the back (like picture 1). If the area surrounding the text is smaller (that is, if I make every label a little smaller) it would work.

I searched on the web and found a project named TransparentLabel ->
http://www.sourcehints.com/articles/creating-a-real-transparent-label-in-vb-net.html

With this project I can create truly transparent labels and even place them in front of other labels and controls. I did the exact same thing of picture 1 with the new label and got picture 2.

The only thing is the fact that it does not automatically clear the text that was before, and I don't which method or event or whatever to call.

That's why I chose this new label! :)
 

Attachments

What you're looking to do is, by design, not possible.

You could probably fudge it by painting in a certain manner that isn't necessarily correct. For example, when your transparent control is invalidated, it can force a refresh of the underlying region of its parent, then do its own painting. Depending on exactly how you orchestrate it, it may or may not work. But even if you get it to work, you're not following the rules, and different versions of Windows and .NET aren't required to behave the same way in circumstances where somebody isn't following the rules. So even if you get it working on your machine, you could ship out the program and find that it doesn't work for 50% of the users. Or it might not work with the next version of Windows. Who knows?

In WinFroms, each control has its own window, and what you want to do violates the principles of how windows interact. Every window is responsible for drawing itself and only itself. .NET's "transparent" controls are a special case where a window kindly asks its parent window to draw itself before the child draws its own content on top. That is a very simple level of cooperation between the two controls. What you need is much more complex. If Label2 needs to be drawn, first it needs the container to draw itself, then Label 2 and all other overlapping controls would have to draw themselves in order, based on Z-order, each window rendering text to parts of the screen it doesn't own. This level of cooperation between controls simply isn't supported in .NET.

So how do you fully support transparent controls? That are different ways you can approach that. Maybe you don't even need controls at all. If all you need to do is draw overlapping text in a form, you can do that in the Form's paint event. If you're looking for a re-usable solution, I've personally had plenty of success with "pseudo-controls" similar to VB6's lightweight (i.e. windowless) controls. They can duplicate the most important aspects of a control (location and size, invalidation/painting, keyboard and mouse handling) and be implemented by either a control designed to host windowless control and route events as appropriate, or as components that handle events of the container and interpret the events as they apply to the windowless child.

Without knowing why you need transparent labels, it's hard to recommend the most appropriate solution.
 
Hi again snarf!
You're always so kind to hear my (and everyone elses) questions. The ultimate goal is to make a music score usercontrol (I managed to do it before, but I'm rewriting all the code to make it more efficient and organized) like picture 1 (lately I've been using a lot of pictures right?).

BTW -> The labels in the pictures are normal labels, with transparent background and "sent to back" on the form. :)

The thing is, if I want to make a chord, the labels would overlap and I would only be able to see the first one or the last one (this is because the font I use (called Sonora) combined with the size I use it (52 pts) takes a lot of "space" (like picture number 2, where u can see that behavior in the label's size) and that extra space is the one that does not allow the other labels to be seen :(

The solution would be something like making the label be as big as the character, but I don't know if it's possible to achieve that.

But..... thanks to dear snarfblam, I'm thinking I could manage all of that with the Form.OnPaint method. I will give it a try for sure! :p
 

Attachments

I was looking at the possible issues and it was the parent form refresh method (as u said snarf... u are awesomely awesome).

Is there any way to trigger a sort of Parent.Invalidate method within a user control? I've tried it and it gives an instance object error.
 
Uh... to be perfectly clear, I was recommending a solution that doesn't involve any controls at all except for a single control to host all your content. It sounds like you're still working on a solution that involves layering controls.

One of the solutions I suggested was to use "pseudo-controls." Such a class would inherit the System.Component class instead of System.Windows.Forms.Control or System.Windows.Forms.UserControl. All pseudo-controls would be hosted within a single control that maintains a list of pseudo-controls and routes events as appropriate. This involves a little bit of re-inventing the wheel, duplicating many functions of the existing Control class, but you only have to implement those features you need.

You would have a Paint event and an Invalidate function, and quite probably mouse events. You would have to convert coordinates from the host control's coordinates to the child control coordinates. Here's an example of what is probably to most complex case of converting between coordinates (code is untested):
Code:
    Private Sub Form1_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint

        Dim transform As Drawing2D.Matrix = e.Graphics.Transform

        For Each child As PseudoControl In pcontrols
            If e.ClipRectangle.IntersectsWith(child.Bounds) Then
                [COLOR="Green"]' Adjust graphic output to be relative to the child "control" (*)[/COLOR]
                e.Graphics.TranslateTransform(child.Bounds.X, child.Bounds.Y)

                [COLOR="Green"]' Restrict invalid rectangle to child's "client area"[/COLOR]
                Dim localRect As Rectangle = e.ClipRectangle
                localRect.Intersect(child.Bounds)
               [COLOR="Green"] ' Translate to child coordinates[/COLOR]
                localRect.Offset(-child.Bounds.X, -child.Bounds.Y)

              [COLOR="Green"]  ' Let the child draw[/COLOR]
                child.Paint(localRect, e.Graphics)

               [COLOR="Green"] ' Restore the transformation matrix (*undo what we did above)[/COLOR]
                e.Graphics.Transform = transform
            End If
        Next
    End Sub

Other features (nested controls, focus, keyboard input) would be implemented only if needed.

This isn't the only possible solution, and it is a lot of work and a bit advanced, but it is a solution that will be robust, efficient, re-usable, and will separate layout and rendering logic from all your other code. Whether you use this method or another is your call.

Now, even if you get something set up involving layered controls, and get it to work consistently across multiple machines with different versions of Windows with different settings, I'm concerned that the graphic updates will be slow and flickery. Maybe this is okay. Maybe that doesn't matter for your program. I don't know.

As far as your object instance error, you haven't posted the code that's throwing the error or explained when the error occurs. (When the form loads? When it resizes? When it paints?) But I'm guessing that you can probably make some sense of it if you use the debugging features available. Set a breakpoint on the problem function, or let the debugger break when the exception occurs. The hover over variables or use the Watch window to examine variables and properties and try to figure out what is going wrong. Is something set to null that you're not expecting? Why? Maybe a function is being called when you don't expect. Who knows? Exercise your debug muscle.
 
Back
Top