Refresh child controls in flowlayoutpanel when scrolling - c#

In .NET 3.5 with winforms I'm making an image thumbnail viewer control.
The main control is derived from a FlowLayoutPanel which takes a list of images and displays them. The images which are displayed are made from a CustomControl on which I paint the and the accompanying label as well as the border of the control.
Images can be selected through clicking and yada yada as you would expect for that kind of control.
Here's a screenshote to illustrate:
That part works fine. The problem is then when I scroll the FlowLayoutPanel derived control the border doesn't redraw properly and there are lines remaining as shown in this screenshot:
I have set both the FlowLayoutPanel and the Images to double buffered. And the images and labels do not have the problem, so I suspect it is something else, but can't figure out what it is.
I think the method used to paint the border of the images might be at fault. Here's the code I use:
protected override void OnPaint(PaintEventArgs e)
{
Rectangle captionContainer;
captionContainer = new Rectangle();
if (!string.IsNullOrEmpty(this.Caption))
captionContainer = this.DrawCaption(e.Graphics);
if (this.Image != null)
this.DrawImage(e.Graphics, captionContainer);
this.Size = new Size(this.Padding.Horizontal + this.ImageSize.Width, this.Padding.Vertical + this.ImageSize.Height + captionContainer.Height);
ControlPaint.DrawBorder(e.Graphics, e.ClipRectangle, this.currentBorderColor, ButtonBorderStyle.Solid);
base.OnPaint(e);
}
I'll post more code if needed, but it is pretty lengthy, so I do not want to put too much code unless it actually is necessary.
Can anybody see where this is going wrong?

I have solved by also drawing the border using the Graphics object. Replacing
ControlPaint.DrawBorder(e.Graphics, e.ClipRectangle, this.currentBorderColor, ButtonBorderStyle.Solid);
with
e.Graphics.DrawRectangle(new Pen(this.currentBorderColor, 1F), new Rectangle(Point.Empty, new Size(this.Width - 1, this.Height - 1)));
does the trick. No idea why one works and not the other though...

Related

C# WinForms, ToolTip with semi-transparent background

i'm working on this small WinForm app and decided to use custom drawn tooltip with semi-transparent background. So i started with setting the OwnerDraw property on ToolTip to True, created event handlers for Draw and Popup events (see the example code bellow. The commented version isn't working either).
private void toolTip_Popup(object sender, PopupEventArgs e)
{
e.ToolTipSize = new Size(400, 400);
}
private void toolTip_Draw(object sender, DrawToolTipEventArgs e)
{
//e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(120, Color.Red)), new Rectangle(e.Bounds.Location, e.Bounds.Size));
e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(120, Color.Red)), new Rectangle(0, 0, 400, 400));
}
Now when the ToolTip is shown for the first time for a specific control everything works as intended. See the picture bellow (400x400 tooltip with semi-transparent red background).
But when i hover over the same control for the second time ToolTip loses its semi-transparency. See the picture bellow. Why is that so?
Thank you all for your help. I'm pretty sure that Ben Voigts answer, or Jimis comments could solve this problem somehow too (i'll try them out later and update the answer if i'll be able to utilise them).
I based my solution on the first comment made by Hans Passant where he suggested to use Graphics.CopyFromScreen() in Popup event handler(toolTipDay_Popup), to capture the image underneath the ToolTip and then in Draw event handler(toolTipDay_Draw) i just drew the captured image.
(There is a problem with different DPI scalings as noted by Hans Passant, but that can be +- solved by Farshid T answer in How to get Windows Display settings?, i didn't include it in code bellow).
So the solution i'm using right now is as follows:
Bitmap dayToolTipBackground = new Bitmap(200, 200);
private void toolTipDay_Popup(object sender, PopupEventArgs e)
{
e.ToolTipSize = new Size(200, 200);
var backGraphics = Graphics.FromImage(dayToolTipBackground);
var cursorPosition = Cursor.Position;
backGraphics.CopyFromScreen(new Point(Cursor.Position.X, Cursor.Position.Y + 21), new Point(0, 0), new Size((200, 200)));
}
private void toolTipDay_Draw(object sender, DrawToolTipEventArgs e)
{
e.Graphics.DrawImage(dayToolTipBackground, new Point(0, 0));
e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(120, Color.Red)), new Rectangle(e.Bounds.Location, e.Bounds.Size));
}
The first time you hover over the control, a new instance of tooltip is created with the transparent color (ARGB.120). When you click outside the tooltip, whether the tooltip object is fully disposed or are you simply setting the instance as invisible?
When you are hovering over the instance for the second time, then, if the old tooltip object is not disposed, there is a chance that the same object is called again. So now when you do Graphics.FillRectangle() on an existing tooltip with color = ARGB.120, you are just overlaying another layer of ARGB.120 color on it, which will darken it further because the color levels are changed.
Therefore when you click outside the tooltip after you call it for the first time, you might need to dispose the tooltip object(or the e.Graphics object, if that doesn't affect other parts of your application) and create new tooltip objects every time you hover over the control.
Windows needs to be told that the windows beneath the popup need to be redrawn. This is done via a "layered window" style. With layering, the content gets drawn in z-order and transparency blending works. Without layering, only the top window gets sent a repaint and it draws on top of meaningless leftover data in the DC's screen buffer.
You can try p/invoking SetLayeredWindowAttributes
I strongly recommend reading the MSDN documentation on Layered Windows:
Here and here

Change Border of ToolStripComboBox with Flat Style

I would like to be able to change the border color of ToolStripComboBox controls in some of my toolstrips, since the default border color of ComboBoxes when used with flat styling is SystemColors.Window, which is basically invisible against the default control color of the toolstrip. After a lot of digging around in Reflector, I don't see any obvious way to do this, since all the infrastructure behind ComboBox rendering is highly protected behind internal and private interfaces.
Outside of ToolStrips, a common solution I've seen proposed for fixing border color on ComboBoxes is to subclass ComboBox, override WndProc, and manually paint the border. This can't work for ToolStripComboBox controls since the internal ComboBox control is its own private subclass of ComboBox, with no way that I can see to replace the instance of the control.
An alternative solution I'm considering is putting one of the extended ComboBox objects into a ToolStripControlHost, which allows me to draw a border, but then I have to give up some of the professional renderer tweaks. A secondary drawback I've noticed is that I get occasional flicker during mouseover.
Switching my design to WPF is not an acceptable solution. Wrapping controls in parent controls for drawing borders is also not acceptable, as this gains nothing over the ToolStripControlHost alternative.
Does anyone have a clever solution to defeat this problem, or is there an existing (permissively-licensed) re-implementation of the ComboBox flat-style rendering stack out in the wild, which fixes some of the shortcomings in the existing implementation?
Here's a way to make it work ... sort of :)
Create an event handler for the Paint event of the ToolStrip. Then loop through all of the ToolStripComboBoxes and paint a rectangle around them.
private Color cbBorderColor = Color.Gray;
private Pen cbBorderPen = new Pen(SystemColors.Window);
private void toolStrip1_Paint(object sender, PaintEventArgs e)
{
foreach (ToolStripComboBox cb in toolStrip1.Items)
{
Rectangle r = new Rectangle(
cb.ComboBox.Location.X - 1,
cb.ComboBox.Location.Y - 1,
cb.ComboBox.Size.Width + 1,
cb.ComboBox.Size.Height + 1);
cbBorderPen.Color = cbBorderColor;
e.Graphics.DrawRectangle(cbBorderPen, r);
}
}
Here's what it looks like (note that you may need to adjust the Height of the ToolStrip to prevent the painted border from being cut off):
improvement:
check the type of the toolstrip item,
so the program will not crush if it is toolstipLabel for example.
foreach (var item in toolStrip1.Items)
{
var asComboBox = item as ToolStripComboBox;
if (asComboBox != null)
{
var location = asComboBox.ComboBox.Location;
var size = asComboBox.ComboBox.Size;
Pen cbBorderPen = new Pen(Color.Gray);
Rectangle rect = new Rectangle(
location.X - 1,
location.Y - 1,
size.Width + 1,
size.Height + 1);
e.Graphics.DrawRectangle(cbBorderPen, rect);
}
}
toolStrip1.ComboBox.FlatStyle = FlatStyle.System;
This sets the default, OS-styled, border around the combo box. It is a light grey and thin border on Windows 10. Although, depending on the background, this may not show. In which case, you could try the other options like FlatStyle.Popup.
If the presets aren't what you are looking for, the other answers allow you to draw a custom border. However, since the rectangle is drawn with +1 pixel size around the combo box, the border is 1 pixel larger than the combo box. Removing the +1s and -1s doesn't work either.

c# - clear surface when resizing

I'm trying to build my own custom control for a windows forms application in C#.Net. Currently I paint some rectangles and other graphic elements using the paint event.
When I now resize the app form to fit the desktop size, all elements are repainted (which is exactly the behaviour I need) but the old one's are shown in the background.
Here's what I'm doing by now:
Pen penDefaultBorder = new Pen(Color.Wheat, 1);
int margin = 5;
private void CustomControl_Paint(object sender, PaintEventArgs e) {
CustomControl calendar = (CustomControl)sender;
Graphics graphics = e.Graphics;
graphics.Clear(Color.WhiteSmoke);
graphics.DrawRectangle(penDefaultBorder, margin, margin, calendar.Width - margin * 2, calendar.Height - margin * 2);
//...
}
Neither the graphics.Clear, nor adding a graphics.FillRectangle(...) will hide the old rectangle from the surface.
Ideas? Thank you all.
Paint events usually don't request an update for the entire canvas, just the area specified in the PaintEventArgs. I'm guessing what's happening is that only the newly-exposed regions of the canvas are being passed in the PaintEventArgs.
This one of the reasons that you shouldn't do any rendering in the Paint event. You should render to an offscreen bitmap - a buffer - and copy from that buffer to the control's canvas in the Paint event.
Searching for "double buffering" here or on Google will give you many examples of the technique.
Have you tried .Invalidate() to cause the form to redraw?

Problem with using Paint event in c#

I have used this below code before and was working perfectly. When I use the same
in one of my window form, the color of the form is not changing.
I mean after the page loads it shows the default color of the form. But when I try to debug the code below, it changes the color of the form perfectly. The problem is, after executing the last line of the code the color of the form goes back to the default color.
Am I missing something?
The form looks like a windows taskbar, and it has one tab control in it.
private void TaskBar_Paint(object sender, PaintEventArgs e)
{
Graphics mGraphics = e.Graphics;
Pen pen1 = new Pen(Color.FromArgb(96, 155, 173), 1);
Rectangle Area1 = new Rectangle(0, 0, this.Width - 2, this.Height - 2);
LinearGradientBrush LGB = new LinearGradientBrush(Area1,
Color.FromArgb(96, 155, 173),
Color.FromArgb(245, 251, 251),
LinearGradientMode.Vertical);
mGraphics.FillRectangle(LGB, Area1);
mGraphics.DrawRectangle(pen1, Area1);
}
There isn't much to go on here. What is this handler attached to? The Paint event of the Form? If so, you should override OnPaint() instead of attaching to the handler. My guess is that some other method is doing some painting too. You need to track that down. Without more code, it is not very likely that anyone here can help you. Sorry.

Painted Border Runs In Certain Situations

In my tool I use a a panel to change pages. Each page has it's own panel and when I change a page I send the panel with the controls. On the panel I use as the canvas I have the following paint event:
private void panelContent_Paint(object sender, PaintEventArgs e)
{
e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
// Paints a border around the panel to match the treeview control
e.Graphics.DrawRectangle(Pens.CornflowerBlue,
e.ClipRectangle.Left,
e.ClipRectangle.Top,
e.ClipRectangle.Width - 1,
e.ClipRectangle.Height - 1);
e.Graphics.Flush();
base.OnPaint(e);
}
This method basically draws a nice border around the panel so it look better. For some reason when I move a another form above this panel the lines that make up the border start to run a little. Occasionally small lines will be drawn from the border too. The problem only happens for a few seconds before the entire panel redraws again. Is there anything I can do to prevent this from happening?
ClipRectangle tells you what part of the control needs to be repainted. If you're moving something over it, this is probably going to be the intersection of your object and the one being moved. You can use this information to more efficiently repaint your control.
You probably want to draw the rectangle from (0, 0) to (panelContent.Width-1, panelContent.Height-1).

Categories