C# & WPF - Understanding windows & classes, & passing window as argument in method - c#

Newbie question! I have "MainWindow" and "EditSettings", which are classes. As far as I can gather, (please correct me if wrong) they are both classes derived from the Window class:
public partial class MainWindow : Window
When I actually want to create a window, I create an instance of the derived class, like so:
EditSettings winEditSettings = new EditSettings();
This means that winEditSettings is an instance of class EditSettings, which is derived from Window.
If I wanted to then write a method which accepts ANY of my windows as an argument, what would I write as the argument? I initially had:
protected void OpenWindowOnce(Window win)
{
//This method tells the user whether the window is already open or not
foreach (Window n in Application.Current.Windows)
if (n == win)
{
MessageBox.Show("The window is already open");
}
else
{
MessageBox.Show("You have not opened that window yet");
win.Show();
}
It seems as though the IF part of the statement is not working - it keeps telling me that the window is not open yet, and then opening a new version of it. I called the method as follows:
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
EditSettings winEditSettings = new EditSettings();
OpenWindowOnce(winEditSettings);
}
That's all I have in my program so far - I'm just trying to write a method that prevents windows from being opened more than once. Running this code gives me:
"You have not opened that window yet" --> The window "winEditSettings" is opened.
If I press the button again, I get two messages saying "You have not opened that window yet" and it opens another instance of winEditSettings.
EDIT
How can I rewrite my OpenWindowOnce method to accept any instance of any window class? I want to be able to pass winEditSettings, or winMainWindow, or winAbout as parameters.

Since you pass instances to your method, you have to pass the instance of the opened window to see if it is opened.
Otherwise, just try comparing their types:
void ShowWindowOnce(Window window)
{
foreach (Window n in Application.Current.Windows)
{
if (typeof(n) == typeof(window)
{
// A window with the given type is opened, no need to show another one so return
return;
}
}
// No open form with that type was open, show it
window.Show();
}

You pass the instanced Window to your method, so you need to know if the Window is loaded or not.
private void OpenOnce(Window win)
{
foreach (Window n in Application.Current.Windows)
{
if (n.GetType() == win.GetType())
{
if (n.IsLoaded)
{
MessageBox.Show("The window is already open");
return;
}
}
}
win.Show();
}

Related

How to open a file with an executable already open?

i have a C#/WPF project that represents a dbf editor that allows you to open files in 3 ways:
- with apposit button in toolbar
- with drag & drop
- with double click on file
Now i use a TabControl that contain every dbf open.
I can handle it with my internal button and drag add item to container.
if I open a file with the double click and there is an open instance I would like to add it to the container and instead open a new instance.
My code :
App
public partial class App : Application
{
private static Editor mainWindow = null;
protected override void OnStartup(StartupEventArgs e)
{
Editor mainWindow = new Editor(e.Args);
mainWindow.Show();
}
}
Editor:
public partial class Editor : Window
{
ChooseMessage.Choose choose;
public Dictionary<int, DBFStructure> ds;
string DbfName;
private string[] OldNew;
public Editor(string[] e)
{
InitializeComponent();
ds = new Dictionary<int, DBFStructure>();
OldNew = new string[2];
choose = ChooseMessage.Choose.OK;
if (e.Length > 0)
if (File.Exists(e[0]) && e[0].EndsWith(".dbf", StringComparison.InvariantCultureIgnoreCase))
EffOpen(e[0]);
}
private void dbf_Drop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop, true) == true)
{
string filename = ((string[])e.Data.GetData(DataFormats.FileDrop, true))[0];
if (File.Exists(filename) && filename.EndsWith(".dbf", StringComparison.InvariantCultureIgnoreCase))
EffOpen(filename);
}
}
....
}
I apologize for code display but I can not set it correctly.
my problem is to intercept the opening of a dbf from the last open editor instance and add it to the controltab, otherwise create a new instance.
P.S. EffOpen(filename) represents the method that, by passing the fil name, loads it and adds it to the container
Thanks to all
When you open a file with a double click, a new application instance will be started by Windows. This is by design, you cannot change this.
In this new instance you have no direct references to the objects of your first application instance, so you cannot just add the file being opened to the TabControl of the first instance.
You need to implement it like that:
since you can open files via double click, your application can handle files using the command line arguments - that's good
you need a way to check whether there is an instance already running - use a Mutex with a dedicated and unique name to do that; try creating a Mutex on app startup and check the out parameter of the Mutex(bool, string, out bool) constructor - if it's false, then there is an instance already
you need a way to tell the already running instance that it needs to open a file that has been passed as the command line argument to the second instance; use any of the inter-proc methods that better suits you: a NamedPipe would be a pretty simple way

C# WPF - Want to open multiple windows at once, but only one instance of each window

I am new to WPF and have been hunting for an answer, surely this is not difficult?
I have created a main window with links to multiple windows, and I want them to run modelessly alongside one another, but I don't want to open multiple instances of the SAME window.
In simple terms, I can have Windows A, B, C open at once, but not Windows, A, A, B, C, C.
I need to implement a check for the window I'm trying to open (in this case, EditSettings).
If open - activate it
if not open, open it.
I have the following code in Main, which is not working.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
EditSettings winEditSettings = new EditSettings();
string isOpen = null;
if (isOpen == "true")
{
winEditSettings.Activate();
}
else
{
winEditSettings.Show();
isOpen = "true";
}
}
}
Now I know what's wrong with this logic - every time I press the button to open EditSettings, it's setting isOpen to null again. If I don't set a value to isOpen, the If condition breaks.
I could initialise the variable 'isOpen' as a public variable outside the MenuItem_Click method, but then I think I would need an isOpen variable for each window I create!! Surely there is a better way?
The other option I tried is:
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
EditSettings winEditSettings = new EditSettings();
if (winEditSettings.IsLoaded)
{
winEditSettings.Activate();
}
else { winEditSettings.Show(); }
I can't figure out why this isn't working, I tried isVisible, isLoaded, isActive - nothing is stopping the window from opening more than once. Thank you for the help!
There are people who'll perhaps throw a fit at the idea, but whenever I've needed to do this, I made the child window objects part of the application. Then, in your MenuItem_Click(), test if winEditSettings is null, instead.
It's still a member variable for each window (like your provisional isOpen solution), but having the window objects available can have advantages later, if you need to bridge information between the windows. In my cases, we wanted to be able to close all the child windows together, which (most trivially) meant keeping track of those objects in a central location.
Alternatively, if you want the setup completely decoupled, you could take a singleton-like approach and put the logic into your child window classes. Specifically, you could call EditSettings.Activate and let the class keep track of whether a window needs to be created or the existing window merely Show()n.
If I were handed your code to rewrite, I'd move it something like this:
private static EditSettings winEditSettings = null;
public static void WakeUp()
{
if (winEditSettings == null)
{
winEditSettings = new EditSettings();
}
winEditSettings.Activate(); // This may need to be inside the block above
winEditSettings.Show();
}
Both of those are part of the class (static), rather than an instance. Your application object therefore calls EditSettings.WakeUp() inside the original MenuItem_Click(), and never actually sees the child window, itself.
If you change your mind about the decoupled architecture later, by the way, you can add a get accessor to your winEditSettings and keep everybody fairly happy.
if (_adCst == null)
{
_adCst = new AddCustomerPage();
_adCst.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
_adCst.WindowState = System.Windows.WindowState.Normal;
_adCst.ResizeMode = System.Windows.ResizeMode.NoResize;
_adCst.Activate(); // This may need to be inside the block above
_adCst.Show();
}
else
{
if (!_adCst.IsLoaded == true)
{
_adCst = new AddCustomerPage();
_adCst.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
_adCst.WindowState = System.Windows.WindowState.Normal;
_adCst.ResizeMode = System.Windows.ResizeMode.NoResize;
_adCst.Show();
}
_adCst.Activate();
}
My suggestion would be that you set some form of a counter. This will prevent more than one instance of the window being opened.
int windowOpen = 1;
private void button_Click(object sender, RoutedEventArgs e)
{
if (windowOpen == 1)
{
WindowA winA = new WindowA();
winA.Show();
windowOpen++; //increments windowOpen by 1, windowOpen will now = 2
}
else if (windowOpen > 1)
{
MessageBox.Show("Window is already open");
}
}
I hope this helps.
For anyone else with this question, I have found another solution - which works except that it doesn't manage to bring the open window to the front (Activate). It does, however, prevent opening the same window more than once.
foreach (Window n in Application.Current.Windows)
if (n.Name == "winEditSettings")
{ winEditSettings.Activate(); }
else
{ winEditSettings.Show(); }
Can anyone speculate on why the window is not brought to the front, with Activate()?
EDIT
For others with this question, placing the winEditSettings.Activate() outside of the foreach loop does everything I'm trying to achieve:
foreach (Window n in Application.Current.Windows)
if (n.Name == "winEditSettings")
{ }
else
{ winEditSettings.Show(); }
winEditSettings.Activate();
This will stop multiple instances of the same window from opening, and will bring the window to the front if the user attempts to reopen it.

Have only one instance of a subform open at a time c#

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
}

C# - Program without a Window

I'm wondering whether it is possible to 'turn off' my main Window from loading automatically when my program starts with a command-line argument (i.e. when a file name is passed). The problem I have is that my program loads when a file associated with it is clicked, but does so by opening another main window and using that. The problem I have is that the program still launches the MainWindow afterwards, thus opening two Windows, one with the file contents and one that is empty.
How do I prevent the blank Window? As I see it, I either stop it from opening the main Window, close the main Window or make the program pass the file to the main Window. My problem is that I don't know which of these would be the best or how to to do it.
This is the code:
protected override void OnStartup(StartupEventArgs e)
{
if (e.Args != null && e.Args.Count() > 0)
{
this.Properties["ArbitraryArgName"] = e.Args[0];
}
base.OnStartup(e);
if (Application.Current.Properties["ArbitraryArgName"] != null)
{
string fname = Application.Current.Properties["ArbitraryArgName"].ToString();
MainWindow mw = new MainWindow();
mw.Show();
mw.readVcard(fname);
Application.Current.Windows.
}
}
EDIT:
My solution is at the bottom.
I believe you can add a separate class with its own Main method and set that to be the entry point of your executable. Then you can parse the method arguments there and either bring up the main window or not.
(I'm assuming this is a WPF app - it's simpler in a WinForms app as you can modify the original Main method directly.)
I assume you use WPF? You'll want to replace the entry point (Main) that WPF supplies for you. Then, you can start WPF or not depending on the command-line arguments. See this question for more info:
Replacing the WPF entry point
Remove the WindowUri from APP.XAML page.
That will not show any window. Also, add your logic on app() constructor or startup event.
I'd rewrite your code as follows:
protected override void OnStartup(StartupEventArgs e)
{
// start application window
MainWindow mw = new MainWindow();
mw.Show();
// store argument and read card info
if (e.Args != null && e.Args.Count() > 0)
{
this.Properties["ArbitraryArgName"] = e.Args[0];
string fname = Application.Current.Properties["ArbitraryArgName"].ToString();
mw.readVcard(fname);
}
}
This assumes that the method MainWindow.readVcard(string) simply loads data into the current instance.
Hi everyone and thanks for getting back to me, sorry I've not come back sooner. Part of what Nate said was correct in that I needed to call my Window earlier and then, if there was the command-line argument, parse the file name. The issue as I saw it was that it still started up a main Window afterwards because that was set as my startup, So I used the information suggested by Qwertie to alter my app.xaml, which meant that it pointed to a different startup, which in turn meant that the Window wasn't opened unnecessarily.
In ' App : Application ' class in App.xaml.cs:
private void OnStartUp(object sender, StartupEventArgs e)
{
OnStartup(e);
}
protected override void OnStartup(StartupEventArgs e)
{
MainWindow mw = new MainWindow();
if (e.Args != null && e.Args.Count() > 0)
{
this.Properties["ArbitraryArgName"] = e.Args[0];
}
//base.OnStartup(e);
if (Application.Current.Properties["ArbitraryArgName"] != null)
{
string fname = Application.Current.Properties["ArbitraryArgName"].ToString();
mw.Show();
mw.readVcard(fname);
//Application curApp = Application.Current;
//curApp.Shutdown();
}
else if (e.Args.Count() == 0)
{
mw.Show();
}
}
In App.xaml:
<Application x:Class="Vcardviewer.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="OnStartUp"
>
<Application.Resources>
</Application.Resources>
</Application>
<!--StartupUri="MainWindow.xaml"-->
Thanks again to everyone for their answers. Regards to you all.
I edit the app.xmal to remove the start URL. I then edit the app.xaml.cs and add a constructor for App and do my processing there - I use "Shutdown()" to close the application.
You can open windows as needed. When I launch other windows, I use the OnStartup event to do it...

How can I make sure only one WPF Window is open at a time?

I have a WPF window that I am launching from inside of a winform app. I only want to allow once instance of that WPF window to be open at a time, and not warn that user if they try to open it again.
I am having a problem however trying to search for that WPF window being open because the window is being launched from a winform. What I normaly do is when searching for a winform, I search for any instances of that winform existing in the Application.Current.OpenForms, and when in WPF I search for Application.Current.Windows
The problem I have is that System.Windows.Application.Current is null when launched from inside of a winform, so I can't search for the WPF window that way. Is there any better way of searching for an existing instance of an open window?
My Code:
if (System.Windows.Application.Current != null)
{
foreach (System.Windows.Window win in System.Windows.Application.Current.Windows)
{
if (win is frmCaseWpf)
{
MessageBox.Show("You may have only one active case open at a time.", "Open Case",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
win.WindowState = System.Windows.WindowState.Normal;
win.Focus();
win.Activate();
return;
}
}
}
Instead of searching the static application objects, you could instead just track this within your window, with a single static variable. Just keep a variable in the window:
private static frmCaseWpf openWindow = null; // Assuming your class name is frmCaseWpf
When you create a window, either in the initialize routines, or OnLoaded, depending on how you want it to work..:
partial class frmCaseWpf {
public frmCaseWpf {
this.OnLoaded += frmCaseWpf_OnLoaded;
}
private void frmCaseWpf_OnLoaded(object sender, RoutedEventArgs e)
{
if (this.openWindow != null)
{
// Show message box, active this.openWindow, close this
}
this.openWindow = this;
}
}
If you want this window to be reusable, make sure to set this.openWindow = null; when you close the window, as well.
Here's something that's working for me.
private About aboutWin;
private void AboutOpenClicked(object sender, RoutedEventArgs e)
{
if(aboutWin == null)
{
aboutWin = new About();
aboutWin.Closed += (a, b) => aboutWin = null;
aboutWin.Show();
}
else
{
aboutWin.Show();
}
}
It would be better make the frmCaseWpf class a singleton. That way you can't create another instance
Rather than try to search for a Window instance, many people use a session- (or system-) wide "Mutex" or a Mutual Exclusion lock. I was going to rewrite one for you, but I found a good codeproject article demonstrating the technique. It's not complex and very simple.
http://www.codeproject.com/KB/cs/SingleInstanceAppMutex.aspx?msg=2908697
Sneak peek:
[STAThread]
static void Main()
{
bool onlyInstance = false;
Mutex mutex = new Mutex(true, "UniqueApplicationName", out onlyInstance);
if (!onlyInstance) {
return;
}
Application.Run(new MainForm);
GC.KeepAlive(mutex);
}
Hope this helps.
(edit: of course you'll have to modify this slightly for your particular use-case, but it demos the general idea)
I am not really a 'proper' programmer, however I have achieved this in a WPF application (not from a winforms one) by using the following:
Dim wdwDetails As New detailsNew()
Private Sub openNewDetails(ByVal recordID As String)
wdwDetails.Owner = Me
wdwDetails.recordID = recordID
wdwDetails.WindowStartupLocation = Windows.WindowStartupLocation.CenterOwner
wdwDetails.Show()
End Sub
Essentially because I am creating the window object outside of the sub that opens it, there will only be a single window. Any new call to the window open sub will use the same object. But I guess that is what Thomas is referring to also.
Like I said, not sure if this will help you or not though.
You can use XXXwindown.isLoad to check if window is loaded before you create a new window:
if ( !ChildWindow.IsLoaded)
{
childWindow= new ChildWindow();
childWindow.Show();
}

Categories

Resources