I have a button that loads a Form, as it gets lots of data from a database and takes a few seconds, I want to advise the user to wait.
When I click the button the button text does not change.
This is the button Click code I am using:
private void btnItemConfigForm_Click(object sender, EventArgs e)
{
var itemConfigBtnText = btnItemConfigForm.Text;
btnItemConfigForm.Text = "Waiting...";
ItemConfigForm form = new ItemConfigForm();
form.Show();
if (form.Created)
{
btnItemConfigForm.Text = itemConfigBtnText;
}
}
If I Comment out
if (form.Created)
{
btnItemConfigForm.Text = itemConfigBtnText;
}
Then the button text changes to waiting after the new form window is visible.
What am I missing to get the button text to change before the form window is visible.
the simple solution is to add this row:
btnItemConfigForm.Refresh();
after this row
btnItemConfigForm.Text = "Waiting...";
Otherwise the text of the button will be changed only when the function ends, this function will redraw the form display!
P.s.
If you want the form will not be blocked - you can use in asynchronic running to the function "Show" (or New) then you will need Event to notify the first form when the form will be loaded
sorry for my English... :)
Added
btnItemConfigForm.Update();
under
var itemConfigBtnText = btnItemConfigForm.Text;
btnItemConfigForm.Text = "Waiting...";
This updates the button Control before moving on to initialising and showing the form.
Related
I am working on a WinForms Desktop application in C# .NET. The target framework is .NET Framework 4.8. When the application is started, the main form is displayed with several buttons. Each button hides the main form when clicked and opens another form that displays data with which the user interacts. The main form is the owner of the new form.One button opens a form that presents a list of files from a network share folder in a data grid view. The user selects a row in the data grid and clicks a button to import the information in the file to various tables in a SQL Server database. When the import is complete, the selected row is removed from the data grid. When the user closes this form, there is code in the Form Closed event to show the owner. This all works well.My problem is that when the main form is unhidden, I need to disable the button that opens the form to list files to import if there are not any files in the network share folder to be imported. There is also a line of code to change the button's text property informing the user there are not any files to import.I realize I can place the code to disable the button and change button text in the VisibleChanged event. But, I only want the code to run after the owned form's closed event shows the owner form. How do I enclose the code in the main form's VisibleChanged event to disable the file import button only after the owned form is closed. Or, is it possible to edit the properties of the button on the owner form in the Form Closed event prior to the Owner.Show();I found a similar question WinForm Form Closed Event. But when I follow the suggestion
private void LoadChildForm_Click(object sender, EventArgs e)
{
ChildForm form = new ChildForm();
form.FormClosed += new FormClosedEventHandler(ChildFormClosed);
form.Show();
}
substituting my names
private void btnImportHFR_Click(object sender, EventArgs e)
{
Form form = new frmHFRFiles();
form.FormClosed += new FormClosedEventHandler(frmHFRFiles_FormClosed);
form.Show(this);
Hide();
}
Visual Studio flags frmHFRFiles_FormClosed as an error for the name does not exist in the current context.
ChildForm form = new ChildForm(this);
Then in ChildForm constructor:
MainForm m_MainForm;
Public ChildForm (MainForm mainForm)
{
m_MainForm = mainForm;
}
Then in closing event:
m_MainForm.button1.Enabled = false;
Ensure button1 is public
Here is what I did. I created a boolean variable in the main form and set the initial value to false.
private bool updateButtons = false;
The main form's constructor executes the search for files in the network folder.
public frmMainMenu()
{
InitializeComponent();
Shared.FilesForImport = GetHFRFiles();
}
The form's load event calls the EnableButtons() method
public void EnableButtons()
{
btnImportHFR.Enabled = Convert.ToBoolean(Shared.FilesForImport.Count);
btnImportHFR.Text = btnImportHFR.Enabled ? "Find Available HFR" : "No HFR For Import";
btnGetFacilityStatus.Enabled = Shared.sqlWrap.GetDataForFacStat(Shared.DsFacStat);
updateButtons = false;
}
The main form's visible changed event fires after the form load event. The network folder is not searched again because the updateButtons value is set to false.
private void frmMainMenu_VisibleChanged(object sender, EventArgs e)
{
if(updateButtons)
{
EnableButtons();
}
}
In the button click event, the updateButtons value is set to true after the main form is hidden.
private void btnImportHFR_Click(object sender, EventArgs e)
{
frmHFRFiles form = new frmHFRFiles();
form.Show(this);
Hide();
updateButtons = true;
}
The child form's closed event calls the Owner.Show() method
private void frmHFRFiles_FormClosed(object sender, FormClosedEventArgs e)
{
Owner.Show();
}
This causes the main form's visible changed event to fire. Only this time the EnableButtons() method will run because the updateButtons value is true.
private void frmMainMenu_VisibleChanged(object sender, EventArgs e)
{
if(updateButtons)
{
EnableButtons();
}
}
public void EnableButtons()
{
btnImportHFR.Enabled = Convert.ToBoolean(Shared.FilesForImport.Count);
btnImportHFR.Text = btnImportHFR.Enabled ? "Find Available HFR" : "No HFR For Import";
btnGetFacilityStatus.Enabled = Shared.sqlWrap.GetDataForFacStat(Shared.DsFacStat);
updateButtons = false;
}
Finally, the EnableButtons() method sets the updateButtons value to false.
It seems rudimentary to me, but it works. Thank you everyone for your feedback.
My problem is that when the main form is unhidden, I need to disable the button that opens the form to list files to import if there are not any files in the network share folder to be imported. There is also a line of code to change the button's text property informing the user there are not any files to import.
So your main form has a button X. If this button is clicked a method is called. This method will first hide the form, then show the subform until the subform is closed. The method should disable button X, change the button's text and unhide the form.
To make this flexible, we won't do everything in one procedure, we make several procedures with the intention of the procedure: "Inform operator there are no files to import" "Disable opening the subform", and of course their counterparts "Enable opening the subform", "Inform operator there are files to import"
TODO: invent proper method names
private void ShowNoFilesToImport()
{
this.buttonX.Text = ...;
}
private void DisableOpeningSubForm()
{
this.buttonX.Text.Enabled = false;
}
The advantage of this, is that if you later want to change the way that you want to inform the operator, for instance if you want to use an information field at the bottom of you screen, you will only have to change this in one place.
Furthermore, you can reuse the procedures, for instance you can add a menu item that will do the same as button X, this menu item can call the same methods
private void PerformActionsButtonX() // TODO: invent proper name
{
// Hide this form, and show the Subform until it is closed
this.Visible = false;
using (var dlg = new SubForm())
{
// if needed, set properties of the subForm:
dlg.Text = ...
dlg.Position = ...
// show the form until it is closed
var dlgResult = dlg.ShowDialog();
this.ProcessDlgResult(dlgResult, dlg);
}
// Show that there are no files to Import and disable OpeningSubform
this.ShowNoFilesToImport();
this.DisableOpeningSubform();
// Finally: show this form:
this.Visible = true;
}
And of course call this method when ButtonX or menu item Y are clicked:
private void OnButtonX_Clicked(object sender, ...)
{
this.PerformActionsButtonX();
}
private void OnMenyItemYClicked(object sender, ...)
{
this.PerformActionsButtonX();
}
Okay so this is a little bit tricky to explain.
I'm working on a basic time table form.
I have 7 buttons, named btnMonTime, btnTueTime and so on till btnSunTime based on the days of the week. Now on each button click, a pop up window (winform) opens which lets the user select a certain time through a dateTimePicker control. The time is parsed into a string and stored. There is an Accept button on the popup which when pressed, the popup closes and a label beside the particular day stating the time is posted.
`
Now I know how to do it for one particular day, but the thing is that I have one single function doing this label creating. But how do I know which Time button was clicked to place it at the right place?
This is the code that I could come up with:
private void btnAccept_Click(object sender, EventArgs e)
{
formPopup.time = timePicker.Value.ToShortTimeString();
//label1.Text = formPopup.time;
Label newLabel = new Label();
newLabel.Text = formPopup.time;
newLabel.Location = new System.Drawing.Point(205 + (100 * formTimeTable.CMonTime), 78);
formTimeTable.CMonTime++;
newLabel.Size = new System.Drawing.Size(100, 25);
newLabel.ForeColor = System.Drawing.Color.White;
thisParent.Controls.Add(newLabel);
this.Close();
}
This is the Accept button click handler which places the label at the right place. Whereas the variable CMonTime keeps track of how many times a particular Time button was pressed.
public static int CMonTime = 0;
private void btnMonTime_Click(object sender, EventArgs e)
{
formPopup f2 = new formPopup();
f2.thisParent = this;
f2.Show();
}
And this is what is happening inside the Monday's Time button click handler.
But how can I know which day's Time button was actually clicked for proper placement of the timestamp label?
Like if Tuesday's Time button would be clicked, the timestamp should be displayed beside the Time button for Tuesday.
I tried to be as clear as possible.
Thanks in advance.
You can get the button that was clicked by casting the sender parameter as a Button control.
Use the button's location as a parameter for your formPopup constructor
private void btnMonTime_Click(object sender, EventArgs e)
{
var button = (Button)sender;
formPopup f2 = new formPopup(button.Location);
f2.thisParent = this;
f2.Show();
}
formPopup
Point _buttonLocation;
public frmPopup(Point buttonLocation)
{
_buttonLocation = buttonLocation;
}
Then use the button's location to set your label's location
private void btnAccept_Click(object sender, EventArgs e)
{
formPopup.time = timePicker.Value.ToShortTimeString();
Label newLabel = new Label();
newLabel.Text = formPopup.time;
newLabel.Location = new Point(_buttonLocation.X + 100, _buttonLocation.Y);
formTimeTable.CMonTime++;
newLabel.Size = new System.Drawing.Size(100, 25);
newLabel.ForeColor = System.Drawing.Color.White;
thisParent.Controls.Add(newLabel);
this.Close();
}
sender is the object that raised the event. In your case, it will be the button the user clicked on.
Because these combinations of controls repeat, it may be easier to create a UserControl that contains the buttons and labels you want. Think of a UserControl as a small form consisting of a few controls, and you place it on your form as many times as you need to. It has its own event handlers. That way there's technically only one button (not seven) and one event handler. Then you can place that UserControl on your form seven times.
There are other ways to avoid duplicating the code, like having one event handler and assigning it to the click events for all seven buttons. But the UserControl will really save you time if you want to edit the layout of the buttons themselves. You don't want to have to do that seven times.
To try it out:
Add > New Item > User Control. You'll get what looks like a new form. Add a few controls to it the same way you would add controls to a form. Then save it using the name (just for the sake of example) MyUserControl.cs.
Build the project.
A new toolbox tab will appear in the toolbox, and your control will be there.
Drag your control onto your form just as you would any other control.
More info on creating user controls
I'm working on a C# WinForms application in which I have a number of processes that are all managed by a 'master' application.
In this master application, each process is visualized by its own FlowLayoutPanel which contains a number of buttons for various function. I call these panels the 'process blocks'.
However, when many of these processes are made, not all blocks easily fit on the screen. For this reason I am implementing a 'compact mode', which hides all the buttons of all the process blocks, leaving only their name, their status and the start/stop button visible. I then assign a ContextMenuStrip to each process block, in which I show all the buttons listed as a ToolStripMenuItem so I can access all the functions of the process block that way. I am clearing these ContextMenuStrips dynamically and add the items when the menu is opened.
I do this by iterating over all the child controls of the FlowLayoutPanel, see if they are of type Button, and if so, I add them to the ContextMenuStrip. See the code snippet below:
private void PanelCmsOpened(object sender, EventArgs e) {
try {
ContextMenuStrip cMenuStrip = (ContextMenuStrip) sender;
// Clear all items from the context menu
cMenuStrip.Items.Clear();
// Loop over all controls in the FlowLayoutPanel
foreach (var c in CPanel.Controls) {
Button btn = c as Button;
if (btn == null) continue; // Not a button, continue
// Get the text from the button
string lbl = btn.Text;
if (string.IsNullOrEmpty(lbl)) {
try {
// The button has no text (only an icon), so we get the tooltip text of the button
lbl = PanelTooltip.GetToolTip(btn);
}
catch {
// We can't get any text to display, so skip this button
continue;
}
}
// Add a new item to the ContextMenuStrip
cMenuStrip.Items.Add(new ToolStripMenuItem(lbl,
btn.BackgroundImage,
(s, ea) => btn.PerformClick() // Perform a click on the button
)
{
Enabled = btn.Enabled
});
}
}
catch (Exception Ex) {
MessageBox.Show("Fout bij openen van context menu: " + Ex.Message, "Fout", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
The problem:
Now this is all working fine, as long as the buttons are visible. However, when going to compact mode, I hide the buttons by setting their Button.Visible property. In that case, nothing happens. I've tried putting a try-catch block around the PerformClick, but no exception is thrown. Just nothing happens. Does anyone know how to make this work for hidden buttons?
PerformClick checks if button available for clicking before performing click. Hidden buttons are considered unavailable. You can simply show button just before performing click, and hide it after clicking:
cMenuStrip.Items.Add(
new ToolStripMenuItem(lbl, btn.BackgroundImage, (s, ea) => {
var size = btn.Size;
btn.Size = Size.Empty; // button still will be invisible
btn.Show(); // make it clickable
btn.PerformClick();
btn.Hide(); // hide again
btn.Size = size; // restore original size
});
NOTE: If you also need to add some visible buttons, then you should handle them separately to avoid flickering
cMenuStrip.Items.Add(new ToolStripMenuItem(
lbl, btn.BackgroundImage, (s, ea) => ClickButton(btn)));
Where ClickButton is a method which performs different logic depending whether button is visible or not:
private void ClickButton(Button button)
{
if (button.Visible)
{
button.PerformClick();
return;
}
var size = button.Size;
button.Size = Size.Empty;
button.Show();
button.PerformClick();
button.Hide();
button.Size = size;
}
The only thing you can't do is setting visible = false;
Other than that yu can use any trick to hide the buttons: You can stack them behind another control, you can move them out of sight in any direction or even move them into a different parent:
To hide them:
panel1.Size = Size.Empty;
button1.Parent = panel1;
//..
To show them again:
button1.Parent = this;
//..
Assuming they sit on the Form.
Note that they will have kept their original locations and sizes; watch for changes in the tab-order and z-order!
If the button is not visible the Click event won't be raised.
One option is to take the code which is in the button click event and add it as a separate method. Then call the method instead of the PerformClick row. This will work though if you do identical things on every button click.
Another options it to make the buttons transparent. This will make them invisible to the client and the PerformClick event will work fine. You can see that in the following link :Drawing a transparent button.
Hope this helps.
In MainWindow.xaml.cs I want to open another window. My code is this:
WordToHtml.Window1 newForm = new WordToHtml.Window1();
newForm.Show();
return newForm.imagePath;
In Window1.xaml I have label, button and textBox. I want the user to click on the button and then I read the content from the textbox. However, when the breakpoint comes to newForm.Show();, the Window1 is shown but its invisible. It doesn't have label/button etc. This is the code of Window1:
string imagePath;
public Window1() {
InitializeComponent();
label1.Content = #"Please enter path";
}
void button1_Click(object sender, RoutedEventArgs e) {
//this is never entered because I can't see the button
}
public string newImagePath(string imagePath) {
return imagePath;
}
The code snippet you are showing is not making somethings clear. I am pointing out my confusion & questions.
1). I am assuming that when user clicks on some button on MainWindow, a new windows should open "Window1" where there is a label, button and textbox. then user enters some path in the textbox and clicks on the button the window1 should close and the imagepath should be available at "MainWindow" form. Please correct me if i am wrong.
2). In this code snippet
WordToHtml.Window1 newForm = new WordToHtml.Window1();
newForm.Show();
return newForm.imagePath;
You will not get anything at "newForm.imagePath" or null or empty string as you are trying to access this before the user enters any value in the textbox.
3). Using "SomeForm.Show()" method will open the new form which is not modal dialog means user can still get focus of "MainWindow" or click the button (that opens the new Window1 from). I suggest to use "newForm.ShowDialog()" window which returns focus to parent windows only when it is closed.
4). You shold use
newForm.Closing event to get the reference of the new form and before it is closed you can find the textbox control
string imagePath = (newForm.FindName("nameOfTextBox") as TextBox).Content.ToString();
and get the imagepath in the MainWindow.
I would like to disable the combo box which was in the first Form on clicking save on the second Form.
I am having 2 forms and my requirement is to append the 2 forms data together this was done
For my requirement i write a small code but it doesn't work for me
My code is as follows
Form1 i write my code as follows
public void loadingDatafrom(bool str)
{
if (true)
{
cmbServiceClassCode.Enabled = false;
}
else
{
cmbServiceClassCode.Enabled = true;
}
}
Form2 after save and hiding the form2 i call the above method
frmBatch frmbatch = new frmBatch(frmmain);
frmbatch.loadingDatafrom(true);
But this is not working any help please.
I'm not sure to understand your question. From your main form FrmBatch, Call the 2nd form FrmEntry in modal mode. After you Save and close FrmEntry form, you have to disabled combox box. In FrmBatch call this :
Form2 FrmEntry = new Form2();
FrmEntry.ShowDialog();
cmbServiceClassCode.Enabled = false;
The first thing to fix is
if (true) -> if (str)