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;
}
}
Related
I've made a Windows Forms solution. In the main shell, there is added a MenuStrip, and it's possible to add more Views onto it.
The problem is, that when I add/open a new View, it is opened behind the MenuStrip.
Somehow, I want the MenuStrip to have a border, so it is not possible to drag things behind it, but I have no idea how.
The same case should be with other Views.
You should set the Dock property for the control that you want to add.
OK, I have a solution - I don't totally like it but it works! You will need the usual MDI suspects in terms of flags, etc.
The main form that is the MDI container needs to have something like:
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
int BodyCount = 0;
private void fileToolStripMenuItem_Click(object sender, EventArgs e)
{
MDIChildForm child = new MDIChildForm();
child.TitleText = String.Format("Child window {0}", ++BodyCount);
child.MdiParent = this;
child.Show();
}
/*
** This could be fun - shouldn't recurse!
*/
public void ShifTheChild(MDIChildForm spoiltBrat)
{
var m = menuStrip1.Height;
if (spoiltBrat.Location.Y < m)
spoiltBrat.Location = new Point(spoiltBrat.Location.X, 0);
return;
}
}
The child forms need the location changed event hooking:
public partial class MDIChildForm : Form
{
public String TitleText
{
get { return this.Text; }
set { this.Text = value; }
}
MainForm parent = null;
public MDIChildForm()
{
InitializeComponent();
this.ShowIcon = false;
}
private void MDIChildForm_LocationChanged(object sender, EventArgs e)
{
if (parent != null)
parent.ShifTheChild(this);
}
private void MDIChildForm_Load(object sender, EventArgs e)
{
parent = this.MdiParent as MainForm;
}
}
When you move a child into the twilight zone under the menu it will be snapped back out - the method that moves it will cause the event to fire again but the second time nothing should happen (so no recursion).
I don't like this solution simply because I can't get my brain around whether there is a condition that would make it recurse, and I don't like uncertainty.
Good luck.
I needed functionality that doesn't exist in the standard ComboBox, so I wrote my own from a TextBox and a form. When the user types in the TextBox, it shows a dropdown as a separate form.
Here's some of the relevant code:
internal class FilteredDropDown : Form
{
public Control OwnerControl { get; set; }
public bool CloseOnLostFocus { get; set; }
protected override OnLostFocus(EventArgs e)
{
if (CloseOnLostFocus && !OwnerControl.IsFocused)
this.Close();
}
protected override OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e)
// highlight the moused over item in the list
}
...
}
public class FilteredCombo : TextBox
{
private FilteredDropDown dropDown;
public FilteredCombo()
{
dropDown = new FilteredDropDown();
dropDown.OwnerControl = this;
}
public void ShowDropDown()
{
if (dropDown.Visible)
return;
dropDown.RefreshFilter();
var loc = PointToScreen(new Point(0, this.Height));
dropDown.Location = loc;
dropDown.CloseOnLostFocus = false;
int selectionStart = this.SelectionStart;
int selectionLength = this.SelectionLength;
dropDown.Show(this);
this.Focus();
this.SelectionStart = selectionStart;
this.SelectionLength = selectionLength;
dropDown.CloseOnLostFocus = false;
}
protected override OnLostFocus(EventArgs e)
{
if (dropDown.Visible && !dropDown.ContainsFocus())
dropDown.Close();
}
protected override OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
ShowDropDown();
}
...
}
There's obviously a whole lot more code than that to deal with all kinds of stuff irrelevent to my question.
The problem is when I put the FilteredCombo on a modal dialog. Somehow the FilteredDropDown form doesn't receive mouse events at all when it is parented by a modal dialog.
I've read something about WinForms filtering out events on all except the current modal dialog, I suspect that is what's going on, but I have no ideas of how to fix it. Is there some way to get the mouse up/down/move/click/etc. events to work when parented by a model dialog?
I had to go digging through the ShowDialog source code, and I found that it calls user32.dll EnableWindow(Handle, false) on all the windows except the shown one. The problem was that the FilteredDropDown already existed by the time the ShowDialog() method got called. I discovered two different ways to fix this:
Don't allow the DropDown to be shown until the parent form is shown. This is a bit trickier to guarantee, so I also implemented the second way.
Re-enable the DropDown window when it is made visible:
[DllImport("user32.dll")]
private static extern bool EnableWindow(IntPtr hWnd, bool enable);
protected override void OnVisibleChanged(EventArg e)
{
base.OnVisibleChanged(e);
if (this.Visible)
{
EnableWindow(this.Handle, true);
}
}
Why tooltip, displayed manually with ToolTip.Show, is not shown, when window, containing control, is inactive?
public class MyControl : Button
{
private _tip;
public string ToolTip
{
get { return _tip; }
set { _tip = value; }
}
private ToolTip _toolTip = new ToolTip();
public MyControl()
{
_toolTip.UseAnimation = false;
_toolTip.UseFading = false;
_toolTip.ShowAlways = true;
}
protected override void OnMouseHover(EventArgs e)
{
_toolTip.Show(_tip, this, 0, Height);
base.OnMouseHover(e);
}
protected override void OnMouseLeave(EventArgs e)
{
_toolTip.Hide(this);
base.OnMouseLeave(e);
}
}
I went for ToolTip.Show because I must have tooltip onscreen for unlimited time, which is not possible with normal ToolTip. I also love the idea of having tooltip text as a part of control itself. But unfortunately, when showing tooltip this way for inactive window (despite ShowAlways = true), it simply doesn't work.
The OnMouseHower event is rised, but _toolTip.Show does nothing.. unless window is activated, then everything works.
Bounty
Adding bounty for a solution to display tooltip for an inactive form (preferably with solution when tooltip text is a property of control, not IContainer).
There is a private method that does what you want, so to access it, you would have to use reflection to call it:
using System.Reflection;
public class MyControl : Button {
private ToolTip toolTip = new ToolTip() {
UseAnimation = false,
UseFading = false
};
public string ToolTip { get; set; }
protected override void OnMouseHover(EventArgs e) {
base.OnMouseHover(e);
Point mouse = MousePosition;
mouse.Offset(10, 10);
MethodInfo m = toolTip.GetType().GetMethod("SetTool",
BindingFlags.Instance | BindingFlags.NonPublic);
m.Invoke(toolTip, new object[] { this, this.ToolTip, 2, mouse });
}
protected override void OnMouseLeave(EventArgs e) {
base.OnMouseLeave(e);
toolTip.Hide(this);
}
}
The tip will display on an inactive window and it will stay on the screen indefinitely until the mouse moves off the control.
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 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.