I have two forms named 'mainForm' and 'addRslt'. The idea is when users click on a button in mainForm, the addRslt form will Show() and then user will populate a TreeView. Now when user WANT to CLOSE this addRslt form, program will instead Hide() the form (using e.Cancel = true; ) so later if user reopen this he/she can add more things to the TreeView.
In my mainForm I have a button for showing this addRslt form, and also inside this button's click code, there is my FormClosing delegte which will detect and copy the contents od TreeView in addRslt form to a TreeView in mainForm.
Now the problem is I want to check for duplicated Nodes and do not add them to TreeView in mainForm. This is done right, but I also have a message box that tells the user that program had not added existing nodes! thats ok till now.. BUT problem is with each time I do this, this messagebox will appear N+1 times! I mean if I do it for first time, this message box appears 2 time and etc...
Here is my code! Sorry for long story!
private void menuFileAddTestResults_Click(object sender, EventArgs e)
{
addRslt.Show();
addRslt.FormClosing += delegate
{
foreach (TreeNode node in addRslt.treeViewSelectedFiles.Nodes)
{
TreeNode newNode = new TreeNode();
newNode.Text = node.Text;
newNode.Name = node.Name;
newNode.Tag = node.Tag;
if (!treeViewTestFiles.Nodes.ContainsKey(node.Name))
{
treeViewTestFiles.Nodes.Add(newNode);
}
else
{
countExist++;
}
}
if (countExist > 0)
{
MessageBox.Show(countExist.ToString() + " Test files are already exist in the list!");
}
countExist = 0;
};
}
You're adding a FormClosing handler every time you show it. Just add it once, when you set up the rest of what the form looks like. (Personally I'd probably split this into a separate method... I don't think it's a particularly appropriate use of a lambda expression - it's a fairly large chunk of code which doesn't refer to any variables declared within the containing method, so there's no real benefit.)
It looks like you are adding your inline implementation to the multicast delegate repeatedly.
Clearly this is not your intention. You will either need to subscribe one instance of the delegate as Jon Skeet suggests, or manage the subcriptions each time.
Related
I have a UserControl that is dynamically added to a FlowLayoutPanel. In that same UserControl I have a button to remove itself if the user wants it, obviously at runtime. To eliminate I mean not only to eliminate that tight button, but also the full UserControl that contains the button.
The code of when the UserControl are added dynamically at the moment is as follows:
private void agregaUC() {
UserControl1 UC = new UserControl1();
aux += 1;
UC.Tag = aux.ToString();
flowLayoutPanel2.Controls.Add(UC);
}
The code to eliminate this is on the side of the form, that is, where the UserControl are being added. The button event to remove the UserControl is thrown by code through the operator + =, then there I write the suggestions that you give me.
EDIT: Based on the sample of code you've added, I've modified the below code to work better with what you are looking for. You need to find out how to access the Tag of the control you're trying to remove.
Since you don't have a reference, then you should make sure that the .Tag property can be found, because then you can do something like
foreach (Control c in flowLayoutPanel2.Controls) {
if (c.Tag == "Aux") {
flowLayoutPanel2.Controls.Remove(c);
c.Dispose();
break;
}
}
EDIT
Reading through all the comments everywhere, it seems like this is what's happening. There is a UserControl, inside that user control is a Button (Delete) and the button's Click event is subscribed to by the window, and it's in this event handler that we're trying to remove the UserControl from flowLayoutPanel2
Based on these assumptions, your function should look like this:
void UserControl_Delete_Click(object sender, EventArgs e)
{
Button Delete = (Button)sender;
UserControl UC = (UserControl)Delete.Parent;
flowLayoutControl2.Controls.Remove(UC);
UC.Dispose();
}
This is assuming a lot about the internal structure of everything, as I don't have the code to confirm this will work. It will get you a long ways down the path, though, and should only need a little tweaking based on the actual structure of the UserControl.
You can try something like that.
this.Parent.Controls.Remove(this);
Control.Parent Property.
Remark: Setting the Parent property value to null removes the control from the Control.ControlCollection of its current parent control.
So
this.Parent = null;
Edit
The code is intended to be called from within the user control itself.
I have 2 Treeview in one form as below.
left_treeview_node1 | right_treeview_node1
left_treeview_node2 | right_treeview_node2
left_treeview_node3 | right_treeview_node3
left_treeview_node4 | right_treeview_node4
here we can drag and drop left treenode on right for mapping.
now user has opened 2 instances of same form and he is dragging left_treeview_node1 from first instance and dropping it to right_treeview_node4 of another instance of same form.
so how to differentiate the another instance and stop supporting drag and drop from one instance to another instance.
are there different GUID for each instance of same form?
can we use Mutex to differentiate between 2 instances of same form?
Thanks in advance...
I'll assume you pass the TreeNode as the object to drag:
private void treeView1_ItemDrag(object sender, ItemDragEventArgs e) {
treeView1.DoDragDrop(e.Item, DragDropEffects.Move);
}
Then you want to write the DragEnter event handler on the second TreeView to verify that you indeed get a TreeNode and that it came from the TreeView you expected:
private void treeView2_DragEnter(object sender, DragEventArgs e) {
if (!e.Data.GetDataPresent(typeof(TreeNode))) return;
var node = (TreeNode)e.Data.GetData(typeof(TreeNode));
if (node.TreeView == this.treeView1) {
e.Effect = DragDropEffects.Move;
}
}
The object identity check will not match it the node came from another form. If you want to check that it came from the expected form instead of the expected TreeView (seems unlikely here) then write the test as if (node.TreeView.FindForm() == this).
Use Control.Handle property which uniquely identifies a control or a form in your case.
Compare values returned by Control.FindForm - do not allow dropping if they are different for a dragged and target items.
You can also just test in the drag events to see if the form is focused. If it's not then you know the rest. Or if you really want to be sure, disable/enable drag drop on the controls when the form loses/gains focus.
Here I have used the control's HASHCODE to determine the different instance of the control like below and it worked.
in tvw1.DragDrop event
Dim draggedNode As TreeNode = Nothing
draggedNode = DirectCast(e.Data.GetData(GetType(TreeNode)), TreeNode)
If draggedNode Is Nothing Then Exit Sub
If Not (draggedNode.TreeView.GetHashCode = tvwStagingArea.GetHashCode) Then
'do whatever you want
Exit Sub
End If
I'm fairly new to Visual C# and I'm writing an GUI app with multiple forms. One form is main window, and the rest are some kind of option windows. When showing an option window, I need to load some data to it (for example a string to window's editbox), then edit it and return back to main window when closing option window. Is there any simple way I can achieve it?
I've found some solutions like, or c# event handling between two forms, but I can't really conform it to my needs. I was thinking about passing data in constructor, but how to get it back? I've found something about ShowDialog, but as I said I'm new to C# (started yesterday ^^) and don't know if I can use it.
Any ideas, please?
I found the following previous answer which outlines sending specific properties from the one form to another:
Send values from one form to another form
The using keyword will also ensure that the form is cleaned-up properly, here's a link to it's usage (pardon the pun...) : http://msdn.microsoft.com/en-us/library/vstudio/yh598w02.aspx
I've run into the same issue to be honest, and I have to say that prior to this discussion I would just pass the parent form itself to the child and alter it in that way. Such as:
ChildForm child = new ChildForm(this); //from the parent
and
public ChildForm(ParentForm parent)
{
this.parent = parent;
}
Probably not the best convention though, as you probably don't need to access that much from the parent as the child.
Thanks guys, I think I finally get it. Idle_Mind, your idea was the easiest in my point of view, so I decided to use it. If someone else has a problem like this, here's what I've coded:
In main window form: when button is clicked, a new form appears; after closing it, label1 shows the text typed in that form
private void Button1_Click(object sender, EventArgs e)
{
LoadDataForm loaddata = new LoadDataForm("initial value");
if (loaddata.ShowDialog() == DialogResult.OK)
{
label1.Text = loaddata.textBox1.Text;
}
}
In load data form: argument passed in form's constructor appears in textBox1; textBox1's Modifiers property has to be modified to "public"
public LoadDataForm(string initvalue)
{
InitializeComponent();
textBox1.Text = initvalue;
}
private void ApplyButton_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.OK;
}
Regards,
mopsiok
My form looks something like a three-pane email client. Left side is a grid with a list of people. Top right is the current person's detail record. Bottom right is a custom control with many checkboxes displaying the current person's areas of expertise:
[x] cooking [x] window cleaning [x] brain surgery
[x] massage-therapy [x] singing [ ] random acts of vandalism
When the form is opened, the focus goes to the first person listed in the grid on the left-side of the form,, and the grid's focused_row_changed event fires. In the handler for this event I get the current person's id, then fetch detail data for that person from the database and populate the detail record, and also fetch the person's areas-of-expertise rows and set the checkboxes. All of this is working fine except when the form is first opened, because then the custom control with its many checkboxes is not yet initialized. At that point MyCustomControl is null.
if (null != MyCustomControl)
{
MyCustomControl.SetCheckedValues( datasource);
}
What is the best practice design-pattern for handling this situation? What do I do here when my control isn't fully initialized yet?
if (null != MyCustomControl)
{
MyCustomControl.SetCheckedValues( datasource);
}
else
{
// ?? Wait around for a bit and keep trying every 100ms?
}
The way I have solved this in my controls when they have had this problem is to implement ISupportInitialize.
In your control, you would put something like:
public class MyCustomControl: ISupportInitialize
{
private bool _initializing = false;
private void BeginInit()
{
_initializing = true;
}
private void EndInit()
{
_initializing = false;
}
private void SomeMethodThatWouldRaiseAnEventDuringInit()
{
if (_initializing) return;
//...
}
}
The windows forms designer checks for your control implementing the interface, and produces this code in the .Designer.cs file:
((System.ComponentModel.ISupportInitialize)(this.customControl1)).BeginInit();
///
/// customControl1
///
this.customControl1.SelectedIndex = 0; //this would normally raise the event
((System.ComponentModel.ISupportInitialize)(this.customControl1)).EndInit();
From what I understand, you are setting MyCustomControl.SetCheckedValues( datasource); when focused_row_changed event fires.
This also tends to happen when the form is just loading, which is generally not desired, because you end up with events telling things to load when, for example, a selected index is still -1.
The way I have been working around this is I have a global boolean in the form called doneLoading. It starts off false and becomes true when the Form_Shown() event gets called.
From there I just put an if(doneLoading) around any piece of code that needs to wait until the form is actually done loading before it is allowed to execute. In your case, I would do:
if(doneLoading)
{
MyCustomControl.SetCheckedValues( datasource);
}
Do your UI initialization functions in a subroutine that isn't called until after all the other UI elements are initialized, or base your calculations on the back-end values instead of the UI.
in response to comments and other posts, if you can't get anything else to work, you can add a 'refresh' button to the UI
I have a Windows form that's generated using code (including buttons and what not). On it, amongst other things, there is a text box and a button. Clicking the button opens a new Windows form which resembles an Outlook contact list. It's basically a data grid view with a few options for filtering. The idea is that the user selects a row in this home-made contact book and hits a button. After hitting that button, the (second) form should close and the email address the user selects should be displayed in the text box on the first form.
I cannot use static forms for this purpose, so is there any way to let the first form know the user has selected something on the second firm? Can you do this with events, or is there another way? Mind that I hardly know anything about delegates and forms yet.
Please advise.
Edit 1 = SOLVED
I can return the email address from the second form to the first form now, but that brings me to another question. I am generating controls and in that process I'm also generating the MouseClick eventhandler, in which the previous procedure for selecting a contact is put.
But how do I, after returning the email address in the MouseClick eventhandler, insert that information into a generated text box? Code to illustrate:
btn.MouseClick += new MouseEventHandler(btn_MouseClick);
That line is put somewhere in the GenerateControls() method.
void btnContacts_MouseClick(object sender, MouseEventArgs e)
{
using (frmContactList f = new frmContactList())
{
if (f.ShowDialog(fPrompt) == DialogResult.Cancel)
{
var address = f.ContactItem;
MessageBox.Show(address.Email1Address.ToString());
}
}
}
That appears separately in the class. So how do I put the email address into a text box I previously generated?
Forms in .Net are normal classes that inherit from a Form class.
You should add a property to the second (popup) form that gets the selected email address.
You should show the popup by calling ShowDialog.
This is a blocking call that will show the second form as a modal dialog.
The call only finishes after the second form closes, so the next line of code will run after the user closes the popup.
You can then check the property in the second form to find out what the user selected.
For example: (In the first form)
using(ContactSelector popup = new ContactSelector(...)) {
if (popup.ShowDialog(this) == DialogResult.Cancel)
return;
var selectedAddress = popup.SelectedAddress;
//Do something
}
In response to my first edit, this is how I solved it. If anyone knows how to make it more elegant, please let me know.
void btnContacts_MouseClick(object sender, MouseEventArgs e)
{
using (frmContactList f = new frmContactList())
{
if (f.ShowDialog(fPrompt) == DialogResult.Cancel)
{
var contact = f.ContactItem;
TextBox tbx = ((Button)sender).Parent.Controls[0] as TextBox;
tbx.Text = contact.Email1Address;
}
}
}
You should keep a reference to your generated TextBox in a variable (private field in your class) and use this instead of looking it up in the Controls array. This way your code would still work even if you some time in the future change the location it has in the array, and you would get a compiler message if you removed that field, but forgot to remove the code that used it.
If the second form is modal, I would recommend that rather than having the first form create an instance of the second form and use ShowModal on it, you should have a Shared/static function in the second form's class which will create an instance of the second form, ShowModal it, copy the appropriate data somewhere, dispose the form, and finally return the appropriate data. If you use that approach, make the second form's constructor Protected, since the form should only be created using the shared function.