Properly reload form to its initial state after saving data - c#

I know there's many answers closely to this one but I really want an option from someone experienced.
So, after fill in form, user have option to Save data and remain in form, save data and close form or save data and add new record.
For saving, saving and close its pretty simple, but for saving and add new record, I have some problems.
So far, I'm resetting all controls to its original state after save data.
Comboboxes to SelectedIndex = -1;
Textboxes to string.Empty;
Radioboxes to checked = false;
Checkboxes to checked = false;
DatetimeEdit to Values = null;
And this works for resetting controls in a small form.
Is there any other, faster and better way to achieve this goal?
Maybe closing and reopening Form?
All my controls, comboboxes fill and other needs are made in constructor. I do not load anything in Load Event.

In order to do it efficiently and reusebale its possible to do something like this, please see comments:
private void RollBackForm()
{
// put here all the containers the contain the controls, panel,groupbox the form itself etc...
Control[] Containers = { panel1, groupBox1, this };
// iterate trough all containers
foreach (Control container in Containers)
{
// check control type, cast it and set to default
foreach (Control childControl in container.Controls)
{
if (childControl is ComboBox)
{
((ComboBox)childControl).SelectedIndex = -1;
}
else if (childControl is TextBox)
{
((TextBox)childControl).Text = string.Empty;
}
else if (childControl is RadioButton)
{
((RadioButton)childControl).Checked = false;
}
else if (childControl is CheckBox)
{
((CheckBox)childControl).Checked = false;
}
}
}
}

Related

How do I change the properties of a control within a list dynamically (C# Winform)?

I have a list of checkbox controls
public List<CheckBox> _checkBoxes = new List<CheckBox>();
I add a certain amount during runtime using a switch case with the properties such as location and visibility.
This all works fine, but if I want the checkboxes to disappear or become checked at some point the GUI doesn't get updated, the list is updated with the new data but just not visually.
* I ended up doing this*
I created a bunch of checkboxes on the winfrom.Created a method which you can choose which ones are visible. Then created a method to fill a list of type checkbox. Then you can search the panel for control types, once you found the control, it can be manipulated. This isn't elegant and probably isn't the best way, but i am new to custom controls and winforms.
//search panel for checkboxes
foreach (Control c in panel1.Controls)
{
if (c is CustomControls.RoundedCheckBox)
{
CustomControls.RoundedCheckBox checkBox = c as CustomControls.RoundedCheckBox;
//if it is checked add to list
if(checkBox.Checked)
{
_checkBoxes.Add(checkBox);
}
}
}
If you have something like this:
_checkBoxes.Add(new CheckBox());
_checkBoxes[0].Parent = this;
then you should be able to manipulate your checkboxes from the list:
_checkBoxes[0].Checked = false;
But, the problem may occur if you do it in some kind of loop and want to see the results immediately.
In Windows there is something called message loop. Application simply works like that (pseudocode: TL;DR)
while(true)
{
message = GetFirstMessage();
if(message != null)
{
if(message.Id == CloseApplication)
break;
DispatchMessage(message);
RemoveFirstMessage();
}
}
So, application takes message from queue, then process it. A message is everything - button click, mouse move, paint... Everything.
So when a message is dispatched it looks for the control that should receive this message and then it does some work.
So, for example if you have something like that:
foreach(var ch in _checkBoxes)
{
ch.Checked = false;
DoSomeWorkThatTakesTime();
ch.Checked = true;
}
You won't see the change, because you are "trapped" in DispatchMessage. When you set Checked, you really sending a message. But this message cannot be Dispatched right now, because you are inside the foreach loop.
So the only thing you can do here is to tell your application - now, please DO READ message queue. In WinForms it's called "DoEvents", so this will do the work:
foreach(var ch in _checkBoxes)
{
ch.Checked = false;
Application.DoEvents();
DoSomeWorkThatTakesTime();
ch.Checked = true;
Application.DoEvents();
}

Using findControl to find a child element

I have a Placeholder and I have a dynamically created panel in the placeholder, I also have some dynamically added radio buttons in the panel, now I can usefindControl() to find the radio buttons if they are direct children of the placeholder.
I've literally spent the whole of yesterday trying to find them when they are the child elements of the Panel. How is there a way to do this?
Here's my code below:
PlaceHolder1.Controls.Add(myPanel); //add the panel to the placeholderenter code here
myPanel.Controls.Add(myRadioButton); //add the radiobutton to the panel
You should make method that recursively searches for a control using it's Id. That mean that the method will search for a control inside of (in your case) placeholder. If method finds control, it will return it. If not, it will go search every placeholder's subcontrol, going "deeper". And then, if nothing is found, it will search one more level down, in every placeholder subcontrols' subcontrol etc.)
private Control FindControl(string ctlToFindId, Control parentControl)
{
foreach (Control ctl in parentControl.Controls)
{
if (ctl.Id == ctlToFindId)
return ctl;
}
if (ctl.Controls != null)
{
var c = FindControl(ctlToFindId, ctl);
if (c != null) return c;
}
return null;
}
and then use it like this:
Control ctlToFind = FindControl(myRadioButton.Id, Placeholder1);
if (ctlToFind != null)
{
//your radibutton is found, do your stuff here
}
else
{
// not found :(
}
Finding Controls recursive is an option, but it also has a couple of down-sides.
If you know the ID's of all the controls you can just use FindControl
RadioButtonList myRadioButton = PlaceHolder1.FindControl("Panel1").FindControl("RadioButtonList1") as RadioButtonList;
Label1.Text = myRadioButton.SelectedValue;
But you will need to give your dynamically added controls an ID.
Panel myPanel = new Panel();
myPanel.ID = "Panel1";
RadioButtonList myRadioButton = new RadioButtonList();
myRadioButton.ID = "RadioButtonList1";
PlaceHolder1.Controls.Add(myPanel);
myPanel.Controls.Add(myRadioButton);

Clearing all data on tab page when clicking No Button

in my application im submitting a data to the db on a tabControls page(page:tabPage2) and i want when hitting the submit button first saving data to db(im achieving this) the a question will ask anything will be done? if the user hit the no button all fields on tabpage2 will reset. so i wrote a script like below but it is not clearing fields.
if (dr == DialogResult.Yes)
{
for (int i = 0; i < this.tabControl1.Controls.Count; i++)
{
if (this.tabControl1.SelectedTab == tabPage2)
{
if (tabPage2.Controls[i] is TextBox)
{
tabPage2.Controls[i].Text = "";
}
if (tabPage2.Controls[i] is ComboBox)
{
tabPage2.Controls[i].Text = "";
}
if (tabPage2.Controls[i] is PictureBox)
{
tabPage2.Controls[i].Text = "";
}
if (tabPage2.Controls[i] is RadioButton)
{
tabPage2.Controls[i].Text = "";
}
}
}
}
If you control the class for the page layout within the specific tab page you want to clear, it's probably best to create a public or internal method in that class (such as Clear()) which can access each of its member controls and clear them directly. That's the easiest approach, and it should usually apply.
If you instead need it to handle a page with an unknown structure, you might need an approach like:
private void ClearControls(Control parentControl)
{
foreach (Control ctrl in parentControl.Controls)
{
TextBox ctrlText;
ComboBox ctrlCombo;
PictureBox ctrlPicture;
RadioButton ctrlRadio;
// Pay careful attention to the parentheses...
if ((ctrlText = ctrl as TextBox) != null)
{
ctrlText.Text = string.Empty;
}
else if ((ctrlCombo = ctrl as ComboBox) != null)
{
ctrlCombo.SelectedIndex = -1;
}
else if ((ctrlPicture = ctrl as PictureBox) != null)
{
// Logic to clear a PictureBox called ctrlPicture
}
else if ((ctrlRadio = ctrl as RadioBox) != null)
{
// Logic to clear a RadioButton called ctrlRadio
}
else if (ctrl.Controls.Count > 0)
{
ClearControls(ctrl); // Recursively clear contained controls.
}
}
}
With a call to start it off from the original handler:
if (dr == DialogResult.Yes)
ClearControls(this.tabControl1);
You are itering over the collection of TabControl child controls, not actual TabPage's.
Change your code to this instead:
if (dr == DialogResult.Yes && this.tabControl1.SelectedTab == tabPage2)
{
foreach (var ctrl in tabPage2.Controls)
{
if (ctrl is TextBox || ctrl is ComboBox || ctrl is PictureBox || ctrl is RadioButton)
{
ctrl.Text = "";
}
}
}
I should say though than setting the Text property to "" for controls other than TextBox feels rather wrong to me. As you'll find out, this won't work for combos, images and radio buttons.
Also if you have controls nested into panels or the like they won't be cleared. Containers have their own nested collection of controls, which in turn can also be containers, and so on and on.
IMHO it would be far better for you to explicitely reset form controls one by one rather than trying to find them dynamically on your form. This way you'll be free to move your controls around at design time without ever worrying about breaking the resetting logic.
Additional suggestion: you can also attach your controls at design time to an instance of your own IExtenderProvider component which will take care of resetting controls appropriately based on their type.

A Collection *OF* DataGridViews? Manipulating all DataGridViews at Once

I'm continuing to work on a few VSTO's and I was wondering if there was a way to refer to all the datagridviews in a class at once. I can't seem to figure out what the container should be, and I can't seem to add them to arrays/other containers?
The psudo code for what I'm tring to do would be something like:
For Each datagridview in Globals.MyUserControl
'change some datagridview property ie:
datagridview1.ReadOnly = True
Next
I would be hapy in C# or VB.net, or really any explanation of if this can or can't be done. At the moment I'm manually setting it for all the different datagridviews, as that number grows, I would like a way to hit them all at once.
Still trying to work on the solutions below, another way I've tried this that doesn't work:
For Each ctl In Me.Controls
If TypeOf ctl Is DataGridView Then
ctl.ReadOnly = True
ctl.AllowUserToDeleteRows = False
End If
Next
But I don't know why that doesn't work.
You can use a foreach loop:
foreach (DataGridView ctrl in Globals.MyUserControl.Controls)
ctrl.ReadOnly = true;
If you're expecting any non-datagridview controls in the controls collection that you don't want to set to read-only, then instead of a single statement, you can check the type of ctrl.
foreach (Control ctrl in Globals.MyUserControl.Controls)
if(ctrl is DataGridView) ctrl.ReadOnly = true;
Using LINQ, you can do this:
Globals.MyUserControl.Controls.Cast<Control>().ToList().ForEach((ctrl) => { if (ctrl is DataGridView) ((DataGridView)ctrl).ReadOnly = true; });
Or if all your controls are known to be DataGridView controls, then you can do this:
Globals.MyUserControl.Controls.Cast<DataGridView>().ToList().ForEach(ctrl => ctrl.ReadOnly = true);
To find child controls inside other controls, define a recursive method and call that:
private static void FindControlsRecursively(Control.ControlCollection collection)
{
foreach (Control ctrl in collection)
{
if (ctrl is DataGridView)
((Label)ctrl).ReadOnly = true;
else if (ctrl.Controls.Count > 0)
FindControlsRecursively(ctrl.Controls);
}
}
Then call it with the controls of your user control from your user control:
FindControlsRecursively(this.Controls);
Something like this should work:
For Each ctl In Me.Controls.OfType(Of DataGridView)()
ctl.ReadOnly = True
ctl.AllowUserToDeleteRows = False
Next
Or C#
foreach (DataGridView ctrl in this.Controls.OfType<DataGridView>())
{
ctrl.ReadOnly = true;
ctrl.AllowUserToDeleteRows = false;
}
This loops through only the DataGridViews in the form.
Additionally you can add them to a List(Of DataGridView), if necessary
Another option is to declare a class that inherits DataGridView, set the properties you want, and declare new datagridviews of this type to add to your form(s).
I'm not sure if I should put this in the answer, or if Tombola wants to move it to his, but here was my solution. The problem was that all my datagridviews were nested on tab pages in a tab control. To get it to work I used:
For Each tabpg In TabControl1.TabPages()
For Each ctl In tabpg.Controls 'note, was not able to use 'OfType here, but had to drop to the If statement or I ran into runtime errors.
If TypeOf ctl Is DataGridView Then
ctl.ReadOnly = True
ctl.AllowUserToDeleteRows = False
End If
Next
Next

Iterating through textbox controls in a panel C#

I have seen many others with similar problems but I cannot find the flaw in my logic here. Any help would be greatly appreciated.
I have a Panel which I have added numerous label and textbox controls to, ie:
myPanel.Controls.Add(txtBox);
These controls are created and added in a method called previous to the iteration method.
I want to iterate through each textbox and use its Text property as a parameter in another method but I am not having any luck. Here is my attempt to iterate:
public void updateQuestions()
{
try
{
foreach (Control c in editQuestionsPanel.Controls)
{
if (c is TextBox)
{
TextBox questionTextBox = (TextBox)c;
string question = questionTextBox.Text;
writeNewQuestionToTblQuestions(question);
}
}
}
catch (Exception err)
{
Console.WriteLine(err.Message);
}
}
The problem I am having is that the controls are not in the Panel when I arrive at this updateQuestions() method. Here is the process involved:
A commandButton is clicked and the questions are read from a DB, for each question a method is called which adds 2 labels and a textbox to editQuestionsPanel.Controls. This panel is inside a PlaceHolder which is then made visible.
When a button inside the PlaceHolder is clicked, the updateQuestions() method is called and the editQuestionsPanel.Controls.Count = 1. As there are approx 12 questions in the DB it should be around 36. The one control inside the Panel is of type:
System.Web.UI.LiteralControl
It contains no controls.
I am sure that somwhere in the lifecycle the Panel's controls are being cleared but I do not know how to step thru the life cycle. I have a Page_load method which is called as soon as a button is clicked but once the button which calls updateQuestions() is clicked the editQuestionsPanel.Controls.Count is already back to 1 so it must be cleared before this but I do not know how to correct this...
Any help you can give to help me solve this would be greatly appreciated - its killing me!
This selects from collection controls only that which are of type TextBox.
(the same as control is TextBox or (control as TextBox) != null)
If controls are contained in editQuestionsPanel.Controls:
using System.Linq;
IEnumerable<TextBox> textBoxes = editQuestionsPanel.Controls.OfType<TextBox>();
foreach (TextBox textBox in textBoxes)
{
// do stuff
}
To select all child controls use next extension method:
public static IEnumerable<T> GetChildControls<T>(this Control control) where T : Control
{
var children = control.Controls.OfType<T>();
return children.SelectMany(c => GetChildControls<T>(c)).Concat(children);
}
Using:
IEnumerable<TextBox> textBoxes = editQuestionsPanel.GetChildControls<TextBox>();
When you add controls dynamically, you need to do that on every request - asp.net doesn't do that for you!
Add the controls in the Init or Load phase, then they will get populated with the postback values.
A frequently made mistake: Container.Controls only contains the first level child controls in this container. That is: TextBox1 in PanelA, PanelA in PanelB, you can't get TextBox1 in PanelB.Controls.
My solution is to write an extension method:
public static IEnumerable<Control> AllControls(this Control ctl)
{
List<Control> collection = new List<Control>();
if (ctl.HasControls())
{
foreach (Control c in ctl.Controls)
{
collection.Add(c);
collection = collection.Concat(c.AllControls()).ToList();
}
}
return collection;
}
Now TextBox1 is in PanelB.AllControls(). To filter all controls with type, using PanelB.AllControls().OfType<TextBox>()
If the other answers don't help, try doing your code but add recursivity. Your code would not work if it's editQuestionsPanel => Panel => Textbox
You can do something like this instead:
var questions = from tb in editQuestionsPanel.Controls.OfType<TextBox>()
select tb.Text;
foreach(var question in questions)
{
writeNewQuestionToTblQuestions(question);
}
Try this
int Count = 0;
foreach (Control ctr in Panel1.Controls)
{
if (ctr is TextBox)
Count++;
}

Categories