The following code produces a black flicker on the screen right before the form is displayed (transparently), I'm wondering what my options are for removing that flicker?
Form f = new Form();
f.BackColor = Color.Lime;
f.TransparencyKey = f.BackColor;
f.StartPosition = FormStartPosition.Manual;
f.Bounds = Screen.PrimaryScreen.WorkingArea;
f.Show();
I get the same results if I create a new project, set the background of the form to Lime and the TransparencyKey to Lime, then click Run.
Things I've tried:
Set Opacity to 99% -- same flicker
Force WS_EX_COMPOSITED in OnCreateParams or using SetWindowLong -- same flicker
Show the window smaller, or 0 width, or off screen, then move to desired location -- causes bad display issues where the windows behind my form do not redraw correctly.
Setting ControlStyles.Opaque, ControlStyles.UserPaint, and several other ControlStyles combos and overriding different paint/background-paint events -- various results, either same flicker, worse flicker, or form not transparent.
Moving to WPF might be an option, but not really looking for "use WPF" as an answer.
Set the Opacity to 0.01.
If you need the form (or parts of it) visible - then re-set the Opacity once the form creation is complete:
Form f = new Form { Opacity = 0.01 };
f.Show();
f.BeginInvoke( new Action(() => f.Opacity = 0.99 ));
EDIT: Updated cleaner as Tergiver suggested
Related
I have a WPF application that takes a screenshot of the contents of a WindowsFormsHost control and applies it to the background of a Grid control. The Grid acts as a transition screen from the WindowsFormsHost to other WPF controls. This is done to make a smooth transition effect and to avoid air space issues. I first capture the WinFormsHost control image as a bitmap and apply it to the Grid background. Then programmatically change the visibility of the transition Grid to visible. Then do an opacity animation to smoothly show another control. It works perfectly about 70 to 90 percent of the time depending on what computer I test the application on. The problem is that the transition Grid background is not being rendered fast enough or at the correct time. So that occasionally I am seeing a screenshot from a previous transition which does not match the current screen image at transition time.
If I could somehow ensure that the transition image was drawn before the Grid is made visible, it would work everytime. I just cannot see how to do this. The application seems to always wait until the last minute to do any rendering.
I have tried to force rendering with Dispatcher.Invoke() and Dispatcher.BeginInvoke() methods. I have also tried to delay the time when the Grid is made visible with a Dispatcher Timer, but no matter how much time there is between the call setting the background image and setting the visibility, the Grid does not always update. I have also tried things like InvalidateVisual() with no luck.
After looking at many examples into problems involving WPF's rending and UI threads, I made several attempts to correct the problem...
Attempt 1 - Works poorly, but attempt works better with a line of code that first sets background to nothing or black:
TransitionScreen.Visibility = Visibility.Visible
TransitionScreen.Background = Nothing
TransitionScreen.Background = New ImageBrush(BitmapToImageSource(GrabScreenshot(ScreenWidth, ScreenHeight)))
State = "WPFControlMode"
SwitchState()
Attempt 2 - Best results so far. Works fair to well depending on computer. Increasing the counter value does not improve results. The appearance of the TransitionScreen will be delayed with a larger counter value, but will have the same chance of showing a previous screenshot (about 1 in 5 times on my laptop):
Case "Transition"
'Grab Screen Shot and apply to transition screen...'
TransitionScreen.Visibility = Visibility.Visible
TransitionScreen.Background = Nothing
TransitionScreen.Background = New ImageBrush(BitmapToImageSource(GrabScreenshot(ScreenWidth, ScreenHeight)))
End Select
'The following code is inside a loop...'
If State = "Transition" Then
TransitionCounter += 1
If TransitionCounter = 25 Then
TransitionCounter = 0
TransitionScreen.Visibility = Visibility.Visible
TransitionScreen.Background = Nothing
TransitionScreen.Background = New ImageBrush(BitmapToImageSource(GrabScreenshot(ScreenWidth, ScreenHeight)))
State = "WPFControlMode"
SwitchState()
End If
End If
Attempt 3, 4, 5, ... - Attempts try to force rendering of control by working through Dispatcher. These attempts do not appear to help and often make the problem worse. They have been used in combination with both attempts 1 and 2.
Case "Transition"
'Grab Screen Shot and apply to transition screen...'
TransitionScreen.Visibility = Visibility.Visible
TransitionScreen.Background = Nothing
TransitionScreen.Background = New ImageBrush(BitmapToImageSource(GrabScreenshot(ScreenWidth, ScreenHeight)))
FlushWindowsMessageQueue()
End Select
'Tried many variations of the following procedure. Every dispatcher priority with both BeginInvoke and Invoke methods, as well as calling the methods through Application and TransitionScreen objects.'
Private Sub FlushWindowsMessageQueue()
Application.Current.Dispatcher.Invoke( _
New Action(AddressOf DummySub), _
DispatcherPriority.Background, _
New Object() {})
End Sub
Private Sub DummySub()
End Sub
There were more attempts also, but this should give you an idea of what I have tried. This is a real puzzle. This is the last major problem with a fairly involved project, and I am pretty much invested in the transition screen at this point. If you can think of anything, I will greatly appreciate it. Thanks.
I did come up with a work around for this problem. I do a transition animation before I take the screenshot. The transition animation results in a lower resolution image than the control would normally show. Then I take the screenshot of the lower resolution image which can be applied to the transition grid control, after it is made visible, fast enough that there is never a flicker. Then when I want to return to the WinFormsHost control, I run through this process in reverse. My application now works without flicker and run smoothly on most computers I have tried. On some computers with large monitors, there is sometimes a slight delay when rendering the transition screen, but still no flicker. The delay is about half a second, and I assume it has to do with the larger area that transition image must be rendered to.
Also, the above was done in combination with method/attempt #2. The flicker still occurs when in combination with attempt #1.
I have a WinForm, in which I am hiding all borders and the Control Box. Inside the WinForm I have a RECT() (not a WinForms control) the RECT fills the entire WinForm.
I am trying to add a Label to the WinForm, but I want the label to appear on top of the RECT. The Label appears on the WinForm, but never on top of the RECT. I've tried using the following:
/*App Runs - Label does not show up*/
_label.Text = "This is the label";
_label.BackColor = Color.Cornsilk;
_form.Controls.Add(_label);
_form.Controls.SetChildIndex(_label, 0);
/*App Does Not Run*/
_label.Text = "This is the label";
_label.BackColor = Color.Cornsilk;
_form.Controls.SetChildIndex(_label, 0); //trying to set the index before I add the label to the form
_form.Controls.Add(_label);
/*App Runs - Label does not show up*/
_label.Text = "This is the label";
_label.BackColor = Color.Cornsilk;
_label.BringToFront();
_form.Controls.Add(_label);
/*App Runs - Label does not show up*/
_label.Text = "This is the label";
_label.BackColor = Color.Cornsilk;
_form.Controls.Add(_label);
_label.BringToFront();
As you can see, I've tried a lot of different stuff and nothing is working. I have also tried adding the label after the RECT has been added, to no avail. I am having a similar issue with adding a Background Image (though not the question being asked here). Does anyone know of a more forceful way to make the Label appear on top of the RECT?
Also, because of the API and dll's I am using, I cannot use something other than a RECT or WinForms.
You can use BringToFront on the label itself:
_label.BringToFront();
This will bring the label to the front of the Z order on the form, so it should display on top of other form elements.
I have a RECT() (not a WinFrom control) the RECT fills the entire WinForm
A "RECT" isn't a control - it's a definition size and position. Depending on what you're using to display your background, this may not work. If it's painting into the entire form, it could be overwriting your other controls, and "masking" them, no matter what you use for z order. Without more information, it could be difficult to provide guidance, but you'd have to make sure you cause the label to redraw after the "RECT".
I have a panel and on that I've a picturebox. There are around 20 labels that I've to show in the panel. I want the background of Label to be transparent ie the image in picturebox is shown and the label displays only the text.
Now since labels do not exhibit true transparency I made the labels child of picturebox
this.lbl1.Parent = pictureBox1;
This has solved my immediate problem but now when the form loads, all the labels take a while to become visible and do so one at a time. I'd appreciate if you guys can give some solution for this.
Thanks in advance
The standard cure for flicker is double-buffering. But that cannot solve this kind of flicker. It is a different kind, caused by having multiple windows overlapping each other. Each label is its own window. When the form needs to paint itself, it draws its background leaving holes for the child windows. Each child window then takes a turn drawing itself. And their child windows draw themselves next. Etcetera.
This becomes noticeable when one control takes a while to draw, no doubt your picture box. Especially when it displays a large image that needs to be resized. The holes for the child windows stay unpainted while the picture box draws. They have a white background, black when you use the form's TransparencyKey or Opacity property. This can contrast badly with the image in your picture box, that effect is perceived by the user as flicker.
One immediate cure is to not use controls so you don't pay for their window. A Label is very convenient but it is a massive waste of system resources to burn up a window just to display a string. You can simply implement the picture box' Paint event and draw the strings with TextRenderer.DrawText(). PictureBox has double-buffering turned on by default so the image as well as the text is drawn completely smoothly, no more flicker. The obvious disadvantage is that you lose the convenience of point-and-click, you have to write code.
There are other fixes possible. One of them is to prevent the picture box from leaving holes for the child windows. It will draw the entire image, the labels pop on top of them. That's still flicker but not nearly as noticeable. Add a new class to your project and paste this code:
using System;
using System.Windows.Forms;
internal class MyPictureBox : PictureBox {
protected override CreateParams CreateParams {
get {
var parms = base.CreateParams;
parms.Style &= ~0x02000000; // Turn off WS_CLIPCHILDREN
return parms;
}
}
}
Compile and drop the new picture box control from the top of the toolbox onto your form.
Yet another possible workaround is to make the form and all of its children double-buffered. This doesn't speed up the painting at all but all of the windows get rendered into a memory buffer, the result is blitted to the screen. You'll notice a delay but the window suddenly pops on the screen. This is called compositing. Winforms doesn't support this directly since it can have side-effects but it is easy to enable. Paste this code into your form class:
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}
Supported by XP and later. Watch out for painting artifacts.
or you can ditch the labels and draw the text yourself:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
TextRenderer.DrawText(e.Graphics, "Label1", SystemFonts.DefaultFont,
new Point(10, 10), Color.Black, Color.Empty);
}
The label does not support transparency, you must create your own unique custom control, you can see these code examples.
http://www.codeproject.com/KB/dotnet/transparent_controls_net.aspx http://www.codeproject.com/KB/vb/uLabelX.aspx
Bye
I have a borderless form (FormBorderStyle = None) with the height of 23 pixels (set in the designer)
When .NET draws my form at runtime - it draws it 38 pixels high (it adds the height of a title-bar for some reason).
MessageBox.Show(this.Height.ToString()); //this shows 38!! why?
To work it around I have to set "Height = 23;" in the Form_Load event.
private void MyForm_Load(object sender, EventArgs e)
{
this.Height = 23; //workaround. wtf??
}
You can try this yourself in Visual Studio 2010 (Winforms App, target Framework - 2.0).
Wtf?
Yeah, it is a bug, of sorts. Note how in the designer you set the size of the form with the Width and Height properties. Those properties include the size of the borders and the title bar. That's a problem however, your form may run on a machine where the user has increased, say, the title bar font size. That then would reduce the size of the window's client area. Or in other words, the form's ClientSize property would change on that machine. Leaving less room for the controls and messing up the design of your form pretty badly.
There's code inside the Form class that runs after the Handle is created, right before the Load event runs. It recalculates the Size of the form, using the same ClientSize you had on your machine. Now everything is good, the Height of the form won't match the one you set in the designer but the form otherwise looks the same and the layout of the controls is identical.
That same code also ensures that the window doesn't get too small. And that's where it falls over, it doesn't pay enough attention to the FormBorderStyle property. Clipping the height to the title bar size plus the client area height, as you found out. It also prevents the form getting too narrow, trying to make sure that the icon and min/max/close buttons are always visible. Even if you don't have any.
The workaround is to change the ClientSize after this code runs, the OnLoad override or Load event handler is the right place for that. Beware that if you hard-code the form size like this then you should also set the AutoScaleMode property to None. Make sure that this doesn't cause trouble on a machine that has a different DPI setting.
Our application has a number of objects on a canvas; the canvas is contained in a scroll viewer. We also have a slider control and some buttons, always centered at the top of the window.
I am trying to print the application by capturing a bitmap of the app, but without any 'decorations' -- slider, buttons, or scroll bars.
_scrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
_scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Hidden;
var s = xSlider;
s.Visibility = Visibility.Collapsed;
var b = xPlusButton;
b.Visibility = Visibility.Collapsed;
b = xMinusButton;
b.Visibility = Visibility.Collapsed;
b = xButton;
b.Visibility = Visibility.Collapsed;
The slider and buttons are hidden, as expected, but the scrollbars are not.
I suspect the application needs to redraw the layout in order to hide the scrollbars. Is there a way to make that happen? This is made more complicated by the fact that the print operation in SL 4 must be initiated by a UI gesture; there is no way (AFAIK) to initiate programatically, so this redraw must happen in one of the PrintDocument event handlers.
Thanks for any suggestions....
Try following,
canvas.InvalidateMeasure();
canvas.InvalidateArrange();
You can alternatively use WritableBitmap to capture runtime image and send image to print document if in case print document is ignoring render transform.
Also if you are using WritableBitmap to capture the element then you should give RenderTransform as second argument. Can you post your code to capture screen?
In addition to the InvalidateMeasure and InvalidateArrange methods, as suggested by Akash, you can try the UpdateLayout method.
The two invalidate methods will mark the control's measure or arrange as needing to be executed again, but won't necessarily do it immediately. The UpdateLayout will force it to execute some updates immediately.
It's a bit of a black box, so you may need to invalidate then call UpdateLayout. Sometimes you may just need to call UpdateLayout.