I have a forms program that displays two forms. Essentially form1 hides form2, rebuilds it's contents by creating a checkerboard, then shows form2. If I go thru it a step at a time, everything displays fine. I want to run several sequences of hide-rebuild-show with a short pause between each display of form2 so I can verify it is working as expected. If I put a msgbox after each show I see form2 drawn correctly. If I put a 2 second pause (loop doing nothing for 2 seconds) after the show I see the outline of the form but an open space where the checkerboard should be. I think it's some sort of timing issue but don't know how to fix it. What's the correct way to do a short pause?
private void Do1Bot()
{
SetStart();
for (int i = 1; i <= numsess; i++)
{
NextSess();
Do1Sess();
//MessageBox.Show("After do1 sess"); // Checkerboard appears in the middle of the form this way.
//Wait(2); // This way I get the form borders but a blank space in place of a checkerboard.
}
}
private void Wait(int secs)
{
DateTime Tend = DateTime.Now.AddSeconds(secs);
do { }
while (DateTime.Now < Tend);
}
Adding Application.DoEvents() to the wait loop (thanks to Sam Axe) solved the problem.
Related
I encountered this issue while trying to make a custom tooltip system for the custom hit-testing system of OxyPlot. The sample code is below, it moves the mouse cursor automatically when handling the event Form.Click to show the problem.
Steps to reproduce
create an empty Form (example size: 500x500)
handle the Click event of the Form
on Click
show the tooltip string "test 1"
move the mouse
hide the tooltip
wait a few seconds
show the tooltip string two-line "test 123456789\n\nanother line"
For a fraction of second the one-line "test 1" string is shown in the last cursor position too.
Screen recording (the issue is not visible in it)
Code
public partial class Form1 : Form
{
private ToolTip toolTip;
public Form1()
{
InitializeComponent();
Click += Form1_Click;
toolTip = new ToolTip();
toolTip.UseAnimation = false;
toolTip.UseFading = false;
}
private void Form1_Click(object sender, EventArgs e)
{
_ = DoIt();
}
private async Task DoIt()
{
Point pos = PointToClient(MousePosition);
pos.Y += Cursor.Current.Size.Height;
// Without the -2000, the duration of the tooltip is too long (because of the fade-in/out animation)
toolTip.Show("test", this, pos, Math.Max(0, toolTip.AutoPopDelay - 2000));
await Task.Delay(5000);
Cursor.Position = new Point(Cursor.Position.X + 100, Cursor.Position.Y);
toolTip.Hide(this);
Application.DoEvents();
await Task.Delay(5000);
pos = PointToClient(MousePosition);
pos.Y += Cursor.Current.Size.Height;
toolTip.Show("test 123456789\n\nanother line", this, pos, Math.Max(0, toolTip.AutoPopDelay - 2000));
}
}
What I tried
I made the code sample above to show the issue... I have also searched on StackOverflow and did not find something to help me.
Expected result
The second tooltip shows like the first tooltip, without a flashing image of the previous tooltip string.
Actual result
The old tooltip string flashes in the place of the new tooltip string. It is not very visible, mostly when the animations are enabled, but it is still ugly to me.
Ideas of possible solutions
Use a Timer or some trick with async-await.
Create two alternating ToolTip objects.
System configuration
I use for this .NET Framework v4.5.2 but I have tried the code above with .NET Framework v4.7.2 and it has the same problem. I use Windows 10 and VS 2019.
Thank you.
I might be a little late to the party, but anyhow.
The solution is to simply instantiate a new ToolTip component each time you want to display one. That's it, no flicking of the previous text anymore. (You might want to dispose the old one to make sure there are no leaks.)
Here's my function that I use to show print dialog (I write on C#). It was working nicely, when i wrote it. The problem is - since some day this code keeps only freezing my app instead of any printing. I tried also Show() with all 30 Missing params, but it doesn't make any change.
public bool Print()
{
var f1 = _application.Dialogs;
var f2 = f1[Excel.XlBuiltInDialog.xlDialogPrint];
bool DidntCancel = f2.Show();
return DidntCancel;
}
Freezing happens in f2.Show() call. My app window keeps refreshing it's view, but I can't click on anything. A tried to go into Show() method assembler lines via ste-by-step debug, but program doesn't go there, it dies exactly on the moment of method call.
Any help is very appreciated :)
I've been trying to find a way to disable a form, and put another form in front of it to act as a receiving party for my update command from SQL Server. My only problem is... The appearance of my main form, seems to be simple, and I'd rather keep it that way. I got two exhibits, since I can't post pictures in this state yet... But I'll update so more will understand.
Exhibit 1: My medical records form has several buttons, containing the INSERT, UPDATE, DELETE, View via Crystal Report, Print, Search and the Back buttons. There's also a listview object placed that occupies most of the form.
Exhibit 2: My UPDATE form is consisted of 7 textboxes: 6 containing basic info, and one containing the full name of the user which is by default, read-only. Two buttons, UPDATE and Back are in the form as well. Labels are placed above the basic info textboxes to signal the user's information to be entered.
Here's my question: How will I disable all buttons and the listview (which is the form of Exhibit 1), and place Exhibit 2 over it, WITHOUT hiding Exhibit 1?
To clarify, Exhibit 1 cannot be accessed as long as Exhibit 2 is in place, once it's open (which is my goal). I was trying to find a way to put the Update form over the medical records form, but to no luck, I keep seeing the Medical Records form clickable once the two are open at the same time. Please help me out... I'm just a student who's getting the hang of things in Visual Studio 2010, but I thought of asking just to know if anyone knows.
To those who would answer... thank you. :) It's my first post... so please be patient...
Here is an example which uses a main form mainForm and a button cb_update to open a second form updateForm. Upon opening, the 2nd form places itself over the 1st form and disables all controls of the 1st form. When it is closed it hides itself and re-enables all controls of the 1st form.
Note: both forms have a reference to each other, so they can work with all (public) parts of the other form. (If you need to you can always make a Control public!). One reference is kept from opening, the other is handed in the constructor..
En- or Disabling the controls is done recursively using the Controls collection and the fact that a Form is a Control, too. Here is code for the 1st form:
public partial class mainForm : Form
{
updateForm Uform;
private void cb_update_Click(object sender, EventArgs e)
{
if (Uform == null) Uform = new updateForm(this);
Uform.Show();
}
}
And here the 2nd:
public partial class updateForm : Form
{
mainForm main_Form;
public updateForm(mainForm main)
{
InitializeComponent();
main_Form = main;
}
private void updateForm_Shown(object sender, EventArgs e)
{
this.Size = main_Form.Size;
this.Location = main_Form.Location;
//*1*
setControlState(main_Form, false);
}
private void cb_back_Click(object sender, EventArgs e)
{
setControlState(main_Form, true);
this.Hide();
}
private void updateForm_FormClosing(object sender, FormClosingEventArgs e)
{
setControlState(main_Form, true);
this.Hide();
}
//*2*
public void setControlState(Control Ctl, bool enabled)
{
//*3*
foreach (Control c in Ctl.Controls) setControlState(c, enabled);
Ctl.Enabled = enabled;
//*4*
}
}
Edit: This code assumes that all Controls on the main_form are initially enabled. If that is not the case an exceptions list will take care of that, adding four lines of code:
exceptions.Clear(); //*1*
List<Control> exceptions = new List<Control>(); //*2*
if (!enabled && !Ctl.Enabled) exceptions.Add(Ctl); //*3*
if (enabled && exceptions.Contains(Ctl)) Ctl.Enabled = false; //*4*
Edit 2: I am still not quite sure, what you mean by "Place Exhibit 2 over it, WITHOUT hiding Exhibit 1". If you mean "place it above without hiding it" 1 or 2 lines of the code would be a little different.. Please explain!
I'm using winforms in c#.
I simplified my app so that it just has a ListBox and a Button.
Here is my button click event:
private void button1_Click(object sender, EventArgs e)
{
for (long i = 0; i < 66000; i++)
{
listBox1.Items.Add(i.ToString());
listBox1.SelectedIndex = listBox1.Items.Count - 1;
}
}
When I run my app and push the button I see the ListBox updating and after a while (it varies entry 3041 or so) the program will appear to hang as it adds the rest of the entries once its done, the ListBox will appropriately refresh.
Why the hang? Is it just too much to handle? I looked at my cpu usage and memory didn't seem to be using that much so I'm not sure what the problem is.
You misinterpret what's going on. Your listbox will freeze after 5 seconds. That's when Windows steps in when it notices that you are not taking care of the normal duties of a UI thread. Your window is replaced by the so-called "ghost window" to tell the user you've gone catatonic and that doing things like clicking the Close button isn't going to have any effect.
The easiest way to tell that you got the ghost window is from its title bar, it says "Not Responding".
A version of it that will fare somewhat better:
private void button1_Click(object sender, EventArgs e) {
listBox1.BeginUpdate();
for (long i = 0; i < 66000; i++) {
listBox1.Items.Add(i.ToString());
}
listBox1.EndUpdate();
listBox1.SelectedIndex = listBox1.Items.Count - 1;
}
You shouldn't otherwise expect windows to behave reasonable when you do unreasonable things. And you shouldn't expect users to behave reasonable either when they are faced with this UI disaster. But they'll let you know themselves.
Every time you add an item, the memory it takes gets bigger, and since the items are in a dynamic list, the program may have to reallocate space.
Add to that the fact that you're probably using just one thread in that app (you're not using a background worker thread to update the list, right?) and you see why your app hangs. Windows forms always take a while when they have to update their controls... It's just that for normal, everyday use, they take milisseconds to update. For arcane uses like a list containing more than a few hundreds of items in the GUI, they'll take seconds or minutes to refresh, and you'll perceive that as hanging. Place the item handling in another thread, and you'll see that the form will update it's GUI pretty fast, but will take a long time between updates.
Okay, I was able to create a simple Windows Forms project that reproduces some strange behavior I found. In the designer, make a form with a ListBox (named lbx) anchored Top, Left, Right, and Bottom, and a button (button1). Now, the Form's code is here:
using System;
using System.Windows.Forms;
namespace ListBoxKaboom
{
public partial class Form1 : Form
{
private bool _initFinished = false;
public Form1()
{
InitializeComponent();
this._initFinished = true;
this.Height += 100;
this.Height -= 50;
this.Height += 50;
}
private void lbx_SelectedIndexChanged(object sender, EventArgs e)
{
this.button1.Enabled = (this.lbx.SelectedItem != null);
}
protected override void OnLayout(LayoutEventArgs e)
{
if (_initFinished)
{
int lines = (this.lbx.Height - 4) / this.lbx.ItemHeight;
this.SuspendLayout();
while (lines < this.lbx.Items.Count)
{
this.lbx.Items.RemoveAt(this.lbx.Items.Count - 1);
}
while (lines > this.lbx.Items.Count)
{
this.lbx.Items.Add("Item " + (this.lbx.Items.Count + 1).ToString());
}
this.ResumeLayout();
}
base.OnLayout(e);
}
}
}
PLEASE NOTE THE FOLLOWING INSTRUCTIONS:
Run this, click any of the items in the list box, and use the arrow keys to move down far enough to cause the list box to scroll. Kaboom.
Exception (sometimes NullReferenceException and sometimes IndexOutOfBoundsException). Any ideas why? Also, I would think that the items would be in order, but they're not. Is this just a goofy corner case that didn't get handled properly by Windows Forms, or am I doing something wrong?
Stack trace:
at System.Windows.Forms.ListBox.NativeUpdateSelection()
at System.Windows.Forms.ListBox.SelectedObjectCollection.EnsureUpToDate()
at System.Windows.Forms.ListBox.SelectedObjectCollection.get_InnerArray()
at System.Windows.Forms.ListBox.SelectedObjectCollection.get_Item(Int32 index)
at System.Windows.Forms.ListBox.get_SelectedItem()
I Copy/Pasted it to an empty Form and get a StackOverflow exception. Looking at it, with manipulation of the Items inside a Layout event I would say you deserve little better.
I realize this may be a simplification of something else, but there simply are limits to what you can do in a EDS.
My best guess: The ResumeLayout triggers a recursive layout operation. You could try to stem it with a sibling to _initFinished but I would suggest rethinking tour design here.
I copy/pasted wrong, my bad (used layout event).
Second try:
based on the two while-loops I would expect the Item strings to be in order, and no vertical scrollbar. It is clear that the listbox is confused, showing vertical scroll range and with the items out-of-order. So some 'error' is already present in the internals of the Listbox, waiting for a Scroll. I can also reproduce it with the mouse.
A workaround: You should be able to get the desired effect using the Resize event.
Attempt to an explanation: The (unmanaged part of the) Listbox gets confused by (multiple) Add/RemoveAt operations with suspended Layout. The last items are drawn at the wrong place, and the Listbox can't compute pixel-to-item.
You should not manipulate any GUI elements in the
constructor, e.g. this.Height += 100; in your example.
Strange things can happen. I have been bitten by this
several times in legacy code.
Wait until form load time - handle the base.Load event and do the
height manipulation there.
From "When does Form.Load event get raised?":
Q: "... I need to find out basically
what the difference is between
putting code in Load event's handler,
versus putting code in the Form's
constructor after the
InitializeComponents() line ..."
A: "The Load event is fired once the
control/form has been fully
initialized and has a window handle
created. Therefore once this event
has fired it is a fully usable user
interface control. Remember that
inside the constructor the actual
window handle for the control/form has
not yet been created, you are only
creating C# objects here and inside
the InitializeComponent call."