I have developed a windows form application in C#.Net. There are multiple forms in the application. Main form say frmA has a push button say btnA. On clicking btnA, new form say frmB is opened and frmA goes behind frmB. frmB also has a push button say btnB and position (location) of btnB on frmB is exactly same as that of btnA on frmA. On clicking btnB, some different actions take place.
Now my problem is some of application users double click on btnA. I get two single clicks back to back. On first single click on btnA, frmB is opened with btnB. Second single click is immediately executed on btnB and in effect users don't even get to see frmB.
My constraint is I cannot change locations of buttons on either forms. Is there anyway I can handle this problem?
Set btnB.Enabled to false and use the following code. This will delay the possibility to click the button for half a second.
public partial class frmB : Form
{
private Timer buttonTimer = new Timer();
public frmB()
{
InitializeComponent();
buttonTimer.Tick += new EventHandler(buttonTimer_Tick);
buttonTimer.Interval = 500;
}
private void frmB_Shown(object sender, EventArgs e)
{
buttonTimer.Start();
}
void buttonTimer_Tick(object sender, EventArgs e)
{
btnB.Enabled = true;
buttonTimer.Stop();
}
}
Just try to fire new form into different position.
As i've understood user is firing frmB on first click
and pressing btnB on "second click"
It's not double click.
It's two different clicks
try something like that:
Form2 form=new Form2();
form.StartPosition = FormStartPosition.Manual;
form.Location=new Point(0, 10);
form.Show();
I would do some trick, What about changing the cursor position. Once user click on btnA cursor point will be shifted a little so the second click will not hit the btnB.
this.Cursor = new Cursor(Cursor.Current.Handle);
Cursor.Position = new Point(Cursor.Position.X - 50, Cursor.Position.Y - 50);
Cursor.Clip = new Rectangle(this.Location, this.Size);
disable it on the first click enable it when the user open again the forma or it come from forma to form b
this.button.enable=false;
immediately after pressing the button.
and write again in the constructor of forma
this.button.enable=true;
I agree that this UI is not friendly but if you absolutely cannot change the position of the buttons or the open position of the second form then you need to stop the second click causing the closure of frmB. Easiest way would be to build in a delay upon opening formB e.g.
public partial class FormB : Form
{
public FormB()
{
InitializeComponent();
Thread.Sleep(2000);
}
}
You can use IMessageFilter to detect WM_LBUTTONDOWN messages and suppress them if they occur within a certain time threshold:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Application.AddMessageFilter(new DoubleClickSuppressser());
}
private void button1_Click(object sender, EventArgs e)
{
Form2 f2 = new Form2();
f2.StartPosition = FormStartPosition.Manual;
f2.Location = this.Location;
f2.Show();
}
private void listBox1_MouseDoubleClick(object sender, MouseEventArgs e)
{
Console.WriteLine("listBox1 DoubleClick");
}
}
public class DoubleClickSuppressser : IMessageFilter
{
private int difference;
private DateTime Last_LBUTTONDOWN = DateTime.MinValue;
private const int WM_LBUTTONDOWN = 0x201;
public bool PreFilterMessage(ref Message m)
{
switch (m.Msg)
{
case WM_LBUTTONDOWN:
if (Control.FromHandle(m.HWnd) is Button)
{
if (!Last_LBUTTONDOWN.Equals(DateTime.MinValue))
{
difference = (int)DateTime.Now.Subtract(Last_LBUTTONDOWN).TotalMilliseconds;
Last_LBUTTONDOWN = DateTime.Now;
if (difference < System.Windows.Forms.SystemInformation.DoubleClickTime)
{
return true;
}
}
Last_LBUTTONDOWN = DateTime.Now;
}
break;
}
return false;
}
}
Note that I've specifically disabled Double Clicking only for Buttons with the Control.FromHandle() check. This will allow double clicks to work on other controls such as ListBoxes, etc...
In your own class, inherit the base control and use the .SetStyle() method to disable the double-click. The below code isn't for a Button, but it should work the same:
public class MOBIcon : PictureBox
{
public MOBIcon() : base()
{
this.SetStyle(ControlStyles.StandardDoubleClick, false);
}
}
The problem with enabling and disabling the button is that if the code you have in the click event runs so fast that the button gets enabled before the second click of the double click action (some users don't understand that it only takes 1 click) so here is my solution.
private const int waittime = 2;
private DateTime clickTime = DateTime.Now;
private void cmd_TimeStamp_Click(object sender, EventArgs e)
{
if ((DateTime.Now - clickTime).Seconds < waittime)
return;
else
clickTime = DateTime.Now;
try
{
cmd_TimeStamp.Enabled = false;
//Do some stuff in here
}
catch (Exception ex)
{
//Show error if any
MessageBox.Show(this, ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
cmd_TimeStamp.Enabled = true;
}
}
Related
I'm trying to write a simple application that opens two forms and displays a "newButton" on Form2 in two different places depending on the number of clicks of "button1" in Form1.
int ButtonClick1 = 0;
private void button1_Click(object sender, EventArgs e)
{
++ButtonClick1;
Button newButton1 = new Button();
newButton1.Name = "NewButton1";
newButton1.Text = "1";
Form2 frm2 = new Form2();
switch (ButtonClick1)
{
case 1:
button1.BackColor = Color.Red;
frm2.flowLayoutPanel1.Controls.Add(newButton1);
frm2.Show();
break;
case 2:
button1.BackColor = Color.Green;
frm2.flowLayoutPanel1.Controls.Remove(newButton1);
frm2.flowLayoutPanel2.Controls.Add(newButton1);
frm2.Show();
break;
case 3:
button1.BackColor = Color.Gainsboro;
frm2.flowLayoutPanel2.Controls.Remove(newButton1);
frm2.Show();
ButtonClick1 = 0;
break;
}
}
Every time I click on "button1" it's creating a new instance of Form2 because of frm2.Show() but only with that line, I can see that it's actually doing something.
I don't know how to change it so that it would just update Form2 without opening new instances.
You have identified two things that need to happen to achieve your desired outcome:
Manage the visibility of Form 2
Have Form 2 respond to property changes in Form 1.
To keep the clutter to a minimum, I'll provide a link where you can browse the example code. This answer will focus on how to achieve the outcome in the FlowLayoutPanel controls in Form2 when the button is clicked in Form1.
Property Change for ClickCount
One clean approach is to make a bindable ClickCount property in Form1 and a Button to increment it. Whenever that property changes, it fires a PropertyChanged event:
public partial class Form1 : Form , INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
int _ClickCount = 0;
public int ClickCount
{
get => _ClickCount;
set
{
// Detect changes and notify when that happens
if(_ClickCount != value)
{
_ClickCount = value == 3 ? 0 : value;
PropertyChanged?.Invoke(
this,
new ClickCountPropertyChangedEventArgs(
nameof(ClickCount),
ClickCount));
}
}
}
}
The constructor for Form2 will bind this event source to a PropertyChanged handler:
public Form2(INotifyPropertyChanged binding)
{
InitializeComponent();
binding.PropertyChanged += Binding_PropertyChanged;
}
One really nice thing about doing it this way is that Form1 doesn't need to know anything about Form2 (like whether it does or doesn't have FlowLayoutPanel controls or what Form2 might plan to do with the information) and also would be unaffected by future changes to those things.
Handling the ClickCount changed event
When the PropertyChanged handler in Form2 detects that the count has changed, it calls a specialized handler:
private void onClickCountChanged(int count)
{
labelClickCount.Text = $"Click Count = {count}";
buttonShowOrHideA.Visible = count == 1;
buttonShowOrHideB.Visible = count == 2;
}
You're probably wondering what happened to the "add" and "remove" statements for the buttons? Well here's the thing: why not let FlowLayoutPanel do its job of rearranging the layout when buttons are shown or hidden? This way, all you have to do is place the buttons in the designer and you can subscribe to their click events just one time instead of having to do that every time the button is added or removed. It takes less overhead and there are fewer things to go wrong. Here are the handlers for the Red and Green buttons:
private void buttonShowOrHideA_Click(object sender, System.EventArgs e) =>
MessageBox.Show("Red one clicked");
private void buttonShowOrHideB_Click(object sender, System.EventArgs e) =>
MessageBox.Show("Green one clicked");
Showing and hiding Form2
Being able to show and hide Form2 repeatedly and keep it out in front of Form1 means making sure the Form2 gets disposed properly (in the end) but not prematurely and I would encourage you to browse the full code for this. The main highlight is that when the ShowForm checkbox is true for the first time, it will instantiate Form2 by passing in a reference to itself.
if (checkBoxShowForm2.Checked)
{
if (_form2 == null)
{
_form2 = new Form2(binding: this);
}
_form2.Location = new Point(Location.X + Width + 10, Location.Y);
// Passing 'this' ensures Form 2 will stay in front of Form 1
_form2.Show(owner: this);
}
else _form2.Hide();
Here is my take on what you are trying to achieve:
Form 1:
public partial class testform1 : Form
{
testform2 form2;
int cnt = 0;
public testform1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
++cnt;
if (form2 == null)
{
form2 = new testform2();
}
form2.Show();
Button btn = new Button()
{
Text = "1",
Name = "NewButton1",
};
form2.AddButton(btn, (Button)sender, ref cnt);
}
}
Form 2:
public partial class testform2 : Form
{
public testform2()
{
InitializeComponent();
}
public void AddButton(Button NewButton, Button ParentButton, ref int counter)
{
switch (counter)
{
case 1:
flowLayoutPanel1.Controls.Add(NewButton);
ParentButton.BackColor = Color.Green;
break;
case 2:
flowLayoutPanel1.Controls.RemoveByKey(NewButton.Name);
flowLayoutPanel2.Controls.Add(NewButton);
ParentButton.BackColor = Color.Red;
break;
case 3:
flowLayoutPanel2.Controls.RemoveByKey(NewButton.Name);
ParentButton.BackColor = Color.Gainsboro;
counter = 0;
break;
}
}
}
You can pass the counter as reference in the AddButton method to update the value once it reaches 3.
In a c# I have two forms: Windows Form1 & Windows Form2.
When I click on a link label in Form1, the second form is shown. But when I close the second form, my first Windows form closes too.
my main form is:
namespace Windows_Forms_Basics
{
public partial class ShowLocationOnMapForm : Form
{
private string latitude;
private string longitute;
private GeoCoordinateWatcher watcher = new GeoCoordinateWatcher();
public ShowLocationOnMapForm()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
watcher = new GeoCoordinateWatcher();
// Catch the StatusChanged event.
watcher.StatusChanged += Watcher_StatusChanged;
// Start the watcher.
watcher.Start();
}
private void button_ShowOnMap_Click(object sender, EventArgs e)
{
textBox_Latitude.Text = latitude;
textBox_Longtitude.Text = longitute;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "<Pending>")]
private void Watcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e) // Find GeoLocation of Device
{
try
{
if (e.Status == GeoPositionStatus.Ready)
{
// Display the latitude and longitude.
if (watcher.Position.Location.IsUnknown)
{
latitude = "0";
longitute = "0";
}
else
{
latitude = watcher.Position.Location.Latitude.ToString();
longitute = watcher.Position.Location.Longitude.ToString();
}
}
else
{
latitude = "0";
longitute = "0";
}
}
catch (Exception)
{
latitude = "0";
longitute = "0";
}
}
private void label1_Click(object sender, EventArgs e)
{
}
private HelpForm form2 = new HelpForm();
private void linkLabel_help_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
this.Hide();
form2.Show();
}
}
}
and my second form is:
namespace Windows_Forms_Basics
{
public partial class HelpForm : Form
{
public HelpForm()
{
InitializeComponent();
}
private void HelpForm_Load(object sender, EventArgs e)
{
}
private void button_BackToForm1_Click(object sender, EventArgs e)
{
ShowLocationOnMapForm form1 = new ShowLocationOnMapForm();
this.Hide();
form1.Show();
}
}
}
Do you have any idea how to tackle this problem?
I am guessing you may be “confusing” forms and one forms “ability” to access another form. Let’s start by taking a look at the bit of code in your initial question…
private HelpForm form2 = new HelpForm();
private void linkLabel_help_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) {
this.Hide();
form2.Show();
}
This code is called from (what appears to be) a Form named ShowLocationOnMapForm.
On this form is a LinkLabel named linkLabel_help and its LinkClicked event is wired up and is shown above.
In addition, there appears to be a “global” variable form2 that is a HelpForm object (first line in the code above), which is another form. It is unnecessary to create this “global” form variable in the ShowLocationOnMapForm. …. However, we will continue as it is not pertinent at this point.
When the user “clicks” the `LinkLabel’ the above method will fire.
On the first Line in the method…
this.Hide();
Is going to “hide” the current form ShowLocationOnMapForm … and “show” the second form (HelpForm) with the line…
form2.Show();
On the surface, this may appear correct, however, there is one problem that I am guessing you are missing. The problem is…
How are you going to “unhide” the first form ShowLocationOnMapForm?
The second form (HelpForm) is “shown”, however, it isn’t going to KNOW anything about the first form. In this situation the first form is basically LOST and you have no way of accessing it. Therefore when you attempt the line… form1.Show(); in the second form, the compiler is going to complain because its not going to know what form1 is. In this code, there is NO way to get back to the first form. It is not only hidden from the user, but from the second form’s perspective the “CODE” can’t see it either!
Your faulty solution is not only “creating” another form1 but it is also doing the same with the second form.
Given this, it appears clear, that if you want to keep access to the first form… then you are going to have to use a ShowDialog instead of Show OR pass the first form to the second form.
Since it is unclear “how” you want these forms to interact, I can only proffer two (2) ways that you can use to at least keep access to the first form.
1) Use ShowDialog instead of Show when displaying the second form. It may look like …
private void linkLabel_help_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) {
HelpForm form2 = new HelpForm();
this.Hide();
form2.ShowDialog();
this.Show();
}
In the code above, the ShowDialog is going to “STOP” code execution in the first form and will wait for the second form (form2) to close. When executed, the first form is hidden, then the second form is shown, however, unlike the Show command, the line this.Show(); will not be executed until the second form closes. I am guessing this may be what you are looking for.
2) Pass the first form to the second form giving the second form “access” to the first form.
This will require that the second form has a “constructor” that takes a ShowLocationOnMapForm object as a parameter. With this, the second form can create a “global” variable of type ShowLocationOnMapForm that “points” to the first form. The constructor may look like…
private ShowLocationOnMapForm parentForm;
public HelpForm(ShowLocationOnMapForm parent) {
InitializeComponent();
parentForm = parent;
}
In the first form, you would instantiate the second form with...
HelpForm form2 = new HelpForm(this);
With this approach, the second form will have total access to the first form. You could add the “back” button as you describe and simply execute the line…ParentForm.Show(); However, I recommend you also wire up the second forms FormClose event and show the first form, otherwise, if the user clicks the close button (top right) and doesn’t click the “back” button, then you will have LOST the first form again.
Without knowing “exactly” how you want these forms to interact it is difficult to proffer a complete solution.
There are also other ways to accomplish this, however these should work for most cases.
I hope this makes sense and helps.
I tried to solve this problem by placing a 'Back to Form1' button in Form2. Which works, and the solution is as follows:
on my Form1 I have:
private Form2 form2 = new HelpForm();
private void linkLabel_help_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
this.Hide();
form2.Show();
}
and on my second form I have:
private void button_BackToForm1_Click(object sender, EventArgs e)
{
HelpForm form1 = new HelpForm();
this.Hide();
form1.Show();
}
But the problem is if I click the close button (on top right of the window) instead of the GoBack button on the second form, Form1 & Form2 both close in the same time.
Alright, this is a little strange, but essentially I needed a message box class with buttons that did not come as options within the message box class in c#. Therefore, I read up on how to create my own class (here is the link if your interested: http://social.msdn.microsoft.com/Forums/vstudio/en-US/1086b27f-683c-457a-b00e-b80b48d69ef5/custom-buttons-in-messagebox?forum=csharpgeneral), and used an example provided inside of that. Here is the relevant code:
public partial class Form1 : Form
{
public static bool MessageSucceded { get; set; }
public static string MessageContent{ private get; set; }
private void buttonMyMessageBox_Click(object sender, EventArgs e)
{
using (MyMessageForm myForm = new MyMessageForm())
{
myForm.ShowDialog();
if (MessageSucceded = myForm.ShowDialog(this) == DialogResult.OK)
{
if (MessageContent== "Yes do it")
{
//HERE DO WHAT YOU WANT IF THE USER CLICKS ON BUTTON YES DO IT
}
else if (MessageContent== "No, don`t do it")
{
//HERE DO WHAT YOU WANT IF THE USER CLICKS ON BUTTON NO, DON`T DO IT
}
}
}
}
}
public partial class MyMessageForm : Form
{
private void MyMessageForm_Load(object sender, EventArgs e)
{
this.StartPosition = FormStartPosition.CenterScreen;
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.MinimizeBox = false;
this.MaximizeBox = false;
this.buttonYes.Text = "Yes do it";
this.buttonNo.Text = "No, don`t do it";
this.labelMyForm.Text = "Are you sure you want to… ?";
}
private void buttonYes_Click(object sender, EventArgs e)
{
Form1.MessageSucceded = true;
Form1.MessageContent= buttonYes.Text;
this.DialogResult = DialogResult.OK;
this.Close();
}
private void buttonNo_Click(object sender, EventArgs e)
{
Form1.MessageSucceded = true;
Form1.MessageContent= buttonNo.Text;
this.DialogResult = DialogResult.OK;
this.Close();
}
}
This works exactly as I intended it to, except for one small detail. Just before the message box pops up, for a split second, a terminal version of the message box form opens up, before it closes on its own and the windows form version opens and functions as expected. I can't understand why this is happening, and it isn't causing any performance issues that I can notice, but aesthetically it looks very bad. Does anyone know why this is happening or how to stop it? I'm using Visual Studio 2010 Express for what its worth. Thanks for any help you have and taking the time to read this.
Edit: heres my code for main:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
You're invoking ShowDialog() twice:
myForm.ShowDialog();
if (MessageSucceded = myForm.ShowDialog(this) == DialogResult.OK) {
Form has been closed so it'll close immediatly, right way to do it is to save its first result and check that in your if condition:
var result = myForm.ShowDialog();
if (MessageSucceded = (result == DialogResult.OK)) {
Or directly:
MessageSucceded = myForm.ShowDialog() == DialogResult.OK;
if (MessageSucceded) {
Moreover if you returns different results according to clicked button then you do not need to compare MessageContent (it's also a bad idea to have a reference to caller just to return a result):
private void buttonYes_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Yes;
this.Close();
}
private void buttonNo_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.No;
this.Close();
}
Note that now you can simply remove such code and add proper DialogResult property to each button (because assign Form.DialogResult and call Form.Close() is default behavior if Button.DialogResult property is assigned). Done that you can simply use it like this:
switch (myForm.ShowDialog())
{
case DialogResult.Yes:
break;
case DialogResult.No:
case DialogResult.Cancel: // Unless ControlBox is also Hidden
break;
}
As last note: if you're targeting Vista+ you may also check Task Dialog instead of writing a custom form (you may keep it as fallback for XP's users).
I was wondering how I would make an image appear inside a messagebox that I set up so that whenever the mouse enters a label, it displays the messagebox. What would the code be for the image insertion?
Quick and dirty way to achieve this is to create another windows form that will have same buttons as message box but that will also have an image.
Create public Boolean property in this form that will be named something like OKButtonClicked that will tell you whether OK or Cancel was clicked
Set ControlBox property to False so that minimize, maximize and close buttons are not shown
Here is a code behind for this form
public partial class MazeForm : Form
{
public MazeForm()
{
InitializeComponent();
}
private bool okButton = false;
public bool OKButtonClicked
{
get { return okButton; }
}
private void btnOK_Click(object sender, EventArgs e)
{
okButton = true;
this.Close();
}
private void btnCancel_Click(object sender, EventArgs e)
{
okButton = false;
this.Close();
}
}
Finally in your main form you can do something like this
MazeForm m = new MazeForm();
m.ShowDialog();
bool okButtonClicked = m.OKButtonClicked;
Note that this is something I quickly created in 15 min and that it probably needs more work but it will get you in the right direction.
I'm working in C# with WinForms in a large application with multiple forms.
At several points I have another form coming up as a progress screen. Because I can't freeze my UI thread, I have to start the new form in a new thread. I'm using progressform.ShowDialog() to start the form, but because it's in a new thread, it is possible to Click or Alt + Tab back to the main form. I want to disable this.
My thought is that I can put an EventHandler on the mainForm.GotFocus event and redirect focus to progressForm if it is shown. However, the GotFocus event isn't being triggered when you switch applications or move between the progressForm and mainForm. I'm guessing that it's because some element in mainForm has focus, not the form itself.
If anyone knows of a better way to do this (I'm not committed to the EventHandler approach) or working code for the EventHandler approach, it would solve my problem.
Edit
As per the comment, I tried using the Activated event.
// in InitializeForm()
this.Activated += FocusHandler;
// outside of InitializeForm()
void FocusHandler(object sender, EventArgs e)
{
if (ProgressForm != null)
{
ProgressForm.Focus();
}
}
But it still allowed me to click back to the main form and click buttons.
I've tried some ways and found this which does work as you want, the whole idea is to filter some message from your main UI when your progress form is shown:
public partial class Form1 : Form
{
[DllImport("user32")]
private static extern int SetForegroundWindow(IntPtr hwnd);
public Form1()
{
InitializeComponent();
}
ChildUI child = new ChildUI();
bool progressShown;
IntPtr childHandle;
//I suppose clicking on the button1 on the main ui form will show a progress form.
private void button1_Click(object sender, EventArgs e)
{
if(!progressShown)
new Thread(() => { progressShown = true; childHandle = child.Handle; child.ShowDialog(); progressShown = false; }).Start();
}
protected override void WndProc(ref Message m)
{
if (progressShown&&(m.Msg == 0x84||m.Msg == 0xA1||m.Msg == 0xA4||m.Msg == 0xA3||m.Msg == 0x6))
//0x84: WM_NCHITTEST
//0xA1: WM_NCLBUTTONDOWN
//0xA4: WM_NCRBUTTONDOWN
//0xA3 WM_NCLBUTTONDBLCLK //suppress maximizing ...
//0x6: WM_ACTIVATE //suppress focusing by tab...
{
SetForegroundWindow(childHandle);//Bring your progress form to the front
return;//filter out the messages
}
base.WndProc(ref m);
}
}
if you want to show your progress form normally (not a Dialog), using Application.Run(), showing form normally (using Show()) without processing some while loop will terminate the form almost right after showing it:
private void button1_Click(object sender, EventArgs e)
{
//progressShown = true;
//child.Show();
if (!progressShown)
{
new Thread(() => {
progressShown = true;
if (child == null) child = new ChildUI();
childHandle = child.Handle;
Application.Run(child);
child = null;
progressShown = false;
}).Start();
}
}
I've tested and it works like a charm.