I have a C# winform project that displays a list of results based on a user's search criteria. For each item on the list, the user can open a modeless dialog box showing more details about the selected item.
Every time the user opens an instance of my details window, this code runs:
public void showDetails()
{
GetDetails route = new GetDetails();
route.myParent = this;
route.Show();
}
In order to compare details between two or more items, the user is allowed to open as many instances of this dialog box as it likes. I'd like to be able to close any and all open instances of this window when the user conducts a new search from the main form window? I've tried Googling, but no luck ... does anyone know how to do this?
Application.OpenForms is a collection of open forms owned by the application
try find all details dialogs and close them like this:
foreach(var f in Application.OpenForms.OfType<GetDetails>().ToList())
{
f.Close();
}
You don't really tell, but I assume your GetDetails is a System.Windows.Forms.Control (probably a form, a dialog box, a message box, etc).
If you look closely to your Form.InitializeComponent, you'll see that Form has a property Controls. All child controls are added to the control collection.
If you add each created route to your control collection you can ask this collection for all objects of type GetDetails and order them to close:
public void ShowDetails()
{
var route = new GetDetails();
route.myParent = this;
this.Controls.Add(route);
route.Show();
}
public void CloseAllRoutes()
{
foreach (var route in this.Controls.Where( control => control is GetDetails))
{
route.Close();
}
}
You need to be certain that when a rout is closed, or disposed or something the following code is called:
private void OnRouteClosed (object sender, ...)
{
if (sender is GetDetails)
{
this.Controls.Remove(sender);
}
}
Related
I have a parent container (form) that initializes a new TabPage with a user control inside of it (called from the Menu Strip). The Tab Control has a Context Menu that, when you right click and select "Close Selected Tab", I call a method that Removes that tab. However, it does not stop the code within that TabPage's user control from running. How can I clean this up so when I close the Tab (TabPages.Remove... etc) that it also closes the user control within that TabPage so that any background code stops executing?
The code below is what closes the tab:
The latter part just selects the next tab to the left after the tab is removed.
public static void closeCurrentTab(TabControl tc)
{
int curTabIndex = tc.SelectedIndex;
TabPage tp = tc.SelectedTab;
tc.TabPages.Remove(tp);
if(curTabIndex > 0)
{
tc.SelectedTab = tc.TabPages[(curTabIndex - 1)];
}
}
I found the issue. I found that the asynchronous processes within the child control were running astray even after the parent control (in this case being the TabPage) was disposed of.
A simple example of how I solved it is below.
Also, for more complex task chaining , I can introduce cancellation tolkens once the parent becomes null, thus cleaning up whatever needs to be once a user closes a tab hosting that control, but doesnt close the overall application.
using this.Parent and testing whether it is null or not whevenever id like to.
protected override void OnParentChanged(EventArgs e)
{
base.OnParentChanged(e);
if(this.Parent == null)
{
// Clean up
}
}
and \ or this
private async Task popUp()
{
do
{
MessageBox.Show("Im running!");
await Task.Delay(5000);
}
while (this.Parent != null);
}
I have write the ActiveX using C# to communicate with the other browser-based system b, it need pops up an dialog in an other thread because of the existing architecture. the current behavior is that the dialog can be hidden behind if i click the browser title. Is it possible to keep the pops-up dialog always on the top of browser(IE8)? Thanks in advance.
public int operation()
{
....
MyMsgBox myMsgBox = new MyMsgBox(message,title);
evt = System.Threading.AutoResetEvent(false);
Thread showDialogThread = new Thread(ShowMsgDialog);
ShowDislogThread.SetApartmentState(System.Threading.ApartmentState.STA);
showDialogThread.Start(myMsgBox);
System.Threading.WaitHanle.WaitAll(new System.Threading.WaitHandle[] {evt});
....
}
public void ShowMsgDialog(object requestObj)
{
MyMsgBox msgBox = (MyMsgbox)requestObj;
msgBox.showDialog();
evt.Set();
}
Class MyMsgBox:Form
{
public MyMsgBox(string message, string title)
{
//do initialization....
}
}
I have tried to set the TopMost of Form to 'true', then it will be always on the top of all applications. it's not meet the requirement as the pops-up dialog need be only always on the top of browser. Thanks.
I don't think that what you want will be possible.
However, you can make a div stretched across all page and set and event on mouse move to call BringToFront on your ActiveX object. That should do the trick.
I'm writing an application that uses a wizard-like series of 5 simple forms. The first form, NewProfile, is opened from a menu item on the main application, MainForm, so is a subform of MainForm. The second form, TwoProfile, is opened from a button on NewProfile. The third form, ThreeProfile is opened from a button on TwoProfile, and so on for all 5 forms. Here is the sequence:
MainForm --> NewProfile <--> TwoProfile <--> ThreeProfile <--> FourProfile <--> FiveProfile. My problem is that when any form (NewProfile, TwoProfile, ThreeProfile, FourProfile or FiveProfile) is open, I don't want a user to be able to create an instance of NewProfile.
I started out by implementing a Singleton pattern, which half-way works. It works if NewProfile is open and I go to MainForm and try to create another instance of NewProfile. It does not work if NewProfile has been destroyed, by advancing to the next form and one of TwoProfile, ThreeProfile, FourProfile or FiveProfile is open. It tells me that NewProfile.IsDisposed is true, giving me a bad reference to the Singleton instance.
What I can't figure out is how to do my logic so that NewProfile won't be created if one of TwoProfile, ThreeProfile, FourProfile or FiveProfile is open or if NewProfile itself is open.
I hope this made sense. I don't really have much code to post, except what I did for my Singleton.
private static NewProfile _instance = null;
public static NewProfile Instance
{
get
{
if (_instance == null)
{
_instance = new NewProfile();
}
return _instance
}
}
Thank you :)
As suggested in the comments, each "form" could actually be a usercontrol you swap. That way, you only have one form and multiple pages. Alternatively, you can hide the form.
If you want multiple forms instead, then you could loop through all the open forms and see if the the ones you want to check are open. If not, you can open NewProfile.
bool shouldOpenNewDialog = true;
foreach (Form f in Application.OpenForms)
{
//give each dialog a Tag value of "opened" (or whatever name)
if (f.Tag.ToString() == "opened")
shouldOpenNewDialog = false;
}
if(shouldOpenNewDialog)
np = new NewProfile();
It's untested, but it should loop through all open forms and look for any that has a Tag saying opened. If it comes across one, then it set the shouldOpenNewDialog flag to false and NewProfile won't be called.
The way that we handle this is to have a static window manager class that keeps track of the open form instances. When the user performs an action that would cause a new window to open, we first check the window manager to see if the form is already open. If it is, we set focus on it rather than creating a new instance.
Every form that is opened inherits from a base form implementation that automatically registers itself with the window manager when it is opened and removes its registration when it is closed.
Here is a rough outline of the WindowManager class:
public class WindowManager
{
private static Dictionary<string, Form> m_cOpenForms = new Dictionary<string, Form>();
public static Form GetOpenForm(string sKey)
{
if (m_cOpenForms.ContainsKey(sKey))
{
return m_cOpenForms[sKey];
}
else
{
return null;
}
}
public static void RegisterForm(Form oForm)
{
m_cOpenForms.Add(GetFormKey(oForm), oForm);
oForm.FormClosed += FormClosed;
}
private static void FormClosed(object sender, FormClosedEventArgs e)
{
Form oForm = (Form)sender;
oForm.FormClosed -= FormClosed;
m_cOpenForms.Remove(GetFormKey(oForm);
}
private static string GetFormKey(Form oForm)
{
return oForm.Name;
}
}
And you can use it as follows:
Form oForm = WindowManager.GetOpenForm("Form1");
if (oForm != null)
{
oForm.Focus();
oForm.BringToFront();
}
else
{
oForm = new Form1();
WindowManager.RegisterForm(oForm);
// Open the form, etc
}
I hope you can help me with this one. My application is monitoring a database for alerts. When a alert shows up in the database, my application will add it to the main form in a datagridview, and depending on its priority it will also create a small winform popup with the event.
In the datagridview is a button to flag the alert as "seen", it will then update the database and be it will be gone from the list. However the popup form for this event is still open.
Does anyone know how to close this form? I need a way to find a specific form between the possible multiple alert forms that are open.
The closest I have come this far is the following code:
private void closeForm(int id)
{
foreach (Form f in Application.OpenForms)
{
if (Convert.ToString(id) == f.Name)
{
this.Close();
}
}
}
Which works up until the point that it closes the correct form. then it gives an error saying "Collection was modified; enumeration operation may not execute." This kinda makes sense, but I simply can't figure out another way to do it.
I have a winform class called Alert, wich makes the new forms. As you can see they will get a standard text "Alarm" and a unique Name based on the alert ID.
Alert alertform = new Alert(id);
alertform.Name = formid;
alertform.Text = "Alarm";
alertform.Show();
I hope someone has some good ideas how I can go about this. I have searched but cannot realy find a simple and elegant way to do this.
You need to add break; to your loop after you close the form. The collection is modified when you close the form (that form is removed from the collection), thus rendering the foreach loop invalid. And should you not be calling f.Close, rather than this.Close?
private void closeForm(int id)
{
foreach (Form f in Application.OpenForms)
if (Convert.ToString(id) == f.Name)
{
f.Close();
break;
}
}
You should simply be able to store a reference to your form in the DataGridView or its DataSource then close the form using that reference. This approach is less likely to break in the future than iterating over all the application forms.
What would probably work best is to add a hidden column to the DataGridView that holds the form id, then also have a Dictionary<int, Form> which you use to get the reference to the Form you want to close.
Then you can simply get the form reference out of the dictionary and call close on it:
private void CloseAlertForm(int id)
{
Form f = dict[id];
f.Close();
dict.Remove(id);
}
Additionally you could store Action delegates instead of form references allowing you to slightly decouple the alert forms and the grid form.
just get ref. from foreach loop and close the form outside it.
private void closeForm(int id)
{
Form formtoclose=null;
foreach (Form f in Application.OpenForms)
{
if (Convert.ToString(id) == f.Name)
{
formtoclose = f;
}
}
if(formtoclose!=null)
formtoclose.Close();
}
A Close modifies your OpenForms collection, so instead of enumeration over the OpenForms collection directly, you could enumerate over a copy.
LINQ is very handy into making copies, like this:
foreach (Form f in Application.OpenForms.Where(i => Convert.ToString(id) == i.Name).ToList()) {
// Save to close the form here
}
The ToList executes the query, and stores the copy.
var names = Application.OpenForms.Select(rs=>rs.name).ToList()
foreach (string name in names)
if (Convert.ToString(id) == name)
{
Application.OpenForms[name].Close();
}
You could use the type of the forms to find them (And ToArray to create a new collection an avoid changing the collection you are enumerating).
private void CloseAlerts()
{
var openForms = Application.OpenForms.Cast<Form>();
foreach (var f in openForms.Where(f => f is Alert).ToArray())
{
f.Close();
}
}
In this case you don't need to set a name :
Alert alertform = new Alert(id);
alertform.Text = "Alarm";
alertform.Show();
I have a tabpage user control and 3 property pages for the tabpage user control which are assigned dynamically. This tabpage control is being shown inside the dialog.
The processing and filtering of data for the tabpage control is taking more time and this is resulting in a busy icon shown for more than 10 seconds before opening the Dialog.
I would like to show an empty Dialog opened and show the busy icon while the processing and filtering of data is done and finally shows inside the Dialog.
This is basically changing the order of processing.
However, I am not able to achieve this, and once the dialog is opened it waits for the user input and after giving the input only it goes to the next line. (as observed during debugging).
In the code below, the line MnemonicSelector.InitializeMnemonicSelectorParameters(parameters);
is responsible for the processing and moving that after the showdialog is resulting in object not found action when the user clicks on any item inside the property page under the dialog.
public override MnemonicSelectorResult ShowMnemonicSelector(MnemonicSelectorSearchParameters parameters)
{
MnemonicSelector.InitializeMnemonicSelectorParameters(parameters);
ResizeMnemonicSelectorIfNeeded();
SetupMnemonicDialog(m_PropertyDialog, MnemonicSelector, MnemonicSelector.Title);
DialogResult dResult = ShowFakeDialog(m_PropertyDialog, MnemonicSelector.Title);
return MnemonicSelector.Result;
}
private void ResizeMnemonicSelectorIfNeeded()
{
if ((MnemonicSelector.ClientSize.Width < 909) || (MnemonicSelector.ClientSize.Height < 620))
m_PropertyDialog.ClientSize = new System.Drawing.Size(939, 697);
}
protected void SetupMnemonicDialog(PropertiesDialogControl propertydialog, PropertyPage page, string title)
{
List<PropertyPage> pages = new List<PropertyPage>();
pages.Insert(0, page);
PropertyPage[] propertyPages = pages.ToArray();
if (title != null)
propertydialog.Text = title;
propertydialog.SetPropertyPages(new List<PropertyPage>(propertyPages));
}
public virtual DialogResult ShowFakeDialog(Control contents, string title)
{
return ShowFakeDialog(contents, title, false, "");
}
public DialogResult ShowFakeDialog(Control contents, string title, bool isCancelButtonVisible, string cancelButtonText)
{
FakeDialog fakeDialog = new FakeDialog(this, contents, title, isCancelButtonVisible, cancelButtonText);
using (fakeDialog)
{
lock (this)
{
FakeDialog previousFakeDialog = _activeFakeDialog;
_activeFakeDialog = fakeDialog;
try
{
return fakeDialog.ShowDialog();
}
finally
{
_activeFakeDialog = previousFakeDialog;
}
}
}
}
Please advice on on I can achieve the desired functionality wherein, I can show the dialog and load the property pages (processing) later.
You should use a background worker thread for this. Check this. It should help you to use background worker to separate your progress animation from your processing logic.