Draw Border of Panel When it is being Active Scroll - c#

I just got problem when i'm trying to make a panel border, firstly i have set my properties panel to be: "AutoScroll = true;"
then i put the border drawing codes in Panel event:
ControlPaint.DrawBorder(e.Graphics,
ClientRectangle,
Color.Black, 5,
ButtonBorderStyle.Solid,
Color.Black, 5, ButtonBorderStyle.Solid,
Color.Black, 5, ButtonBorderStyle.Solid,
Color.Black, 5, ButtonBorderStyle.Solid);
actually i still got a second problem and i will explain it all here.. I hope you don't mind.
well, the panel border will get some crash when the panel scroll is being active. take a look at the picture:
even i put
`e.Graphics.TranslateTransform(this.AutoScrollPosition.X, this.AutoScrollPosition.Y);`
it doesn't looks like a border panel because it just draw a rectangle inside panel when the scroll is being active. that's not what i expected, but i need a Border
i bet, the problem is the source code, that is not because it is not possible, take a look at System.Windows.Forms.Panel i think it is perfect panel although it simple.
please help me to solve this problem. this has made me confused

This is not going to work well, you are fighting a Windows system option named "Show window contents while dragging". It is turned on in all recent Windows versions and you cannot reasonably turn it off. What the option does is scroll the window content in an optimized way when you operate the scrollbar. It copies the window pixels by the scroll amount and asks for a repaint for the part of the window that got revealed by the scroll.
Trouble is, that also moved your painted border. So you'll see the black line in the bottom getting moved up as well. But it doesn't get erased because Windows asked only for a repaint of the part of the window that got revealed by the scroll. So it "smears". The top line just disappears, getting scroll off. To get this fixed, you need to repaint the entire window. Easy to do by implementing the Scroll event for the panel control:
private void panel1_Scroll(object sender, ScrollEventArgs e) {
panel1.Invalidate();
}
That fixes the problem, but you may still notice an artifact on slower machines. That black line is still getting moved up, to be quickly overpainted again by your Paint event handler. The "quickly" is the issue, if it is not that quickly then you'll still see that line move. The artifact is, erm, interesting, you'll see the line doing the pogo, jumping up and down. The human eye is very sensitive to motion like that, it was an evolutionary advantage to be able to be good at detecting the lion in the tall savanna grass.
Trying to keep objects stationary in scrolling window just doesn't work that well. You can monkey with the panel control and implement a message handler for WM_NCCALCSIZE to give the panel a non-client area but that's all rather painful.
The simple solution is to just have the Form draw a rectangle around the panel:
protected override void OnPaint(PaintEventArgs e) {
var rc = panel1.Bounds;
rc.Inflate(1, 1);
e.Graphics.DrawRectangle(Pens.Black, rc);
base.OnPaint(e);
}
Or easier yet, set the panel's BorderStyle.

Related

How can I avoid from being see the thick rectangle around a control when selecting the control at run time when the application is running?

This is not my control I downloaded it from this page :
Advanced TrackBar
Then I dragged it to form1 designer from the toolbox.
When the application is running and I use the trackBar at some point it's like I selected the control and it's showing the selected rectangle around it.
and if I select/use another control at runtime the rectangle around the trackBar still exist.
At the bottom of the OnPaint override method in the MACTrackBar.cs file, comment out these lines:
// Draws a focus rectangle
// if(this.Focused)
// ControlPaint.DrawFocusRectangle(e.Graphics,
// Rectangle.Inflate(this.ClientRectangle, -2, -2));

How can I achieve sprite composition that actually works?

My requirements:
A control with a background can have sprites drawn on it.
It needs to be possible to move sprites around programmatically and by setting up dragging events.
The sprite's image may have alpha transparency; sprites must correctly alpha-blend with both the background and each other.
Drawing order must match the logical order of sprites - clicking on a sprite that appears to be on top should initiate dragging that sprite, never one that appears underneath it.
Attempt 1
The obvious approach is to create a custom control to represent the Sprite. Let's try it:
public partial class Sprite : Control
{
public Sprite()
{
InitializeComponent();
For testing purposes, we'll make it so clicking a Sprite just brings it to the front, no dragging yet:
this.Click += Sprite_Click;
}
void Sprite_Click(object sender, EventArgs e)
{
this.BringToFront();
}
So we can identify each sprite and verify the drawing order, let's set a 'frame' color for each:
public Color FrameColor { get; set; }
And draw the sprites as a translucent white body with a solid-colour frame - we should then be able to verify that the background appears shaded behind a single space, shaded more strongly where sprites overlap, and the borders overlap as expected:
protected override void OnPaint(PaintEventArgs pe)
{
Graphics g = pe.Graphics;
g.FillRectangle(
new SolidBrush(Color.FromArgb(128, 255, 255, 255)),
DisplayRectangle
);
g.DrawRectangle(new Pen(FrameColor, 10), DisplayRectangle);
}
And then we can design a form with a dark background, set up a few Sprites on it, make them overlap, give them different frame colours, and test.
Naturally, it doesn't work. Controls by default have a background colour, which appears to default to white, or something close to it. So these sprites overlap properly, but they have opaque white middles.
Attempt 2
Well, surely we can just set that background colour to transparent? A bit of Googling in Microsoft's documentation tells us that we certainly can, but not directly (as someone else on SO found out the hard way). It needs a bit of configuration:
// in constructor
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = Color.Transparent;
Okay, so now the background shows through each Sprite, but the Sprite borders don't show through each other. What gives? A little more Googling tells us that this 'transparent background colour' support is actually a bit of a hack; basically, the parent Control implements its background painting to copy image data from the parent component and composite with that, ignoring everything else.
To be totally honest, I expected the system to be designed such that this would just work automatically - i.e., any time any drawing occurs, it composites with whatever's underneath it on screen, and the only reason controls aren't constantly showing through each other is because of their explicitly opaque backgrounds. But no such luck. I guess the whole system was designed back when people didn't want that sort of thing by default, for performance reasons.
Anyway.
Attempt 3
Well, if the background painting is what's causing the problem, maybe we can just disable it completely?
protected override void OnPaintBackground(PaintEventArgs ignored) { }
Nope. If we click on sprites, we can see them re-order, and composite with each other - but they don't composite with the background image. And, more strangely, when the parent window is invalidated (by resizing the form, or minimizing and restoring it), the sprites turn opaque again.
Attempt 4
After even more Googling, we find StackOverflow answers like this and this, articles like this etc. And they all point at the same low-level hack:
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle = cp.ExStyle | 0x20;
return cp;
}
}
For this to work, we also need to disable painting the background, but of course it no longer matters if we set up that "transparent" background, since that painting logic has been suppressed. (Curiously, it's also possible to suppress painting the background by setting an Opaque option in the ControlStyles; while that sounds like the opposite of what we want, it seems to work about as well.)
Well. It almost works. The Sprites composite both with themselves and the background. But now a very curious thing happens: the drawing order is wrong, and not even consistent. Invalidating the window in different ways (resizing vs. minimizing and restoring) will bring a different Sprite to the front visually; but in general it doesn't correspond to what was clicked and in what order. If we add explicit invalidation logic:
// in click event handler
Parent.Invalidate(this.Bounds, true);
then clicking a sprite actually appears to send it to the back visually - although again, the drawing order may change if we resize the window or minimize and restore it.
What gives? How can I solve this once and for all? Options I've considered:
Burn the controls to the ground; make a parent control keep track of a list of sprite Images, track mouse drag and click events, and do all the picking and rendering logic itself. Should be guaranteed to work, but is a ridiculous amount of work for the job.
Make the controls not draw at all, by overriding both background and foreground painting, and have them expose a property with their desired Image. Let a parent handle all the rendering, but fall back on the built-in Control logic for picking, mouse drags etc. Less work, but seems iffy (maybe there's some hidden thing that still forces Controls to draw something?), is a fairly tightly coupled design, and might still have performance issues if a naive approach is taken to invalidation (?).
After even more research, I appear to have an answer.
First, some explanation about drawing order: as far as I can tell now, it was consistent in Attempt 4, but invalidating by resizing the window causes gradual invalidations of different areas, which produces misleading results. The order is always top to bottom, which is the opposite of what you want for compositing, but which enables optimizations when you treat everything as opaque by clipping the invalid regions. (I'm not sure how much CPU effort this really saves given that the clipping paths can potentially get quite complex, but I guess it also helps reduce flickering when you aren't using double-buffering, since you avoid repainting a given pixel on any given refresh.)
But as it happens, if you read between the lines, you discover that there is another flag in the "extended window styles" (the 0x20 flag that we set in Attempt 4 is WS_EX_TRANSPARENT), called WS_EX_COMPOSITED (value 0x02000000), that reverses the drawing order to be what we want instead. Or you could have just read the documentation more closely in the first place. Oops.
(You'd think they could have just designed it to take care of all this logic automatically - detect which sprites have a translucent or transparent background colour, draw the ones with opaque backgrounds top-down first for speed, then the others bottom-up for correctness while clipping around the opaque ones. Oh well.)
A quick note here: because compositing naturally involves repainting the same area multiple times, this flag also sets up double-buffering that applies across all children of the window created with that flag (as far as I can tell, in Windows API parlance, every control is a "window", as well as the forms themselves). So it seems that people often use it to avoid flickering on forms with lots of controls, even when they don't intend for controls to overlap. However, ironically, WS_EX_COMPOSITED might actually cause huge amounts of flickering with certain controls, such as TabPage. (I'm running 8.1 and was able to reproduce the described problem very clearly.) The documentation states
This cannot be used if the window has a class style of either CS_OWNDC or CS_CLASSDC.
So maybe that has something to do with it. We can avoid this by restricting the "composited area" to a region using a simple control such as a Panel. At least in my tests, that will fix the problem as long as the TabPage isn't a child of the Panel, even if it overlaps.
It also seems that this might not work under XP, but hey, it's 2014 now.
Anyway, on to the code.
What we need to do is set a CreateWindowEx flag, with the same CreateParams technique, in some ancestor of the Sprite, such as the Form they're on:
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle = cp.ExStyle | 0x02000000;
return cp;
}
}
We still need the WS_EX_TRANSPARENT setting, and disabling of background paint on the Sprite controls, from attempts 3 and 4. We don't need to set up a transparent BackColor, because we're not going to paint the BackColor. I have no idea why allowing a fully transparent BackColor to be painted messes things up, but it apparently does.
This will apparently work even if there are intervening controls, i.e. if we put the Sprite controls inside a Panel on the Form. (If we want to restrict the effect to a Panel, as discussed above, we'd have to subclass Panel; but my project already does so :) ) Everything works perfectly in my test project, and I don't even need to explicitly Invalidate anything.

c# drawing on the panel and scrolling the result

I have problem with showing a diagram which is too big to see on one panel.
I have to scrollbars which should change the point of view on the diagram, but when i want to scroll the picture, the shapes are moving on the different position, everything getting crushed.
it looks like this here link
when i show it,and like this here link when i try to look on the bottom of the graph
it looks like application drawing the shapes every time when i scroll the panel, and when i go on the bottom of picture the point on the top left corner still is (0,0) not (0,500)
I have algorithm, which giving value of location on the panel and nr id of object to the array, then i have the loop which drawing it, getting info from dictionary about the object and his position from array.
How this problem can be solved ?
Thx for any advice's
Edited
I dont want to draw it again i want to draw one big graph, something like this(link in the comment), but i know that user can make for example 50 objects(shapes) and that kind of big graph cant be seen on the small panel so we have to have a chance to scroll and see the bottom of grapf, left side or this side which he want.
I'll try to give more details about application.
When you lunch it, you see the control panel(form1), where you adding events/functions/xor/or every of this option have their own shape on the graph.
So user adds for example event with text, pressing the button add which creates the object and adding it to the collection. He can adds events/functions,xor/or as many as he wants.
Ok, when he adds everything what he wanted, and now he would like to see graph so he pressing the button "generate the diagram", now the application is showing the next windwow with panel and scrollbars. You can see the window in links.
In the second form after this line
private void panel1_Paint(object sender, PaintEventArgs e){
I have algorithm which is putting the coordinate values to table, then forech loop is taking from dictionary ( collection):
text which should be shown on diagram in the middle of shape,
value which determine a type of shape on the panel.
from arrays loop taking the coordinate values.
This how its work, I can also put a code in here when its needed.
The standard mistake is forgetting to offset your drawing by the scroll position. Use the panel's AutoScrollPosition property, like this:
void panel1_Paint(object sender, PaintEventArgs e) {
e.Graphics.TranslateTransform(panel1.AutoScrollPosition.X, panel1.AutoScrollPosition.Y);
e.Graphics.DrawLine(Pens.Black, 0, 0, 300, 2000);
}
The Panel control is in general pretty cranky about painting, it was designed to be a container control. You typically also want it double-buffered and to force a repaint when it is being resized. Setting the DoubleBuffered and ResizeRedraw properties requires deriving your own control from Panel.
It looks like application drawing the shapes every time when i scroll the panel
Why don't you erase the drawing area and draw the shapes again?
Maybe you can post a code snippet?

Removing Windows' ugly Selection marker thing from Splitter in SpitContainer Control

I have a SplitContainer control, and the Splitter in the middle is very ugly. By setting the BackColor of the SplitContainer to (insert color here), then setting the BackColor of Panel1 and Panel2 to white, I can have my splitter looking nice. But by default, Windows puts the selection mark over the Splitter, even before it's selected.
How can I make sure that the selection mark never shows on the Splitter?
I think by "Selection Marker Crap", you mean the fuzzy line that indicates the control is selected. If you don't want this showing up, set some other control to be selected at startup. Something like:
Textbox1.Selected = true;
This should solve your issue if it is just one of it not being selected. However, this will come back if you select the item to resize something. In that case, you could put something in the mouse_up event to move the selection off of the control. That way, the user moves the splitter bar and then when they let go, the selection gets cleared off of the splitter.
Another way would be to make the splitter bar narrow enough that the gray fuzzy line doesn't show up. To do this, you could do the following (tested):
splitContainer1.BorderStyle = BorderStyle.FixedSingle;
splitContainer1.SplitterWidth = 1;
I experienced the same problem, and fixed it by setting TabStop to False in the Properties window for SplitContainer1.
This could annoy people who depend or insist on using the keyboard to operate every aspect of your form, but other than that it will work. Controls inside the SplitContainer will remain tab-able, just not the SplitContainer itself.
This code will move the focus from the splitContainer to TreeView shortly after moved.
private void splitContainer1_SplitterMoved(object sender, SplitterEventArgs e) {
if(this.splitContainer1.CanFocus) {
this.splitContainer1.ActiveControl = this.treeView1;
}
}
You could add an event handler to steal the focus from the container on MouseUp's... It's a little messy but it works. :)
I tried a lot to remove splitter but nothing work. I did some different why we need to use splitter for that we can use picture box control make it width (or) height depend upon your project set 5 or 3 .... after picture box mouse move event write code like...
picturebox property-cursor change the cursor type Hsplit its look like splitter
private void picturebox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)//this for mouse left click its work
{
//write you code here if you use panel set panel height or width it reaches...
Cursor.Position = new Point(e.X, e.Y); // this for mouse cursor position according your //project do some fine tune you will get its work...
}
its work because i tried lot for this and i itself found this method...
I set the TabStop to false and it went away.
Most simple solution i found/made - create a button, select it, and hide it.
All via code. there is not side effects or problems with this, place it in the forms load event.
Button DeSelectButton = new Button();
this.Controls.Add(DeSelectButton);
DeSelectButton.Select();
DeSelectButton.Visible = false;

Winforms UserControl shows rectangles with Inverted Colors

In a Winforms application (C#2, FX2.0, VC2008) I am using a Panel derived Control to display custom controls. The controls are arranged vertically and there are usually more than fit in the visible area of the panel.
I have drawn a litte sketch of it:
Panel http://www.ericschaefer.org/Panel.png
Sometimes (usually after scrolling inside the panel) rectangular areas appear to have their colors inverted (green part in sketch). These areas are random in size but seem always to be at the right edge of the panel. As you can see in the sketch the arrow buttons of the panels scrollbar are also inverted, but not the thumbslider and the scroll area.
By inverted colors I mean black becomes white, white becomes black, blue becomes brown, etc...
I am out of ideas.
Can this be caused by my application?
Is it even possible to draw into the scrollbar arrow buttons?
Any ideas?
EDIT: "Screenshot"
EDIT: I was wrong about the Panel. It has been a Panel in the past but it is now a UserControl. Like this:
public class MyPanel : UserControl
{
public MyPanel()
{
DoubleBuffered = true;
BorderStyle = BorderStyle.Fixed3D;
BackColor = Color.DarkBlue;
VScroll = true;
HScroll = false;
AutoScroll = true;
AutoScrollMargin = new Size( 0, 4 );
}
}
Are you using the System.Windows.Forms.Panel ? What did you meant when u said Panel derived control ?
If you are using System.Windows.Forms.Panel then, this looks like a problem with the custom control and not the panel.
could you provide more info on the custom control, if possible ?
From your photo it looks like something's highlighted, but the border doesn't match up with any visible controls.
Have you got an control that's hidden behind the rest somehow?
Solved:
Apparently it was the fault of the touch screen driver. I can now reproduce the behaviour. If you click the "scroll down" button on any scroll bar via touch screen and hold it down for a while it keeps scrolling when you release it (the button stays pushed). After that you can click whereever you want, there will always be the "inverted colors" on the left side of the scroll bar. It happens with any application (explorer.exe!). Now it gets even better: Generally in Windows if you drag a scroll bar thumb button you can drift off of the scroll bar if you keep the mouse button pressed. But if you drift away too far the button will snap back to its original position (where you started dragging). Try it! The inverted colors rectangles are exactly as wide as the area where you can drift off without the button snapping back. I don't even try to understand this.
We installed a newer driver for the touch screen which does not keep scrolling even after you released the "scroll down" button and the issue disappeared. My blood pressure is back to normal but all the grey hair is probably not turning black I guess...
Thanks anyways...

Categories

Resources