I want to paint a plot diagram in a panel with 600 points per each 100 milliseconds. When I do it using Graphics object and simply draw an ellipse, the screen flashes! How can I draw such a diagram efficiently and without flashing?!
An easy way to stop this is to turn double buffering on. Your form has a double buffered property which you can set to true.
Or sometimes you can do it on the control if it supports it.
e.g.
class MyForm : Form
{
public MyForm()
{
InitializeComponent();
this.DoubleBuffered = true;
}
}
The panel's double buffering needs to be turned on via inheritance:
public class BufferedPanel : Panel {
public BufferedPanel() {
this.DoubleBuffered = true;
this.ResizeRedraw = true;
}
}
Then make sure you use the actual paint event of the control:
public Form1() {
InitializeComponent();
bufferedPanel1.Paint += bufferedPanel1_Paint;
}
private void bufferedPanel1_Paint(object sender, PaintEventArgs e) {
e.Graphics.DrawSomething(...);
}
Avoid using CreateGraphics() as that is only a temporary drawing.
Related
I'm developing a Windows Desktop Application using C# in VS 2022 on Windows 10. I'm developing for a touch screen and want the UI to be very intuitive and give good feedback because the user(s) will likely be tech-averse. On several of the forms I'm using a PictureBox as a button because I like the visual effects better. I can get a nice "button pressed" effect by using the MouseDown and MouseUp events to change the border style of the PictureBox to Fixed3D (on mouse down) and back to None (on mouse up). The only issue is that the PictureBox image "blinks" when I do this, like the control is clearing the image out and reloading it or something.
My code is rather trivial, but I'll post it here anyway just in case:
private void Button_Down(object sender, MouseEventArgs e)
{
PictureBox pb = (PictureBox)sender;
pb.BorderStyle = BorderStyle.Fixed3D;
}
private void Button_Up(object sender, MouseEventArgs e)
{
PictureBox pb = (PictureBox)sender;
pb.BorderStyle = BorderStyle.None;
}
If you're open to solving your blinking problem another way, consider this reusable CustomButton class that lets you use your own custom image to depict the 3D pressed state. The icons are superimposed using the Text property and a custom font containing glyphs (making it easy to change their size and color). When the button is not pressed, system theme takes over or you could unset the UseVisualStyleBackColor property to additionally customize things like OnMouseHover.
CustomButton inherits from Buttonand has a PrivateFontCollection giving it access to a .ttf file containing glyphs. This particular flashlight-filter-history-favorite-search.ttf is one I designed for my own project using the Fontello open-source icon font generator.
public CustomButton()
{
UseCompatibleTextRendering = true;
TextAlign = ContentAlignment.MiddleCenter;
refCount++;
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if (!DesignMode) initFont();
}
private void initFont()
{
if (privateFontCollection == null)
{
privateFontCollection = new PrivateFontCollection();
var path = Path.Combine(Path.GetDirectoryName(
Assembly.GetEntryAssembly().Location),
"Fonts",
"flashlight-filter-history-favorite-search.ttf");
privateFontCollection.AddFontFile(path);
var fontFamily = privateFontCollection.Families[0];
GlyphFontUp = new Font(fontFamily, 16F);
GlyphFontDown = new Font(fontFamily, 15F);
}
Font = GlyphFontUp;
ForeColor = GlyphColorUp;
}
PrivateFontCollection privateFontCollection = null;
public static Font GlyphFontUp { get; private set; } = null;
public static Font GlyphFontDown { get; private set; } = null;
public static Color GlyphColorUp { get; } = Color.Teal;
public static Color GlyphColorDown { get; } = Color.DarkCyan;
private static int refCount = 0;
protected override void Dispose(bool disposing)
{
if (disposing)
{
refCount--;
if (refCount == 0)
{
GlyphFontUp?.Dispose();
privateFontCollection?.Dispose();
}
}
base.Dispose(disposing);
}
When the mouse is down the button has an image, the icon is smaller, and the icon color changes. When the mouse comes up the image is removed and everything goes back to normal.
partial class CustomButton : Button
{
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
Image = new Bitmap(Resources.buttonDown, Size);
Font = GlyphFontDown;
ForeColor = GlyphColorDown;
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
Font = GlyphFontUp;
ForeColor = GlyphColorUp;
Image = null;
}
}
The buttonDown image is just something I drew myself. I'm pretty sure you could do better!
The CustomButton class can be swapped out in the MainForm.Designer.cs file.
// private System.Windows.Forms.Button customButton0;
private intuitive_buttons.CustomButton customButton0;
This code assigns the various icons to the buttons:
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
// Assign the icons to the buttons
customButton0.Text = "\uE800";
customButton1.Text = "\uE801";
customButton2.Text = "\uE802";
customButton3.Text = "\uE803";
customButton4.Text = "\uE804";
}
}
Hope this at least gives you a few ideas to try.
I've made a class GradientButton which suppose to be a Button which is filled with gradient background.
I draw gradient filling in the OnPaintBackground() method. Unfortunately it is never invoked, of course I added a GradientButton to a Form via toolbox:
public class GradientButton : Button {
public Color Color1 { get; set; }
public Color Color2 { get; set; }
public float Angle { get; set; }
public GradientButton() {
Color1 = Color.YellowGreen;
Color2 = Color.LightGreen;
Angle = 30;
}
protected override void OnPaintBackground(PaintEventArgs e) {
base.OnPaintBackground(e);
Debug.WriteLine("This never prints");
using (LinearGradientBrush brush = new LinearGradientBrush(this.ClientRectangle,
Color1,
Color2,
Angle)) {
e.Graphics.FillRectangle(brush, this.ClientRectangle);
}
}
protected override void OnResize(EventArgs e) {
base.OnResize(e);
Invalidate();
}
}
Question: How fill the button's background with the gradient? Why OnPaintBackground is not invoked? As far as I know it should be calledbefore OnPaint method.
This is because the Button class has ControlStyles.Opaque flag set, which according to the documentation:
If true, the control is drawn opaque and the background is not painted.
You can turn it off in your class constructor
SetStyle(ControlStyles.Opaque, false);
and your OnPaintBackground override will be invoked.
However, it would not help a lot - there is a reason the flag to be set to true - the OnPaint draws both background and face of the button, so whatever you do in OnPaintBackground will not have any affect of the button appearance. Unfortunately there is no option to paint just the background, so you need to override the OnPaint and actually draw everything yourself.
You need to set the style of the form in the constructor ...
this.SetStyle(ControlStyles.UserPaint, true);
to ensure the OnPaint method is overridden. There are many settings for the ControlStyle which you can combine
I would do this instead.
Firstly, change your constructor to this:
public GradientButton()
{
Color1 = Color.YellowGreen;
Color2 = Color.LightGreen;
Angle = 30;
Paint += new PaintEventHandler(GradientButton_Paint);
}
And then add the below procedure:
private void GradientButton_Paint(object sender,PaintEventArgs e)
{
Debug.WriteLine("This never prints");
using (LinearGradientBrush brush = new LinearGradientBrush(this.ClientRectangle,Color1,Color2,Angle))
{
e.Graphics.FillRectangle(brush, this.ClientRectangle);
}
}
I'm not entirely sure why your code doesn't work, but the way I've described always works for me. Hope that's good enough.
I've a Panel which fills the parent Form.
And I used a Timer to capture screen ,
and set the screenshot as background image of Panel periodically.
However, it runs into crazy flickering. What can I do to solve it?
//Part of code
public partial class Form1 : Form
{
DxScreenCapture sc = new DxScreenCapture();
public Form1()
{
InitializeComponent();
panelMain.BackgroundImageLayout = ImageLayout.Zoom;
}
private void Form1_Load(object sender, EventArgs e)
{
}
void RefreshScreen()
{
Surface s = sc.CaptureScreen();
DataStream ds = Surface.ToStream(s, ImageFileFormat.Bmp);
panelMain.BackgroundImage = Image.FromStream(ds);
s.Dispose();
}
private void timer1_Tick(object sender, EventArgs e)
{
RefreshScreen();
}
}
Try using a double buffered panel. Inherit panel, set DoubleBuffered to true and use that panel instead of default panel:
namespace XXX
{
/// <summary>
/// A panel which redraws its surface using a secondary buffer to reduce or prevent flicker.
/// </summary>
public class PanelDoubleBuffered : System.Windows.Forms.Panel
{
public PanelDoubleBuffered()
: base()
{
this.DoubleBuffered = true;
}
}
}
EDIT
Additionally I want to encourage you to take care a little more about the resources you use. Whenever an object implements the IDisposable interface - dispose the object when not needed any more. This is very important when dealing with unmanaged resources, such as streams!
void RefreshScreen()
{
using (Surface s = sc.CaptureScreen())
{
using (DataStream ds = Surface.ToStream(s, ImageFileFormat.Bmp))
{
Image oldBgImage = panelMain.BackgroundImage;
panelMain.BackgroundImage = Image.FromStream(ds);
if (oldBgImage != null)
oldBgImage.Dispose();
}
}
}
There is actually an easier solution in Visual Studio that requires no code!
If you go to Solution Explorer and then double click on your form (Form1) there will be a list that pops up (If it does not pop up you just have to right click on your form and go to Properties and double click again). Then, go to DoubleBuffered and change it to True.
I found the answer by myself from other site. It sets some ControlStyles on the panel like the following code. And no flickering any more.
class SomePanel : Panel
{
public SomePanel()
{
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.UserPaint, true);
}
}
This is worked for me , Try this
protected override CreateParams CreateParams
{
get
{
CreateParams handleParms = base.CreateParams;
handleParms.ExStyle |= 0x02000000;
return handleParms;
}
}
I created a custom form (FormBorderStyle = FormBorderStyle.None).
I draw my own caption bar at the top with my own custom caption buttons (Close, Maximize ...).
Now my only problem is adding normal user controls to that form. If I give these controls a location, the locations are relative to the form's top (including the caption bar).
I override the default ClientSize & ClientRectangle using the 'new' keyword, which allows me to adjust it (thus removing the caption bar out of it).
This does not seem to work and I haven't been able to figure out how to do this properly without 'hacking' the ControlAdded event (which is still buggy).
protected override void OnControlAdded(ControlEventArgs e)
{
base.OnControlAdded(e);
if (e.Control.GetType() != typeof(VlufiCaptionButton /* Caption buttons: close, minimize & maximize, should not be included */))
{
e.Control.Location = new Point(e.Control.Location.X + ClientRectangle.X, e.Control.Location.Y + ClientRectangle.Y);
e.Control.LocationChanged += Control_LocationChanged;
}
}
private void Control_LocationChanged(object sender, EventArgs e)
{
if (!childControlLocationChangedHandled)
{
System.Diagnostics.Debug.WriteLine("changing");
Control cControl = (Control)sender;
childControlLocationChangedHandled = true;
cControl.Location = new Point(cControl.Location.X + ClientRectangle.X, cControl.Location.Y + ClientRectangle.Y);
}
else
childControlLocationChangedHandled = false;
}
This is the code I currently use, but it's superbuggy & I'm still having other problems with my customly drawn border.
Does anybody know how I should correctly handle this ?
I found a decent solution: I added a ContainerControl to the form & I position & size this according to the form, then whenever adding a control to the form, it should be added to the ContainerControl. Still not a proper solution, but it's the best one so far.
I'd still appreciate if someone came up with another solution.
read comments for detail:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace WindowsApplication1
{
public partial class Form1 : Form
{
int dy = 0;
public Form1()
{
InitializeComponent();
//i add a panel to top form
//( for simulating your caption bar) and get its height
dy = panel1.Height; //for yours its your caption bar height
}
private void button1_Click(object sender, EventArgs e)
{
//adding button control between form top and panel end area
//( simulate in your caption bar )
Button btn = new Button();
btn.Location = new Point(panel1.Location.X+40,panel1.Location.Y+10);
btn.Text = "Salam";
this.Controls.Add(btn);
}
//in control added event i add dy ( height of ignored area) to control Location
private void Form1_ControlAdded(object sender, ControlEventArgs e)
{
e.Control.Location = new Point(e.Control.Location.X, e.Control.Location.Y + dy);
}
private void button2_Click(object sender, EventArgs e)
{
this.Close();
}
}
}
Ok after all, I have finally found a working and pretty nice solution.
What I did is override the Controls property of my custom Form, using my own custom ControlCollection.
So this is what I got in my custom form:
public Control.ControlCollection RealControls
{
get
{
return base.Controls;
}
}
public new CustomControlCollection Controls { get; private set; }
public ContainerControl ControlContainer { get; set; }
And this is the custom ControlCollection:
public class CustomControlCollection
{
public VlufiForm Owner { get; private set; }
public CustomControlCollection (VlufiForm pOwner)
{
Owner = pOwner;
}
public void Add(Control c)
{
Add(c, false);
}
public int Count
{
get
{
return Owner.ControlContainer.Controls.Count;
}
}
public Control this[int index]
{
get
{
return Owner.ControlContainer.Controls[index];
}
}
public void Add(Control c, bool isUsable)
{
if (isUsable)
Owner.RealControls.Add(c);
else
Owner.ControlContainer.Controls.Add(c);
}
public void SetChildIndex(Control c, int nIndex)
{
Owner.ControlContainer.Controls.SetChildIndex(c, nIndex);
}
}
This is just an example custom control collection, you could add more methods in it (thus kind of inheriting ControlCollection more).
I haven't found any bugs in this system yet, it works perfectly at the moment.
EDIT: found a bug, if you dock a control in Visual Studio's Designer Mode, it will dock in the whole form, this doesn't appear when running though.
I am new to windows application. with C# and .NET.
I want something like the image i have attach. when my popup gets open, i want the mdi parent page to be shadowed or disbaled. (like we do in web application or Jquery popup)
Is it possible to do? if yes how can i do that.
Please help.
You can Achive this by making use of the opacity property of the Windows.Form
For this create a new form, sets its opacity (for eg: .75) and show this over the parent for when ever you are showing a child window.
An Example is given Below
There are three windows used here
1. ParentForm
2. OverlayForm
3. ChildForm
1. Parent Form
1. Create an instance of the Child form
2. Create an Instance of the Overlayform, Pass the objects Instances of Child and Parent(current form) as a parameter to the Constructor
3. Then Show the OverLay Form by using ShowDialog Method.
Code:
public partial class ParentForm : Form
{
public ParentForm()
{
InitializeComponent();
}
private void ParentForm_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
ChildForm child1 = new ChildForm();
// Create a new form.
OverlayForm form2 = new OverlayForm(this, child1);
child1.OverLay = form2;
// Display the form as a modal dialog box.
form2.ShowDialog(this);
}
}
2. Overlay Form
1. In the constructor store the childForm and ParentForm object in a local variables.
And Set the The properties (like width,height) to the Overlay Window
2. In the OverlayForm_Load show the ChildForm window.
public partial class OverlayForm : Form
{
public Form ParentForm { get; set; }
public Form child { get; set; }
public OverlayForm(Form parent, Form child)
{
InitializeComponent();
this.child = child;
this.ParentForm = parent;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.ShowInTaskbar = false;
this.Width = ParentForm.Width;
this.Height = ParentForm.Height;
this.Top = ParentForm.Top;
this.Left = ParentForm.Left;
this.StartPosition = ParentForm.StartPosition;
// Set the opacity to 75%.
this.Opacity = .75;
}
private void OverlayForm_Load(object sender, EventArgs e)
{
child.Show();
child.TopMost = true;
child.Focus();
child.BringToFront();
}
}
This will give the parent form a blur look. We should also write some code to close the overlay in the Child Form
3. Child Form
1. Set the object of the Overlay to a property in Child Window
2. And in the Form_Closing event of the child window, close the Overlay window.
public partial class ChildForm : Form
{
//This is set in the Parent form where the child form instatce is created
public Form OverLay { get; set; }
public ChildForm()
{
InitializeComponent();
}
private void ChildForm_Load(object sender, EventArgs e)
{
}
private void ChildForm_FormClosing(object sender, FormClosingEventArgs e)
{
this.OverLay.Close();
}
}
With Microsoft Windows there isn't really a concept of child windows. Pop-ups, or modal / modeless dialogues are just like any other windows in that they can be positioned anywhere on screen, so can be outside of the bounds of what you are thinking of as the parent window.
There are some web concepts that just do not work well on the desktop!
it works fine for me.
But after open child fucus should be on child form first button.
I have found most elegant solution on this link, it even has animation (although, I removed that part for my purpose). Author is "TommyCarlier", here is definition:
class FadingOverlayForm : Form
{
readonly double fFinalOpacity;
public FadingOverlayForm(Form owner, double finalOpacity)
{
StartPosition = FormStartPosition.Manual;
FormBorderStyle = FormBorderStyle.None;
ShowInTaskbar = false;
Owner = owner;
BackColor = Color.FromArgb(235, 245, 255); // or pick your own color
fFinalOpacity = finalOpacity;
if (fFinalOpacity < 0.01) fFinalOpacity = 0.01;
else if (fFinalOpacity > 1.0) fFinalOpacity = 1.0;
}
public void FadeIn(TimeSpan duration)
{
Opacity = 0.01;
Rectangle lWorkingArea = CalculateTotalScreenBounds();
Bounds = new Rectangle(lWorkingArea.X - 150, lWorkingArea.Y - 150, 100, 100);
Show();
Bounds = new Rectangle(Owner.PointToScreen(Point.Empty), Owner.ClientSize);
Animator.Animate(this, "Opacity", 0.01, fFinalOpacity, duration);
}
public void FadeOut(TimeSpan duration)
{
Animator.Animate(this, "Opacity", Opacity, 0, duration, EndFadeOut);
}
void EndFadeOut()
{
Form lOwner = Owner;
Dispose();
if (lOwner != null && !lOwner.IsDisposed)
ActivateFirstOwnedForm(lOwner);
}
static void ActivateFirstOwnedForm(Form form)
{
foreach(Form lOwnedForm in form.OwnedForms)
if (!lOwnedForm.IsDisposed)
{
ActivateFirstOwnedForm(lOwnedForm);
return;
}
form.Activate();
}
static Rectangle CalculateTotalScreenBounds()
{
Rectangle lBounds = Rectangle.Empty;
foreach(Screen lScreen in Screen.AllScreens)
lBounds = Rectangle.Union(lBounds, lScreen.Bounds);
return lBounds;
}
}
And here is how to use it:
DialogResult ShowMyDialog(Form owner)
{
using(MyDialog lDialog = new MyDialog())
{
FadingOverlayForm lOverlay = new FadingOverlayForm(owner, 0.6);
lDialog.Owner = lOverlay;
lOverlay.FadeIn(TimeSpan.FromSeconds(0.7));
DialogResult lResult = lDialog.ShowDialog(lOverlay);
lOverlay.FadeOut(TimeSpan.FromSeconds(0.1));
return lResult;
}
}