Overlay Picturebox (or image) on top of AxWindowsMediaPlayer - c#

I'm trying to overlay a picture box on top of a AxWindowsMediaPlayer control, but as soon as the video starts playing, the video is pushed to the front. Or failing that, I can get the picture box to sit on top, but it never has a transparent background. It seems to inherit the forms background color, so the video will not display beneath the picture box.
I'm using the following properties on the MediaPlayer
uiMode = "none";
enableContextMenu = false;
windowlessVideo = true;
I've tried setting the picture box's BackColor to transparent, tried setting its parent and the media players parent. I've tried adding it to the media player control, all failing.
There doesn't seem to be a solution that I've found via Google, and I'd prefer not to have to use an external library, although if that's the best solution, then I'd be ok with that.
Thank you

For anyone else looking to do this, here is the solution.
I created a Form that sits on top of the form playing the video, and set it's color transparency key to white, and set the window background to white.
After doing that, you can overlay things like picture boxes that have a Png with a partly transparent background and it will render as expected.
This is basically the final piece in the puzzle, from the solution to a similar issue posted here: Draw semi transparent overlay image all over the windows form having some controls
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MyProject
{
public partial class Form2 : Form
{
public Form2(Form formToCover)
{
InitializeComponent();
this.AllowTransparency = true;
this.TransparencyKey = Color.White;
this.BackColor = Color.White;
this.FormBorderStyle = FormBorderStyle.None;
this.ControlBox = false;
this.ShowInTaskbar = false;
this.StartPosition = FormStartPosition.Manual;
this.AutoScaleMode = AutoScaleMode.None;
this.Location = formToCover.PointToScreen(Point.Empty);
this.ClientSize = formToCover.ClientSize;
formToCover.LocationChanged += Cover_LocationChanged;
formToCover.ClientSizeChanged += Cover_ClientSizeChanged;
// Show and focus the form
this.Show(formToCover);
formToCover.Focus();
// Disable Aero transitions, the plexiglass gets too visible
if (Environment.OSVersion.Version.Major >= 6)
{
int value = 1;
DwmSetWindowAttribute(formToCover.Handle, DWMWA_TRANSITIONS_FORCEDISABLED, ref value, 4);
}
}
private void Cover_LocationChanged(object sender, EventArgs e)
{
// Ensure the plexiglass follows the owner
this.Location = this.Owner.PointToScreen(Point.Empty);
}
private void Cover_ClientSizeChanged(object sender, EventArgs e)
{
// Ensure the plexiglass keeps the owner covered
this.ClientSize = this.Owner.ClientSize;
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
// Restore owner
this.Owner.LocationChanged -= Cover_LocationChanged;
this.Owner.ClientSizeChanged -= Cover_ClientSizeChanged;
if (!this.Owner.IsDisposed && Environment.OSVersion.Version.Major >= 6)
{
int value = 1;
DwmSetWindowAttribute(this.Owner.Handle, DWMWA_TRANSITIONS_FORCEDISABLED, ref value, 4);
}
base.OnFormClosing(e);
}
protected override void OnActivated(EventArgs e)
{
// Always keep the owner activated instead
this.BeginInvoke(new Action(() => this.Owner.Activate()));
}
private const int DWMWA_TRANSITIONS_FORCEDISABLED = 3;
[DllImport("dwmapi.dll")]
private static extern int DwmSetWindowAttribute(IntPtr hWnd, int attr, ref int value, int attrLen);
}
}

Related

How to change form borders size when the form TransparencyKey is set to Color.LimeGreen?

I'm making the form1 to be transparent but i want to keep the borders and to be able to change the borders size.
The problem is when changing the form size because it's transparent the mouse loose the control over it.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Testings
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.BackColor = Color.LimeGreen;
this.TransparencyKey = Color.LimeGreen;
this.SizeChanged += Form1_SizeChanged;
}
private void Form1_SizeChanged(object sender, EventArgs e)
{
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
}
I tried in the Form1_SizeChanged to add :
this.TransparencyKey = SystemColors.Window;
this.TransparencyKey = Color.LimeGreen;
like this :
private void Form1_SizeChanged(object sender, EventArgs e)
{
this.TransparencyKey = SystemColors.Window;
this.TransparencyKey = Color.LimeGreen;
}
So it was working at the start but after few times i changed the form size it started blinking between the Window color and the LimGreen color and then lost the control again and i could not drag and change the form size again even if i see the top border but can't touch it with the mouse.
This is a screenshot of the form when it's transparent.
There are two problems :
after some time the mouse lost control over the form top border and can't touch it drag it or change it's size because it's becoming transparent too after changing the size few times.
there is only the top border for now, how can i make that also the other borders will be shown ? left,right,bottom ?
The solution is to use another color. In this case red. It seems that each color is behaving different. When setting to red you can resize the transparent form in all directions and drag it around.
This is cool. The next thing to do is to use ffmpeg to record the transparent form area. this way i can record specific parts of the desktop.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Testings
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.BackColor = Color.Red;
this.TransparencyKey = Color.Red;
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
}

How do I start C# program without the window being active?

I'm trying to create a C# program, but I don't want the window to be active when I open it. I'd like it to open in the background, and the window to show up on top of my other programs, except I want my active window to stay the same. It's because I'm using full screen programs, and I don't want my little popup to take my out of the full screen mode.
Program Use (might help in understanding what I need): I'm creating a set of macros that turn a spare mouse into a media controller. The scroll wheel controls volume, left button controls play/pause, etc. I use Spotify for music, and I want to be able to change the volume of Spotify independently from my computer's global volume. I already have this figured out using code here. I want a popup to display telling me that when I use the scroll wheel, I'm changing the volume of Spotify opposed to global volume. I want to be able to activate the macro, display the popup, change the volume as I wish, and then deactivate the macro without exiting my full screen applications. Hopefully this helps, thank you!
Program Use Edit: Here's just an explanation video, should be easier than trying to explain. To clarify, I want the program to not change activated window when it starts and to always be top most, without me having to activate it first. Thank you!!! https://streamable.com/2pewz
I'm using a program called QuickMacros to open the popup and I've tried a few different settings in there but haven't had any luck. I don't have any experience with C#, so I haven't tried anything inside C#.
My code is unrelated to the issue, but here it is just in case. All this does is give me the ability to move the popup.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace SpotifyPopup
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void label1_Click(object sender, EventArgs e)
{
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
this.Left += e.X - lastPoint.X;
this.Top += e.Y - lastPoint.Y;
}
}
Point lastPoint;
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
lastPoint = new Point(e.X, e.Y);
}
private void label1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
this.Left += e.X - lastPoint2.X;
this.Top += e.Y - lastPoint2.Y;
}
}
Point lastPoint2;
private void label1_MouseDown(object sender, MouseEventArgs e)
{
lastPoint2 = new Point(e.X, e.Y);
}
}
}
Thank you for your help!
Your question is a little bit unclear but if I am right what you want is to start your application in minimized state, to do that simply use code below in your form constructor
this.WindowState = FormWindowState.Minimized;
And when your event is fired and you want your app to be on top just use
this.TopMost = true;
this.WindowState = FormWindowState.Normal;
and for proper positioning of your form you can use this answer
Edit
Ok, now your needs are more clear, this a demo of what i think you want, in this example the form starts minimized and comes to top on mouse wheel, and then goes to background when idle, u can add more events to code and adapt it for your needs,
I used global hooks for this demo thanks to this link, so dont forget to add the proper nuget package based on the provided link.
here is the code:
using Gma.System.MouseKeyHook;
using System;
using System.Drawing;
using System.Windows.Forms;
public class Form1 : System.Windows.Forms.Form
{
private Timer timer;
private IKeyboardMouseEvents m_GlobalHook;
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
public Form1()
{
Subscribe();
timer = new Timer();
timer.Interval = 1000;
timer.Tick += Timer_Tick;
// Set up how the form should be displayed.
ClientSize = new System.Drawing.Size(292, 266);
Text = "Notify Icon Example";
WindowState = FormWindowState.Minimized;
Rectangle workingArea = Screen.GetWorkingArea(this);
Location = new Point(workingArea.Right - Size.Width - 100,
workingArea.Bottom - Size.Height - 100);
}
private void Timer_Tick(object sender, EventArgs e)
{
WindowState = FormWindowState.Minimized;
TopMost = false;
}
public void Subscribe()
{
// Note: for the application hook, use the Hook.AppEvents() instead
m_GlobalHook = Hook.GlobalEvents();
m_GlobalHook.MouseWheel += M_GlobalHook_MouseWheel;
}
private void M_GlobalHook_MouseWheel(object sender, MouseEventArgs e)
{
WindowState = FormWindowState.Normal;
TopMost = true;
timer.Stop();
timer.Start();
}
public void Unsubscribe()
{
m_GlobalHook.MouseDownExt -= M_GlobalHook_MouseWheel;
//It is recommened to dispose it
m_GlobalHook.Dispose();
}
}
Have a look here: Bring a window to the front in WPF
This thread discusses the general mechanism of presenting, activating and showing windows with WPF.

Adding scroll bar to windows forms, c#

I need to add scroll horizontal and vertical scroll bar. The problem is that they doesn't work, as in when I use them the screen doesn't move.
VScrollBar vScrollBar1 = new VScrollBar();
HScrollBar hScrollBar1 = new HScrollBar();
vScrollBar1.Dock = DockStyle.Left;
hScrollBar1.Dock = DockStyle.Bottom;
Controls.Add(vScrollBar1);
Controls.Add(hScrollBar1);
I use the code to add scroll bars, how do I activate them or get them to work as I need?
Thanks!
You usually don't add Scrollbars; you set AutoScroll = true in the form's property panel.
Now when any control grows out of the Form or is moved over right or bottom border the Form will show the necessary Scrollbar.
You can test it with a Label and a TextBox: Set the Label to the right border and script the TextBox's TextChanged event like this:
private void textBox1_TextChanged(object sender, EventArgs e)
{
label1.Text = textBox1.Text;
}
Now run the programm and enter stuff into the Textbox; you will observe how the Label grows and how the Form brings up a horizontal Scrollbar when it goes over the edge.
Note 1: This will not work if the Form has AutoSize = true - then instead the form will grow! If the Form has both AutoSize and AutoScroll true, then AutoSize will win.
Note 2: This test will only work if the Label has AutoSize = true, as it has by default..
You need to use the Panel control as container of your child controls and set "AutoScroll" property to true.
Set true to AutoScroll property of Form.
Write this code in your Form Load Event, and you will get your scroll bar, like I am writing it here in my Form Load Event.
private void Form1_Load(object sender, EventArgs e)
{
Panel my_panel = new Panel();
VScrollBar vScroller = new VScrollBar();
vScroller.Dock = DockStyle.Right;
vScroller.Width = 30;
vScroller.Height = 200;
vScroller.Name = "VScrollBar1";
my_panel.Controls.Add(vScroller);
}
vScrollbars and hScrollbars are just plain controls without code. [UI]
You need to code to make them do something!
Or just set the property 'AutoScroll = true;' in your form or add a panel and set it to true.
However your control needs Focus() to scroll with your mouse wheel.
Here is a little workaround:
public Main()
{
InitializeComponent();
//Works for panels, richtextboxes, 3rd party etc..
Application.AddMessageFilter(new ScrollableControls(panel1, richtextbox1, radScrollablePanel1.PanelContainer));
}
ScrollableControls.cs:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
//Let controls scroll without Focus();
namespace YOURNAMESPACE
{
internal struct ScrollableControls : IMessageFilter
{
private const int WmMousewheel = 0x020A;
private readonly Control[] _controls;
public ScrollableControls(params Control[] controls)
{
_controls = controls;
}
bool IMessageFilter.PreFilterMessage(ref Message m)
{
if (m.Msg != WmMousewheel) return false;
foreach (var item in _controls)
{
ScrollControl(item, ref m);
}
return false;
}
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);
private static void ScrollControl(Control control, ref Message m)
{
if (control.RectangleToScreen(control.ClientRectangle).Contains(Cursor.Position) && control.Visible)
{
SendMessage(control.Handle, m.Msg, m.WParam.ToInt32(), m.LParam.ToInt32());
}
}
}
}

How to toggle a ContextMenuStrip display using a button click but also allowing it to close normally (menu item clicks, lost focus etc)

I created a simple UserControl consisting of a Label and ContextMenuStrip. I made it function like a ComboBox but instead of a dropdown I'm displaying a ContextMenuStrip.
I have it working but there's some trickiness that I can't figure out.
I'm trying to make the label ComboButton work the same way a ComboBox does. Click the button, the dropdown appears. Click the button a second time and it'll retract. The problem is, the ContextMenu disappears on any mouse click. So when I click the button a second time to retract the menu, the menu disappears first, and then the click event fires, displaying the menu again.
I still want the Menu to disappear when a user selects a menuitem and when they just click anywhere on the form like a normal context menu does.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.Windows.Forms.VisualStyles;
using System.Diagnostics;
namespace Controls
{
public partial class CMenu : UserControl
{
ButtonState _buttonState = ButtonState.Normal;
public CMenu()
{
InitializeComponent();
}
private void lblSelect_Paint(object sender, PaintEventArgs e)
{
base.OnPaint(e);
ControlPaint.DrawComboButton(e.Graphics, getLabelRect(), _buttonState);
}
private bool IsDropdownHit(MouseEventArgs e)
{
Rectangle cursor = new Rectangle(e.X, e.Y, 1, 1);
if (e.Button == MouseButtons.Left && cursor.IntersectsWith(getLabelRect()))
{
return true;
}
return false;
}
private void lblSelect_MouseUp(object sender, MouseEventArgs e)
{
if (!IsDropdownHit(e))
return;
if (!cmsItems.Visible)
lblSelect.ContextMenuStrip = cmsItems;
cmsItems.Width = lblSelect.Width;
cmsItems.Show(lblSelect, 0, lblSelect.Height);
}
private Rectangle getLabelRect()
{
return new Rectangle(lblSelect.Width - 20, 0, 20, lblSelect.Height);
}
}
}
So, I've rewritten your mouseUp a little:
private void lblSelect_MouseUp(object sender, MouseEventArgs e)
{
if (IsDropdownHit(e) && cmsItems.Tag == null)
{
cmsItems.Width = lblSelect.Width;
cmsItems.Show(lblSelect, 0, lblSelect.Height);
cmsItems.Tag = "Shown";
}
else
{
cmsItems.Hide();
cmsItems.Tag = null;
}
}
Now, it'll close. BUT - if you dont use the button to close, you will have to click twice to open it, next time.
Workaround for this "bug":
void cmsItems_Closed(object sender, ToolStripDropDownClosedEventArgs e)
{
Point c = PointToClient(Cursor.Position);
if (!IsDropdownHit(new MouseEventArgs(MouseButtons.Left, 1, c.X - lblSelect.Location.X, c.Y - lblSelect.Location.Y, 0)))
cmsItems.Tag = null;
}
Depending on how you designed your form, you might have to adjust the MouseEventArgs-Coordinates, to successfully determine Dropdownhits. I just added the CMenu Control to an empty form.
For the workaround, don't forget to add the handler into the constructor of your CMenu.cs:
cmsItems.Closed += cmsItems_Closed;

how to stop flickering C# winforms

I have a program that is essentially like a paint application. However, my program has some flickering issues. I have the following line in my code (which should get rid of flickering - but doesn't):
this.SetStyle(ControlStyles.AllPaintingInWmPaint
| ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
my code(minus the super and sub classes for the shapes is as follows:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Paint
{
public partial class Paint : Form
{
private Point startPoint;
private Point endPoint;
private Rectangle rect = new Rectangle();
private Int32 brushThickness = 0;
private Boolean drawSPaint = false;
private List<Shapes> listOfShapes = new List<Shapes>();
private Color currentColor;
private Color currentBoarderColor;
private Boolean IsShapeRectangle = false;
private Boolean IsShapeCircle = false;
private Boolean IsShapeLine = false;
public SPaint()
{
InitializeComponent();
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
currentColor = Color.Red;
currentBoarderColor = Color.DodgerBlue;
IsShapeRectangle = true;
}
private void panelArea_Paint(object sender, PaintEventArgs e)
{
Graphics g = panelArea.CreateGraphics();
if (drawSPaint == true)
{
Pen p = new Pen(Color.Blue);
p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
if (IsShapeRectangle == true)
{
g.DrawRectangle(p, rect);
}
else if (IsShapeCircle == true)
{
g.DrawEllipse(p, rect);
}
else if (IsShapeLine == true)
{
g.DrawLine(p, startPoint, endPoint);
}
}
foreach (Shapes shape in listOfShapes)
{
shape.Draw(g);
}
}
private void panelArea_MouseDown(object sender, MouseEventArgs e)
{
startPoint.X = e.X;
startPoint.Y = e.Y;
drawSPaint = true;
}
private void panelArea_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
if (e.X > startPoint.X)
{
rect.X = startPoint.X;
rect.Width = e.X - startPoint.X;
}
else
{
rect.X = e.X;
rect.Width = startPoint.X - e.X;
}
if (e.Y > startPoint.Y)
{
rect.Y = startPoint.Y;
rect.Height = e.Y - startPoint.Y;
}
else
{
rect.Y = e.Y;
rect.Height = startPoint.Y - e.Y;
}
panelArea.Invalidate();
}
}
private void panelArea_MouseUp(object sender, MouseEventArgs e)
{
endPoint.X = e.X;
endPoint.Y = e.Y;
drawSPaint = false;
if (rect.Width > 0 && rect.Height > 0)
{
if (IsShapeRectangle == true)
{
listOfShapes.Add(new TheRectangles(rect, currentColor, currentBoarderColor, brushThickness));
}
else if (IsShapeCircle == true)
{
listOfShapes.Add(new TheCircles(rect, currentColor, currentBoarderColor, brushThickness));
}
else if (IsShapeLine == true)
{
listOfShapes.Add(new TheLines(startPoint, endPoint, currentColor, currentBoarderColor, brushThickness));
}
panelArea.Invalidate();
}
}
private void rectangleToolStripMenuItem_Click(object sender, EventArgs e)
{
IsShapeRectangle = true;
IsShapeCircle = false;
IsShapeLine = false;
}
private void ellipseToolStripMenuItem_Click(object sender, EventArgs e)
{
IsShapeRectangle = false;
IsShapeCircle = true;
IsShapeLine = false;
}
private void lineToolStripMenuItem_Click(object sender, EventArgs e)
{
IsShapeCircle = false;
IsShapeRectangle = false;
IsShapeLine = true;
}
private void ThicknessLevel0_Click(object sender, EventArgs e)
{
brushThickness = 0;
}
private void ThicknessLevel2_Click(object sender, EventArgs e)
{
brushThickness = 2;
}
private void ThicknessLevel4_Click(object sender, EventArgs e)
{
brushThickness = 4;
}
private void ThicknessLevel6_Click(object sender, EventArgs e)
{
brushThickness = 6;
}
private void ThicknessLevel8_Click(object sender, EventArgs e)
{
brushThickness = 8;
}
private void ThicknessLevel10_Click(object sender, EventArgs e)
{
brushThickness = 10;
}
private void ThicknessLevel12_Click(object sender, EventArgs e)
{
brushThickness = 12;
}
private void ThicknessLevel14_Click(object sender, EventArgs e)
{
brushThickness = 14;
}
private void FillColour_Click(object sender, EventArgs e)
{
ColorDialog fillColourDialog = new ColorDialog();
fillColourDialog.ShowDialog();
currentColor = fillColourDialog.Color;
panelArea.Invalidate();
}
private void button1_Click(object sender, EventArgs e)
{
ColorDialog fillColourDialog = new ColorDialog();
fillColourDialog.ShowDialog();
currentBoarderColor = fillColourDialog.Color;
panelArea.Invalidate();
}
}
}
How do i stop the flickering?
*UPDATE:*This code actually works great when i'm drawing directly on the form. However, when i try to draw on the panel, flickering becomes an issue
For a "cleaner solution" and in order to keep using the base Panel, you could simply use Reflection to implement the double buffering, by adding this code to the form that holds the panels in which you want to draw in
typeof(Panel).InvokeMember("DoubleBuffered",
BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
null, DrawingPanel, new object[] { true });
Where "DrawingPanel" is the name of the panel that you want to do the double buffering.
I know quite a lot of time has passed since the question was asked, but this might help somebody in the future.
Finally solved the flickering. Since I was drawing on a panel instead of the form the line of code below will not solve the flickering:
this.SetStyle(
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.DoubleBuffer,
true);
SetStyle must be of type 'YourProject.YourProject' (or derived from it) hence, you have to create a class as such (so that you can use MyPanel which will be derived from SPaint.SPaint and hence allowing you to use doublebuffering directly for the panel - rather than the form):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SPaint;
namespace YourProject
{
public class MyPanel : System.Windows.Forms.Panel
{
public MyPanel()
{
this.SetStyle(
System.Windows.Forms.ControlStyles.UserPaint |
System.Windows.Forms.ControlStyles.AllPaintingInWmPaint |
System.Windows.Forms.ControlStyles.OptimizedDoubleBuffer,
true);
}
}
}
After you've done this(although you should really never edit the designer code unless you truly know what you're doing) you'll have to edit the Form.Designer.cs. Inside this file you will find code that looks like this:
this.panelArea = new YourProject.MyPanel();
The above line needs to be changed to:
this.panelArea = new MyPanel();
After I completed these steps, my paint program no longer flickers.
For anyone else having the same issue, the problem is finally solved.
Enjoy!
Copy and paste this into your project
protected override CreateParams CreateParams
{
get
{
CreateParams handleParam = base.CreateParams;
handleParam.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
return handleParam;
}
}
This enables double-buffering for all controls from the form level down, otherwise double buffering needs to be individually enabled for each one... you may want to fine tune double bufferring after this because blanketed double buffering may give unwanted side effects.
I have had the same problem. I was never able to 100% rid myself of the flicker (see point 2), but I used this
protected override void OnPaint(PaintEventArgs e) {}
as well as
this.DoubleBuffered = true;
The main issue for flickering is making sure you
paint things it the right order!
make sure your draw function is < about 1/60th of a second
winforms invokes the OnPaint method each time the form needs to be redrawn. There are many ways it can be devalidated, including moving a mouse cursor over the form can sometimes invoke a redraw event.
And important note about OnPaint, is you don't start from scratch each time, you instead start from where you were, if you flood fill the background color, you are likely going to get flickering.
Finally your gfx object. Inside OnPaint you will need to recreate the graphics object, but ONLY if the screen size has changed. recreating the object is very expensive, and it needs to be disposed before it is recreated (garbage collection doesn't 100% handle it correctly or so says documentation). I created a class variable
protected Graphics gfx = null;
and then used it locally in OnPaint like so, but this was because I needed to use the gfx object in other locations in my class. Otherwise DO NOT DO THIS. If you are only painting in OnPaint, then please use e.Graphics!!
// clean up old graphics object
gfx.Dispose();
// recreate graphics object (dont use e.Graphics, because we need to use it
// in other functions)
gfx = this.CreateGraphics();
Hope this helps.
Double buffering is not going to be of much help here I'm afraid. I ran into this a while ago and ended up adding a separate panel in a rather clumsy way but it worked for my application.
Make the original panel that you have ( panelArea ) a transparent area, and put it on top of a 2nd panel, which you call panelDraw for example. Make sure to have panelArea in front. I whipped this up and it got rid of the flickering, but left the shape that was being drawn smeared out so it's not a full solution either.
A transparent panel can be made by overriding some paint actions from the original panel:
public class ClearPanel : Panel
{
public ClearPanel(){}
protected override CreateParams CreateParams
{
get
{
CreateParams createParams = base.CreateParams;
createParams.ExStyle |= 0x00000020;
return createParams;
}
}
protected override void OnPaintBackground(PaintEventArgs e){}
}
The idea is to handle drawing the temporary shape during the MouseMove event of the 'panelArea' and ONLY repaint the 'panelDraw' on the MouseUp Event.
// Use the panelDraw paint event to draw shapes that are done
void panelDraw_Paint(object sender, PaintEventArgs e)
{
Graphics g = panelDraw.CreateGraphics();
foreach (Rectangle shape in listOfShapes)
{
shape.Draw(g);
}
}
// Use the panelArea_paint event to update the new shape-dragging...
private void panelArea_Paint(object sender, PaintEventArgs e)
{
Graphics g = panelArea.CreateGraphics();
if (drawSETPaint == true)
{
Pen p = new Pen(Color.Blue);
p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
if (IsShapeRectangle == true)
{
g.DrawRectangle(p, rect);
}
else if (IsShapeCircle == true)
{
g.DrawEllipse(p, rect);
}
else if (IsShapeLine == true)
{
g.DrawLine(p, startPoint, endPoint);
}
}
}
private void panelArea_MouseUp(object sender, MouseEventArgs e)
{
endPoint.X = e.X;
endPoint.Y = e.Y;
drawSETPaint = false;
if (rect.Width > 0 && rect.Height > 0)
{
if (IsShapeRectangle == true)
{
listOfShapes.Add(new TheRectangles(rect, currentColor, currentBoarderColor, brushThickness));
}
else if (IsShapeCircle == true)
{
listOfShapes.Add(new TheCircles(rect, currentColor, currentBoarderColor, brushThickness));
}
else if (IsShapeLine == true)
{
listOfShapes.Add(new TheLines(startPoint, endPoint, currentColor, currentBoarderColor, brushThickness));
}
panelArea.Invalidate();
}
panelDraw.Invalidate();
}
I know this is really old question but maybe someone will find it useful.
I'd like to make little enhancement to viper's answer.
You can make simple extension to Panel class and hide setting property through reflection.
public static class MyExtensions {
public static void SetDoubleBuffered(this Panel panel) {
typeof(Panel).InvokeMember(
"DoubleBuffered",
BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty,
null,
panel,
new object[] { true });
}
}
If your Panel variable's name is myPanel you can just call
myPanel.SetDoubleBuffered();
and that's it. Code looks much cleaner.
I'd advise overriding OnPaintBackground and handling the background erase yourself. If you know you are painting the whole control you can just do nothing in OnPaintBackground (don't call the base method) and it will prevent the background colour being painted first
In this condition you have to enable double buffer .
Open current form and go to form properties and apply double buffer true;
or you can also write this code .
this.DoubleBuffered = true;
In form load.
if all of the above doesn't work you can always create your own double buffer
link to Microsofts tutorial: https://learn.microsoft.com/en-us/dotnet/framework/winforms/advanced/how-to-reduce-graphics-flicker-with-double-buffering-for-forms-and-controls
hopes it works for you
It's a bit of and old question, but just to be complete: There is yet another solution, that worked for me where the double-buffering did not.
As it turns out Microsoft offers the BufferedGraphics class as a solution. Nice thing about this class is that it enables you to copy one Graphics object to another, so except from setting up a temporary Graphics object and eventually copying it to the final destination, one can use pretty much the same code that one would have done when the flickering should not have been a problem:
private void Indicator_Paint(object sender, PaintEventArgs e)
{
Control pbIndicator = (Control)sender;
Rectangle targetRect = pbIndicator.ClientRectangle;
Image img = Bitmap.FromFile("bitmap.bmp");
BufferedGraphicsContext ctx = new BufferedGraphicsContext();
BufferedGraphics bg = ctx.Allocate(e.Graphics, targetRect);
// Do the graphic stuff
bg.Graphics.Clear(this.BackColor);
bg.Graphics.DrawImage(img, 0, 0);
// etcetera
bg.Render(e.Graphics);
bg.Dispose();
ctx.Dispose();
}
Downside of this solution that it might clutter your code. Furthermore I'm not sure whether it is a good idea to setup the context each time, or whether it would suffice to create one in advance and keep using that context.
For more information see https://learn.microsoft.com/en-us/dotnet/api/system.drawing.bufferedgraphicscontext?view=dotnet-plat-ext-3.1
here is the program of moving circle in .net, that doesn't flicker.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;
namespace CircleMove
{
/// <summary>
/// Description of MainForm.
/// </summary>
public partial class MainForm : Form
{
int x=0,y=0;
Thread t;
public MainForm()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
//
// TODO: Add constructor code after the InitializeComponent() call.
//
}
void MainFormPaint(object sender, PaintEventArgs e)
{
Graphics g=e.Graphics;
Pen p=new Pen(Color.Orange);
Brush b=new SolidBrush(Color.Red);
// g.FillRectangle(b,0,0,100,100);
g.FillEllipse(b,x,y,100,100);
}
void MainFormLoad(object sender, EventArgs e)
{
t=new Thread( new ThreadStart(
()=>{
while(true)
{
Thread.Sleep(10);
x++;y++;
this.Invoke(new Action(
()=>{
this.Refresh();
this.Invalidate();
this.DoubleBuffered=true;
}
)
);
}
}
)
);
t.Start();
}
}
}
If memory is tight (so you don't want the memory cost of double-buffering), one possible way to REDUCE, though not eliminate, flicker, is to set background color to the dominant color in your current scene.
Why this helps: flicker is a momentary flash of the background color, which the OS draws before drawing child controls or your custom drawing code. If that flash is a color that is closer to the final color to be displayed, it will be less noticeable.
If you are not sure what color to start with, start with 50% gray, because this is an average of black and white, so will be closer to most colors in your scene.
myFormOrControl.BackColor = Color.Gray;
Try to insert drawing logic in current form's
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
}
method. In this case you should use parameter e to get Graphics object. Use e.Graphics property. Then you should invoke Invalidate() method for this form whenever form must be redrawn.
PS: DoubleBuffered must be set to true.
Drawing onto a Label instead of a Panel, solved the problem for me.
No need to use DoubleBuffering or anything either.
You can remove the text from the label, set AutoSize to false, then Dock it or set the Size and use it as for the Panel.
Best wishes,
Quite late into the game... but for me WS_EX_COMPOSITED worked fine for a while and after some time of application start, layout started behaving very weird. I use TabControl and some TabPages on addition/removal started displaying titles of its siblings, so be careful with using it. Without WS_EX_COMPOSITED all worked fine, but I still had the flickering issue on new TabPage add.
None solution here really worked for me so I started looking at my code. I have a functionality where I create RichTextBox, then this RichTextBox is added to newly created TabPage and finally TabPage is added to TabControl. I decided to change order of that action - firstly I create TabPage, then add it to TabControl, and when TabPage is already added to TabControl, I add RichTextBox to it. When I did that, no more flickering and all works perfectly without any 'hacks'.
The moral of the story is that if nothing really works for you, try to (if possible and applicable in your case) play around with order in which you add controls.
Can you try using a timer and boolean to check if mouse is down, and paint in that spot, using a variable again check if user has moved his mouse, if moved paint that spot too etc.
Or just check if mouse down(via boolean that sets true when mouse is down) using a timer and paint it considering you are probably trying to just paint one pixel, not like you have shadow etc. Instead of using actual mousedown. So you check every 1 second instead of 0.0001 and it wont flicker. Or vice-versa, try it with your own times.
just do this.Refresh() when shown the form.

Categories