I have a User Control that has some WinForm Controls inside. I want to be able to paint those inner controls myself (for scrolling purposes, some of them will remain, others will move).
Even when I override the OnPaint and the OnPaintBackground method, those controls keeps showing on my usercontrol which is completely black now because there is no painting methods on it.
Is there a way I can suppress those controls from been painted and then paint them by myself with the DrawToBitmap method of each control?
Yes. Remove them from (or better yet, never add them to) the UserControl's set of child controls. Simulating interaction with the controls is going to be a PITA, though.
Do you just need the controls to "look" like they're there. Or do they need to actually be there? If its the latter, you would be better off faking the scrolling somehow by just repositioning the controls manually.
Trying to re-invent the windowing system is an exercise in pain. You will be better off if you learn and work within its paradigms.
If you don't need interaction, just set every child control .Visible = false.
Otherwise, have you tried WM_SETREDRAW?
Related
I am making a simple chat box control. Its just a hobby project to learn. I want to make my own control like below :
I have learnt how to paint graphics and text onto a custom control surface inheriting from control using OnPaint. But problem is I want this control to have the elements in the screenshot, most importantly to be able to infinitly scroll. Had a try googling but didnt find any answer.
Also because its painted, I probably wont be able to differentiate between the different users, or the speech bubbles as they are all the same to the control.
But is there a way to know the user is clicking on a certain bubble, or certain user? And the other question was how to make it scrollable?
Many thanks in advance.
If you want to make it scrollable, you should try to make each comment a separate panel & add that panel to your control.
Then set the autosize property of your control to true (you will need to make your control a panel, inheriting control doesn't have autosize property).
For the clicking on the bubbles, I suggest again to make each bubble a custom control & then add it to the panel (the panel inside your main panel), then just use the MouseEnter event.
I developed some components with their own OnPaint-override where I draw them.
I now added a status panel which is designed to overlap topmost with part of my form. I use a alpha-blended color to gray out the underlying controls and display a text on it.
Unfortunately some of my underlying custom controls paint themselves after the status bar and so draw themselves above the status bar. Debugging I found out that first the status bar on paint event was called, later the paint event of the underlying controls.
How can I make sure the proper order is used to paint?
When underlying components draw themselves the components infront of them should always be automatically invalidate so they redraw themselves but that is not happing. I also called SendToFront on the statusbar but that did not helper neither. So I am also asking what is the proper mechanism to maintain the order and how do the core components handle that.
Edit: I also run in this problem: With every run of my OnPaint-event my overlay gets more and more opaque because it draws with alpha=128--red again and again on itself.
To eliminate external factors (like variations between XP, Vista, Win7), you could:
blend the "status panel" graphics onto each control in their OnPaint handlers, so you don't need to mess about with a semi-transparent status window to achieve the desired display.
use one opaque control (essentially the status panel) and render the final image you want into it by compositing the graphics from the individual controls, which would elimimnate any uncertainty over how the rendering process will operate. This would mean that you'd have to add custom click handling, but that's usually a fairly trivial addition - one easy way around this is to put the main "display control" behind a set of invisible controls (the buttons) so that all the click handling can work as normal, but the painting is delegated to a single control.
I'm currently writing an custom control which has to hold child control and have to support scrolling and autorisze.
Autoscrolling is not possible because my control is fully selfpainted an only a part of the control should be scrolled.
Now the the repainting of the Controls is extrem slow, especialy with textbox and buttoncontrols with systempainting. Deactivating systempainting (TextBox.BorderStyle = Borderstyle.Fixed) helps, but the control should also support this. Using SetRedraw and updating the controls afterward doesn't help, because the textbox systemdraw is ignored and the textbox looks ugly. Refreshing my whole control or using `RedrawWindow slows the painting down again.
i also already tried to suspend my control and child controls layout without success.
Does anybody know how to speedup the childcontrol painting like AutoScrolling.
My CustomControl has the Style ControlStyles.AllPaintingInWmPaint | ControlStyles.SupportsTransparentBackColor | ControlStyles.OptimizedDoubleBuffer.
The ChildControl bound is changed on (MyControl)Resize and (ScrollBar)OnValueChanged.
Using a Timer helps a little, but i don't know if this is realy the right solution.
EDIT :
I have found the problem, aufter reactivating SetRedraw and Refresh, OnPaint fires 38 times with 37 Controls. Is the any workaround?
It gets slow with that many controls, 37 of them is just rather a lot. For comparison, Microsoft Outlook uses about 50 windows, you've got 38 for just a control. It gets extra slow because of transparency effects on those controls. The OnPaint() method runs so often to provide the background pixels of the controls. You can't always fix that, a Button for example is going to let its parent draw the background even if it is not transparent. Controls are very convenient but they are not cheap.
Only one way to really get ahead here: stop using so many controls and stop trying to support transparency. Not sure what you use, but a Label for example is especially wasteful. Using TextRenderer.DrawText in your OnPaint method also can draw a label, minus the cost of a Control.
I was able to speed it up, by adding a custom flag which tells me if the OnPaint is fired by my control or by a child control. If my control fires the event i draw my controls content to a bitmap and just blit it for the childcontrols.
I've made a control which contains few other controls like PictureBox, Label and TextBox. But I'm having two problems:
1. I tried to paint some things on top of my control and when I'm overriding OnPaint, it results that things I try to draw are under controls that my control contains. Areas on which I would like to draw intersect with controls in ways that are not easy to predict. I mean that it includes something drawn on controls inside as well as on base of control. Is there a simple way to draw something on top of all contents of my control? Setting ForeColor to Transparent isn't a solution that would help me much.
2. I have a problem with grabbing mouse click events when I place my control on a form and add click event handling. It only works when I click on an area not occupied by controls inside. I would like the whole control to react to clicks and other actions like it was one consistent control. How can I redirect/handle these clicks to make them work the way I want?
Thanks in advance for any tips
edit:
Thanks for answer, i hoped for something less complexed.
Finally i decided to do it from scratch, painting all necessary things directly on surface of my UserControl, TextBox shows up only when it is necessary. That solved all of my problems with drawing and events.
MTH
1) Overriding a control's OnPaint event allows you to draw onto that control. Whenever that control is painted, your drawing code will be executed. Then, and only then, will the child controls be drawn on top of that control, each of them with their own OnPaint events executing. Each control is its own window (in the sense of the Windows API), and therefore each is responsible for drawing its own surface. This explains why your graphics are being covered up by the child controls, and why they are only visible in the empty spaces where no child controls have been placed.
The upshot is that drawing on top of multiple controls inside a container is not well-supported. There are a couple of hacks you could try to help get around these limitations, but you may well be disappointed.
The first possible hack is to draw your graphics on the container control as you've already done, and then also draw on top of the child controls themselves. You'll need to override each child control's OnPaint event in order for this to work, however, which immediately presents a problem. A TextBox control (along with a ComboBox, ListView, TreeView, and a handful of other controls) doesn't do its drawing in an OnPaint event; it's drawn natively by the operating system. The possible workarounds for this are so potentially painful that you might as well forget about it and change your design.
The second possible hack is to add another control that sits on top of the container control and do your drawing on its surface. Perhaps the easiest way to do this is by creating something like a transparent panel using the WS_EX_TRANSPARENT extended window style:
public class TransparentPanel : Panel
{
protected override CreateParams CreateParams
{
get
{
const int WS_EX_TRANSPARENT = 0x00000020;
CreateParams cp = base.CreateParams;
cp.ExStyle |= WS_EX_TRANSPARENT
return cp;
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
//Do nothing
}
}
2) Again, it becomes relevant that each child control is its own window and handles its own mouse input events. That's why the control whose MouseClick event you've overridden is only detecting the clicks that occur directly on its surface. The other clicks get passed to the child controls, who just ignore them. Events in WinForms are not bubbled up the control hierarchy.
As before, one possible solution is to handle the MouseClick event (and any others you want) for both the container and child controls. If you decide to go this route, I think I would recommend consolidating your logic into a single routine and simply calling that routine from each control's event handler.
If you're looking for something more comprehensive, you might check out Broadcasting Events through a Control Hierarchy, although I haven't taken the time to read it.
Of course, you could also send the relevant mouse input messages to your parent container control from each child control using the SendMessage API function. But I'll leave that as an exercise for the reader because I feel like you asked a simple question expecting a simple answer.
I have a Control that can overlay multiple C# user controls in my GUI. This control has a semi-transparent background in order to 'grey-out' portions of the GUI and the class looks somethink like this:
public greyOutControl: UserControl
{
// Usual stuff here
protected overide OnPaint()
{
paintBackround();
base.OnPaint();
}
}
Currently the control sometimes gets caught in a loop and constantly re-draws the background, making the semi-transparent color appear less and less transparent.
My idea to combat this is the following (in broad terms):
1) Determine what controls the greyOutControl is on top of
2) call Refresh() on those controls to update the display
3) continue drawing the greyOutControl.
My question is: How can I determine which controls the greyOutControl overlaps?, or is there a way that I can refresh only the part of the GUI that greyOutControl covers?
Why don't you keep track of your transparent controls and paint them after all the other controls are drawn?. Painting anything at the top of the Z-order shouldn't cause the other controls to be repainted.
I don't see a direct way of finding the overlapping controls. I think you might need to check the whole control tree to find out that. About refreshing, you can use Control.Invalidate(Rectangle) method to specify which part to refresh.
The solution to this problem I found was to programmatically take a screen shot of the area being overlayed and then use that image as the background for the control being overlayed. This then allows you to put the alpha overlay into the image within the OnPaint() method and the control to draw itself correctly.
This does have the disadvantage that the background isn't updated in the overlapping control, but unless there was a number of event handlers watching if something changes and then update the overlayed control I cant see any way around the issue. Sometimes I regret not trying to use WPF!