Refresh display of transparent user control - c#

I've written a user control for that supports transparent background (among other things).
However, I've found a problem that when the background is transparent, and you change the text of the user control, the previous text is still displayed on the screen, under the new text, making it impossible to read.
I've been googling for half a day now, finding all sort of suggestions that didn't work in my case, most of them involving painting the parent control onto a bitmap and draw that bitmap on my control's surface.
However, in my case the parent control is also transparent so I've tried to go up to the form's level like suggested here but i was getting an InvalidArgumentException,
I've tried invalidating the parent control like suggested here but no luck either.
my code is basically this (truncated to the bare minimum):
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20;
return cp;
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
if(this.BackColor != Color.Transparent)
{
base.OnPaintBackground(e);
}
}

I keep learning again and again that simply explaining the problem to someone else often helps you solve it.
So, After combining a few answers from all my searches,
mainly this one about invalidating a specified rectangle of the parent,
and this one about getting the location of the control on the form,
I came up with this:
// Make the Text property browsable again, call Refresh when changed.
[Browsable(true),
DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override string Text
{
get { return _Text; }
set
{
if(_Text != value)
{
this.Refresh();
_Text = value;
}
}
}
// Override Refresh to invalidate the relevant part of the parent form
public override void Refresh()
{
Form form = this.FindForm();
// only for transparent controls that has text and no background image
if (this.BackColor == Color.Transparent &&
!string.IsNullOrEmpty(this.Text) &&
(this.Gradient.BackColor2==Color.Transparent || !this.Gradient.IsGradient) &&
this.BackgroundImage == null &&
form != null)
{
Point locationOnForm = form.PointToClient(
this.Parent.PointToScreen(this.Location)
);
// Invalidate the rectangle of the form that's behind the current control
form.Invalidate(new Rectangle(locationOnForm, this.Size));
}
base.Invalidate();
}
I will still have to deal with parent controls between the current control and the form, but for now this is good enough for me.

I think we are close on this one.
Firstly, you cannot suppress OnPaintBackground like that and expect the rest of winforms to run as expected, this means nothing gets painted to that region at all, so we have left GDI+ or windows or presumably some other construct to "fill in the blanks". The controls on your form that are opaque may want to call your UserControl to get it's background, so we have to paint something...
So we always call base.OnPaintBackground, but if the background is Transparent, then we need to invalidate the entire surface of the control to be repainted.
public Glass()
{
InitializeComponent();
this.SetStyle(ControlStyles.Opaque, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20;
return cp;
}
}
private bool rePaintState = false;
protected override void OnPaintBackground(PaintEventArgs e)
{
base.OnPaintBackground(e);
if (this.BackColor == Color.Transparent)
{
// we want to invalidate and force it to re-paint but just once
if (rePaintState)
{
rePaintState = false;
}
else
{
rePaintState = true;
this.Invalidate();
}
}
}
This is still not complete, but saved most of the flickering I was experiencing when controls above and below the UserControl had textboxes with values refreshing on a timer.
I know its not a huge help, but WPF is really good for these types of UX issues ^-^

Related

Show a Form without stealing focus stopped working after a while

I created my own on screen keyboard with special features.
I'm using override methods ShowWithoutActivation and CreateParams for preventing my Form getting focus (according to this stackoverflow question).
protected override bool ShowWithoutActivation
{
get { return true; }
}
protected override CreateParams CreateParams
{
get
{
CreateParams param = base.CreateParams;
param.ExStyle |= 0x08000000;
return param;
}
}
But after a few Hide() and Show() my Form is again focusable.
To fix it I need to restart application, but it is of course poor solution.
Is it possible to do without restart?
Showing Form after being hidden without giving it focus not help. My application can just be focused again.
I'm noticed that in most cases it happens randomly, but in one of them always: if I show my application after right mouse click on NotifyIcon in tray and choose item from ContextMenuStrip. OnClick I simply call show function.
Do you need to click the window? When not, try this one:
protected override void WndProc(ref Message message)
{
switch (message.Msg)
{
case 0x84: // WM_NCHITTEST
message.Result = (IntPtr)(-1); // HTTRANSPARENT;
return;
}
base.WndProc(ref message);
return;
}

Why does this single line of code cause Visual Studio to crash?

With one line of code, I can cause VS2012 to crash consistently. (By "crash" I mean when I hit Build, Visual Studio hangs indefinitely and I have to kill it from Task Manager.) Here's the code (in a custom user control):
public class TransparentPanel : System.Windows.Forms.Panel
{
protected override void OnPaintBackground(PaintEventArgs pe)
{
this.Invalidate(); //this is the offending line
}
}
To reproduce the problem, create a control based on the above code and add it to a Windows Form; then try to Build Solution. "Build started..." will be displayed in the Status Bar and then VS will immediately and permanently freeze up. I tried troubleshooting, using devenv /log, but the Activity Log displayed no errors or warnings. So my question is, why is this code fatal to the C# compiler? (It causes problems in the IDE too. For example, after adding this transparent control, the Properties pane becomes frozen whenever the Form Designer is open.)
Side question: should this bug be reported to Microsoft? If so, how? (I tried to submit the bug on the MS Connect site, but apparently they are only accepting bugs for VS2013.)
[If you want to know the background of why I was trying to use that line, read on.]
I created a custom (Windows Forms) control for an application. (I was trying to create a control with a partially transparent image, whose location could be changed through mouse interactions.)
I used the following code, which gave me the image (on a panel) with the transparency I was looking for:
public class TransparentPanel : System.Windows.Forms.Panel
{
public Image Image { get; set; }
protected override void OnPaint(PaintEventArgs pe)
{
Rectangle rect = new Rectangle(this.Location, this.Size);
if (Image != null)
pe.Graphics.DrawImage(Image, this.DisplayRectangle);
}
protected override void OnPaintBackground(PaintEventArgs pe)
{
//this.Invalidate();
Brush brush = new SolidBrush(Color.FromArgb(0, 0, 0, 0));
pe.Graphics.FillRectangle(brush, pe.ClipRectangle);
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20; // WS_EX_TRANSPARENT
return cp;
}
}
}
However, the transparency doesn't "stick". When the control is relocated, for example with this event handler:
private void transparentPanel1_MouseClick(object sender, MouseEventArgs e)
{
int y = this.transparentPanel1.Location.Y ;
int x = this.transparentPanel1.Location.X ;
this.transparentPanel1.Location = new System.Drawing.Point(x-5, y-5);
}
...the transparent portion of the control retains the same background it had when it was first painted. (This can only be seen if another control is placed behind it. Sorry, hard to describe.) So I was attempting to use the Invalidate() to repaint the control, and therefore, repaint the transparent portion to match whatever was behind the control after the relocation. This is when the VS bug appeared. (Actually I didn't notice the bug right away, so it was rather excruciating to isolate the offending line of code.)
public class TransparentPanel : System.Windows.Forms.Panel
{
protected override void OnPaintBackground(PaintEventArgs pe)
{
this.Invalidate(); //this is the offending line
}
}
That is akin to an infinite loop.
When you panel repaints, your handler is called... and you tell it to invalidate itself... which causes a repaint... which calls your handler... etc.
The designer loses its mind. You don't need or want to invalidate the control from within OnPaintBackground, unless it is conditional (still rare).

RichTextBox scrollbar flicker

I'm having an issue with a richTextBox inside of a windows form.
I have enabled vertical scrolling and everything works fine, except when I use the mouse to drag the scrollbar. When I do this, the scrollbar just stays in place and flickers until I release the drag. The bar scrolls regularly without flickering when I use the scroll wheel on my mouse, or click the up/down arrow keys.
Any ideas as to why this is happening?
In case anyone else ever has this problem, I found a solution here: http://www.angryhacker.com/blog/archive/2010/07/21/how-to-get-rid-of-flicker-on-windows-forms-applications.aspx
Essentially, all that needs to be added to the form.cs file is:
int originalExStyle = -1;
bool enableFormLevelDoubleBuffering = true;
protected override CreateParams CreateParams
{
get
{
if (originalExStyle == -1)
originalExStyle = base.CreateParams.ExStyle;
CreateParams cp = base.CreateParams;
if (enableFormLevelDoubleBuffering)
cp.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
else
cp.ExStyle = originalExStyle;
return cp;
}
}
private void TurnOffFormLevelDoubleBuffering()
{
enableFormLevelDoubleBuffering = false;
this.MaximizeBox = true;
}
private void frmMain_Shown(object sender, EventArgs e)
{
TurnOffFormLevelDoubleBuffering();
}

Removing the padding around a Tab control in WinForms [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How can I remove the border padding on container controls in WinForms?
I've developed a Winforms application in Visual studio 2008. On the main form I have a Tab control. Now I am trying to use a background image to the tab page. The problem that I am running into is that the tab control seems to have a thick border around it. Also the tab control does not cover the entire form leaving a line of space over the top between the form and tab page. (I have the tab pages alignment set at bottom). So a border around the tab control and line of space at the top making my page look ugly. I tried to give the same image as the background to form, but the tab control padding playing the spoilsport.
Any ideas to make my design better would be appreciated.
I agree with the majority of comments made here. The standard TabControl is drawn very poorly on Microsoft's Part...even in Windows Vista / 7 it doesn't look great! You would be better to write your own custom implementation by inheriting the TabControl and then drawing the additional stuff you want.
You might consider using this as a template for your new control. You just need to add some cool design / drawing work to the OnPaint and OnPaintBackground methods.
namespace CustomControls
{
#region USING
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
#endregion
public class CustomTabControl : TabControl
{
#region VARIABLES
private int hotTrackTab = -1;
#endregion
#region INSTANCE CONSTRUCTORS
public CustomTabControl() : base()
{
this.InitializeComponent();
}
#endregion
#region INSTANCE METHODS
private void InitializeComponent()
{
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.DrawMode = TabDrawMode.OwnerDrawFixed;
}
private int GetTabUnderCursor()
{
Point cursor = this.PointToClient(Cursor.Position);
for (int index = 0; index < this.TabPages.Count; index++)
{
if (this.GetTabRect(index).Contains(cursor))
{
return index;
}
}
return -1;
}
private void UpdateHotTrack()
{
int hot = GetTabUnderCursor();
if (hot != this.hotTrackTab)
{
if (this.hotTrackTab != -1)
{
this.Invalidate(this.GetTabRect(this.hotTrackTab));
}
this.hotTrackTab = hot;
if (this.hotTrackTab != -1)
{
this.Invalidate(this.GetTabRect(this.hotTrackTab));
}
this.Update();
}
}
#endregion
#region OVERRIDE METHODS
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
this.UpdateHotTrack();
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
this.UpdateHotTrack();
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
this.UpdateHotTrack();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
switch (this.Alignment)
{
case TabAlignment.Bottom:
case TabAlignment.Left:
case TabAlignment.Right:
case TabAlignment.Top:
default:
throw new NotImplementedException();
}
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
base.OnPaintBackground(pevent);
}
#endregion
}
}
Bear in mind that the code above draw an absolutely blank TabControl that only displays the DisplayRectangle. Everything else including the tabs you will need to do yourself!
Additionally, to draw backgrounds for individual TabPages, you may need to override and custom implement TabPage as well, however you might be able to achieve the result you are looking for with just a custom tab control.
Check this out
http://www.codeproject.com/Articles/42046/Customized-TabControl-by-Repainting-Microsoft-s-Pa
IMHO This is better...
http://www.codeproject.com/Articles/38014/KRBTabControl
IMHO this is better still...
http://www.codeproject.com/Articles/91387/Painting-Your-Own-Tabs-Second-Edition
also look on VB Forums...I know I've seem some awesome custom tab controls on there!

Disable focus cues on a SplitContainer

How can I disable the focus cues on a SplitContainer?
I ask because I'd rather draw them myself using OnPaint in order to make it look somewhat smoother.
I tried this:
protected override bool ShowFocusCues
{
get
{
return false;
}
}
And this is my control:
public class cSplitContainer : SplitContainer
{
private bool IsDragging;
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (!IsSplitterFixed) IsDragging = true;
Invalidate();
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
if (IsDragging)
{
IsDragging = false;
IsSplitterFixed = false;
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (IsDragging)
{
IsSplitterFixed = true;
if (e.Button == MouseButtons.Left)
{
if (Orientation == Orientation.Vertical)
{
if (e.X > 0 && e.X < Width) SplitterDistance = e.X;
}
else
{
if (e.Y > 0 && e.Y < Height) SplitterDistance = e.Y;
}
}
else
{
IsDragging = false;
IsSplitterFixed = false;
}
}
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
base.OnPaint(e);
if (IsDragging)
{
e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(127, 0, 0, 0)), Orientation == Orientation.Horizontal ? new Rectangle(0, SplitterDistance, Width, SplitterWidth) : new Rectangle(SplitterDistance, 0, SplitterWidth, Height));
}
}
}
but it didn't work. I also tried some other methods mentioned before, but I'm still getting focus cues.
I don't think what you are seeing is the FocusCue so much as a floating window that is used to move the slider.
If keyboard access isn't important, you can try making it unselectable:
public class MySplit : SplitContainer {
public MySplit() {
this.SetStyle(ControlStyles.Selectable, false);
}
protected override void OnPaint(PaintEventArgs e) {
e.Graphics.Clear(Color.Red);
}
}
This prevents the SplitContainer from getting focus, but your mouse can still interact with it.
The code of SplitContainer is like:
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
if (Focused) {
DrawFocus(e.Graphics,SplitterRectangle);
}
}
DrawFocus is not virtual. So you can't override it.
Focused is virtual. Maybe you can set it to false while calling base.OnPaint(...) in your OnPaint override.
So you could add following code (I did not tested if it works):
private bool _painting;
public override bool Focused
{
get { return _painting ? false : base.Focused; }
}
protected override void OnPaint(PaintEventArgs e)
{
_painting = true;
try
{
base.OnPaint(e);
}
finally
{
_painting = false;
}
}
That is more a hack than a clean solution.
I was googling for this issue and this question came up on the top.
There is a solution and interesting discussion on a Microsoft forum regarding the splitter stealing focus for no good reason. The following comment is spot on:
The focus issue you mentioned is by design, however to get the performance you want, you can use the following workaround: ....
It may be "by design", but it is not a very good one. What spitters have you ever seen in any Microsoft production application that even temporarily take the focus from the panes they split? I also added the code you suggest, and it does keep me from permanently losing the focus to the splitter, but I still don't like the fact that my panes hide and show their selections during splitter manipulation.
This distracting selection flash just is not present in most professional applications. It is just good enough that it probably won't be worth my time to fix for a while, but not what most people really want. If you respected the TabStop property or even added a AcceptsFocus property, most people would want this off. I think you should add this option to the design in a future version.
--Brendan
Simple solution: give away focus immediately when receiving it!
Three steps:
Create a GotFocus handler for the SplitContainer
Forward the focus to another control with AnotherControl.Focus().
Set TabStop to False
That's all. The ugly focus cue is never shown.
Now, one subtlety: Which other control to give the focus to? It's up to you. Just take the first control by tab order, or a upper-left focusable control in the right pane of the SplitContainer (TextBox in the ASCII diagram below). The perfect solution would be the previous control which had focus, but sadly this is not easy to find out: Find out the control with last focus, but IMHO the upper-left focusable control is a very good response.
left pane right pane
------------------------------------------------
: :: :
: :: [TextBox] [Button] :
: :: :
: :: [Combobox V] :
: :: :
------------------------------------------------
it is a kind a similar question being asked on stackoveflow one solution is sugested as being used by you also along with overriding showfocuscues property you need to override paint method as well.

Categories