Pselus Posted December 21, 2005 Posted December 21, 2005 Right, so...long story short, I have a panel with curved corners. I need the portion of the panel outside the curve to be transparent. La-dee-da, that's easy as pie...done did it. Problem: Due to the amount of graphical processing in the app (and the use of this panel ALL over the place), when the application raises an event or resizes there is alot of "flickering". I traced the issue down to the fact that the application is redrawing the background and for a split second it is showing through all the controls (on each of the panels in the application...so it causes alot of visual nastiness). Now, the true problem arises in that, when I override OnPaintBackground...this flickering stops flat out...but I can't get the outside corners to draw transparent. I've tried alot of different things and come to the conclusion that you CANNOT use transparency if you Override OnPaintBackground (and do nothing in it). I pray that I am wrong about that conclusion and was hoping someone here would konw a way that I can. for reference sake my panel is based off this example. Here is a shot of the panel before overriding OnPaintBackground: http://www.livethislife.net/images/ForWork/panelbefore.gif and here is a shot after overriding OnPaintBackground: http://www.livethislife.net/images/ForWork/panelafter.gif So...anyone know of a way for me to draw a transparent rectangle as the background without actually drawing the background? To further complicate matters...I need to do this over gradients. Quote
Pselus Posted December 22, 2005 Author Posted December 22, 2005 guess no one wants to touch this one with a 10 foot pole eh? let me try to make it easier. How could I write a procedure to do Transparency just like Color.Transparent would do in VB.Net. That's the gist of it...I need to write what M$ already wrote because theirs only works if OnPaintBackground isn't overridden. Quote
Cags Posted December 22, 2005 Posted December 22, 2005 Maybe this will help... http://www.bobpowell.net/transcontrols.htm Quote Anybody looking for a graduate programmer (Midlands, England)?
Pselus Posted December 22, 2005 Author Posted December 22, 2005 sadly no...that does allow me to make the corners transparent without calling MyBase.OnPaintBackground...but it raises a whole host of other issues like not fully drawing the controls until events are raised...though I'm sure that this is my code interfering with what works from that example...but I can't for the life of me figure out why. :( Quote
Cags Posted December 22, 2005 Posted December 22, 2005 It's going to be incredibly hard to be any more helpfull without seeing more code as there are many variables that may be causing you trouble, are you calling the main draw code from the paint event or from other events (which cause changes). Are you drawing directly to screen or to some kind of memory buffer? On a side note I assume you haven't accidently caused a loop by calling InvalidateEx from the OnPaint Event? Quote Anybody looking for a graduate programmer (Midlands, England)?
Pselus Posted December 23, 2005 Author Posted December 23, 2005 well...it's gonna be pretty tough to show all the relevant code because 1) it's huge and 2) it's code for my company and I can't show much of it (I always hate that rule). Mainly, I think one of my problems with that example is that our program uses this panel for other controls. See, this is a panel that was built for this company by a different programmer who no longer works here...he did it solely for the purpose of creating rounded edges in a panel. Then, when they asked for buttons with rounded edges and labels with rounded edges and tab-controls with rounded edges he (stupidly in my opinion) used this panel to create them. For the label, he created a control with this panel and a windows label inside of it. This means that on any given form I have lots of instances of this panel, all of which are trying to be transparent and I end up with controls not completely drawing for some reason... From what I understand this whole WS_EX_Transparent thing just tells the control to wait to draw after it's children and parents...but what happens when a control with this setting is a child of another control with this setting? Quote
Diesel Posted December 23, 2005 Posted December 23, 2005 Screw the transparency issue. Make this easier on yourself by using a graphicsPath... http://www.codeproject.com/cs/media/ExtendedGraphics.asp http://www.dotnet247.com/247reference/msgs/10/51694.aspx Quote
Cags Posted December 23, 2005 Posted December 23, 2005 Maybe I'm missing the point here, but that surely doesn't help at all, and is infact quite likely to be the method Pselus is using. The links you provide basically talk about drawing a rounded rectangle. Pselus control already does that. The problem is it's being drawn onto a square control and what Pselus wants is the areas its not being drawn on to show whatever is behind the control. Quote Anybody looking for a graduate programmer (Midlands, England)?
Pselus Posted December 23, 2005 Author Posted December 23, 2005 but will this get over the issue of the form redrawing when events are raised? I've come up with PLENTY of ways to get around the Transparency thing, but so far none of them have solved that particular problem... now that I think about it...I'm not sure I even mentioned that problem here...hmmm.... ah, yes, I did: I traced the issue down to the fact that the application is redrawing the background and for a split second it is showing through all the controls so yeah, overriding OnPaintBackground stops this flickering...but then the transparency problem comes up. I will look into the links you posted...I hope they help me. Quote
Pselus Posted December 23, 2005 Author Posted December 23, 2005 Maybe I'm missing the point here' date=' but that surely doesn't help at all, and is infact quite likely to be the method Pselus is using. The links you provide basically talk about drawing a rounded rectangle. Pselus control already does that. The problem is it's being drawn onto a square control and what Pselus wants is the areas its not being drawn on to show whatever is behind the control.[/quote'] YES! exactly. :P my current thing I'm looking into is trying to basically "capture" a rectangle on the parent at certain coordinates (specifically, where the corner is outside the curve) and draw that onto the control to mimic transparency....but I have no clue how to actually GET that rectangle from the parent that I need. Quote
Cags Posted December 23, 2005 Posted December 23, 2005 I'm sure the method I outlined is probably your best solution, but I can't really say for sure untill i've tested it as a designtime control (which i don't know how todo) so i'll have a look into that and get back to you. Quote Anybody looking for a graduate programmer (Midlands, England)?
Pselus Posted December 23, 2005 Author Posted December 23, 2005 umm....could I somehow use Regions for that purpose? I have never worked with them before so I'm not sure. Quote
Pselus Posted December 23, 2005 Author Posted December 23, 2005 I'm pretty sure that your method is the overall best solution as well...however, there are so many other things dependant upon it now that I don't think it's the FASTEST way. My boss has already said that we will take the time to go back and rewrite the controls later...so all I need to find now is a quick fix...when I redesign them I will most definatly use your method. Quote
Diesel Posted December 23, 2005 Posted December 23, 2005 Try using Control.SuspendLayout() and ResumeLayout() when redrawing...without the need to override OnPaintBackground. Also...is the username/password rectangle a user control? What is the need for the panel? Quote
Leaders Iceplug Posted December 23, 2005 Leaders Posted December 23, 2005 So, are you considering setting a region for the panel? Basically, taking a GraphicsPath, adding a RoundedRectangle identical to yours (but starting at 0,0), turning the GraphicsPath into a Region object and then setting the Panel region to the Region that you just created? Quote Iceplug, USN One of my coworkers thinks that I believe that drawing bullets is the most efficient way of drawing bullets. Whatever!!! :-(
Pselus Posted December 23, 2005 Author Posted December 23, 2005 we've already tried the SuspendLayout, ResumeLayout route. the Login is just a form, it happens to have one of these panels on it. I can show you what it looks like with the control working properly...I have highlighted in green all the places just in one screen of the app that use this panel (ignore the red background...it's me trying to test things): http://www.livethislife.net/images/ForWork/system.png then this is what it looks like with OnPaintBackground overridden (the places that don't work are highlighted green, the places that do are blue): http://www.livethislife.net/images/ForWork/system2.png honestly, for the life of me I can't figure out why some of those things work fine when others don't... I must say...I used to hate doing bugs...now I just hate doing Graphics bugs when the graphics were designed by an incompetant programmer...I can't tell you how many places just have arbitrary Color.Transparent assignments... Quote
Pselus Posted December 23, 2005 Author Posted December 23, 2005 literally the difference in those 2 pictures is when I go into this control and comment out (or uncomment) the single line: Mybase.OnPaintBackground. doing that one thing in 1 control causes that much change in the application. Quote
Pselus Posted December 23, 2005 Author Posted December 23, 2005 and now I have to go home for the weekend...everyone have a great Christmas and I hope I can solve this on Monday. :) Quote
Cags Posted December 23, 2005 Posted December 23, 2005 Setting the Region setting seems to be the best solution, I just gave it a quick test. Create a region using a graphics path (if your not already doing that), if you are just set this.Region = new Region(myGraphicsPath), you will need to make this calculation everytime you resize the control etc but it should work fine. Quote Anybody looking for a graduate programmer (Midlands, England)?
Pselus Posted December 27, 2005 Author Posted December 27, 2005 ok, I only partially get what you're saying there Cags. you are saying that I should set the controls region using a graphics path..I understand that part...but what exactly should this graphics path consist of? If I make the graphicspath based on the rounded edges, is it going to still need MyBase.OnPaintBackground to paint the transparent corners? If so, well...that's the whole issue...painting the background normally is the origional problem because it creates alot of flickering. Quote
Cags Posted December 27, 2005 Posted December 27, 2005 No it will not need the onPaintBackground as the control only draws what is within the region and yes the region is made up of the 4 lines and 4 corners that make the shape of your control. Quote Anybody looking for a graduate programmer (Midlands, England)?
Pselus Posted December 27, 2005 Author Posted December 27, 2005 Cags, I just got around to trying the Region thing and it is so very very close to what I need. If I weren't in a place of business I'd be giggling like a schoolgirl. A few problems though: 1) My app just slowed waaaaaaaaaaaaaaaaay down after doing that. For reference sake, here is my Paint code for the uctrlPanel control: Dim filler As System.Drawing.Drawing2D.LinearGradientBrush Dim rect As System.Drawing.Rectangle = Me.ClientRectangle Dim topCorn As Rectangle Dim graphPath As System.Drawing.Drawing2D.GraphicsPath If memGraphics.CanDoubleBuffer() Then If Not Me._HasDrawn Then '''Me._HasDrawn = True ' Draw the curved border graphPath = Me.GetPath() If Me.ClientRectangle.Width = 0 Then rect.Width += 1 End If If Me.ClientRectangle.Height = 0 Then rect.Height += 1 End If If Me._GradientMode = LinearGradientMode.None Then filler = New System.Drawing.Drawing2D.LinearGradientBrush(rect, Me._BackColour1, Me._BackColour1, System.Drawing.Drawing2D.LinearGradientMode.Vertical) Else '''filler = New System.Drawing.Drawing2D.LinearGradientBrush(rect, Me._BackColour1, Me._BackColour1, CType(Me._GradientMode, System.Drawing.Drawing2D.LinearGradientMode)) filler = New System.Drawing.Drawing2D.LinearGradientBrush(rect, Me._BackColour1, Me._BackColour2, CType(Me._GradientMode, System.Drawing.Drawing2D.LinearGradientMode)) End If memGraphics.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias memGraphics.Graphics.FillPath(filler, graphPath) Me.Region = New Region(graphPath) Select Case Me._BorderStyle Case System.Windows.Forms.BorderStyle.FixedSingle Dim borderPen As New System.Drawing.Pen(Me._BorderColour, Me._BorderWidth) memGraphics.Graphics.DrawPath(borderPen, graphPath) borderPen.Dispose() Case System.Windows.Forms.BorderStyle.Fixed3D Me.DrawBorder3D(memGraphics.Graphics, Me.ClientRectangle) Case System.Windows.Forms.BorderStyle.None End Select ' Render to the form memGraphics.Render(e.Graphics) filler.Dispose() graphPath.Dispose() End If End If Is there something in there I shouldn't be doing? Something I should do different? I am so new to this whole GDI+ thing that I'm learning on the fly (though you have been very helpful so far). There are a couple other problems right now (not drawing all of the border, not drawing all of the controls, etc.) but I'll go one at a time...for now the speed of the app is a big thing. Quote
Pselus Posted December 27, 2005 Author Posted December 27, 2005 couple other relevant code pieces: Protected Function GetPath() As System.Drawing.Drawing2D.GraphicsPath Dim graphPath As New System.Drawing.Drawing2D.GraphicsPath If Me._BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D Then graphPath.AddRectangle(Me.ClientRectangle) Else Try Dim curve As Integer = 0 Dim rect As System.Drawing.Rectangle = Me.ClientRectangle Dim offset As Integer = 0 Select Case Me._BorderStyle Case System.Windows.Forms.BorderStyle.FixedSingle If Me._BorderWidth > 1 Then offset = DoubleToInt(Me.BorderWidth / 2) End If curve = Me.adjustedCurve Case System.Windows.Forms.BorderStyle.Fixed3D Case System.Windows.Forms.BorderStyle.None curve = Me.adjustedCurve End Select If curve = 0 Then graphPath.AddRectangle(rect.Inflate(rect, -offset, -offset)) Else Dim rectWidth As Integer = rect.Width - 1 - offset Dim rectHeight As Integer = rect.Height - 1 - offset Dim curveWidth As Integer = 1 If (Me._CurveMode And CornerCurveMode.TopRight) <> 0 Then curveWidth = (curve * 2) Else curveWidth = 1 End If graphPath.AddArc(rectWidth - curveWidth, offset, curveWidth, curveWidth, 270, 90) If (Me._CurveMode And CornerCurveMode.BottomRight) <> 0 Then curveWidth = (curve * 2) Else curveWidth = 1 End If graphPath.AddArc(rectWidth - curveWidth, rectHeight - curveWidth, curveWidth, curveWidth, 0, 90) If (Me._CurveMode And CornerCurveMode.BottomLeft) <> 0 Then curveWidth = (curve * 2) Else curveWidth = 1 End If graphPath.AddArc(offset, rectHeight - curveWidth, curveWidth, curveWidth, 90, 90) If (Me._CurveMode And CornerCurveMode.TopLeft) <> 0 Then curveWidth = (curve * 2) Else curveWidth = 1 End If graphPath.AddArc(offset, offset, curveWidth, curveWidth, 180, 90) graphPath.CloseFigure() End If Catch ex As System.Exception graphPath.AddRectangle(Me.ClientRectangle) End Try End If Return graphPath End Function And Public Sub Render(ByVal g As Graphics) If Not Me._memoryBitmap Is Nothing Then g.DrawImage(Me._memoryBitmap, New Rectangle(0, 0, Me._width, Me._height), 0, 0, Me._width, Me._height, GraphicsUnit.Pixel) End If End Sub Quote
Cags Posted December 28, 2005 Posted December 28, 2005 (edited) Well I thought I might reply now, just incase you have chance to look at this before I'm available to check more closely tomorrow. If my post doesn't make sense I appologise, I have been drinking. As I'm only looking at sections of your code it's hard to tell, but I notice that in your Render method you are attempting to draw a local bitmap to your screen. Does this mean that all drawing is done to a local bitmap and then the paint method simply draws the local bitmap? If this is the case then an application witch uses alot of instances of this control could use a lot of memory. I suppose this might cause some slowdown. I can see no particular reason why the basic code behind drawing a curved rectangle should cause a slowdown in your application. I will look at your code further tomorrow, but if you could give me an approximation of how many of this control may be on screen at once, then that would be very helpfull. EDIT: I nearly forgot to mention, you say your entire controls doesn't draw correctly, I could be wrong but i'm guessing by this you mean part of the border is missing. If this is the case it is perhaps because you are setting the region of the control to the size of the border, wheras infact this region should be large enough to entirely encase the border (i.e at least 2 pixel larger in either direction). Edited December 28, 2005 by Cags Quote Anybody looking for a graduate programmer (Midlands, England)?
Cags Posted December 28, 2005 Posted December 28, 2005 After another look at your code I would also suggest trying to eliminate the Try Statements as they shouldn't be neccessary in this case. If you store the path of the border locally it will prevent you having to call GetPath everytime you paint, instead calling it only when the control is resized. Quote Anybody looking for a graduate programmer (Midlands, England)?
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.