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
Related
I am new to WPF and Coding in general, I am trying to create a small UI to read and write data on .txt files. All the reading part functions works well but now I am having trouble making a small UI to display the information found in the .txt files.
My problem is more with how button clicks work in WPF. All the data is stored in the forms of object of class Static_Data_Scheme and Dynamic_Data_Scheme inside the program, these class are just simple collection of Dictionaries Objects. Part of my data is 'static' meaning it will be stored in .txt files that won't change location and so can be loaded when the program is started, and another part of my data is 'Dynamic' meaning it can be stored anywhere and the file path can be changed using a file explorer in the program. Important thing to note, the Static_Data_Scheme is needed to generate the Dynamic_Data_Scheme.
My initial way of handling it when I made the program to test it out was to generates both Data Scheme with the same button press called load, but since the static dictionaries can be loaded right at the start of the program I want to try and add that method to the MainWindow instead and only have the program generates the Dynamic_Data_Scheme when I press the load button, but I'm unable to find any documentation on how to add arguments to the click method of the button.
Current code that works but that I don't like due to the fact that Static_Data_Scheme.Start method is called each time the load button is pressed and could instead be loaded only once when the program starts :
public MainWindow()
{
InitializeComponent();
}
private void Save_Loader_Click(object sender, RoutedEventArgs e)
{
Static_Data_Scheme static_Data_Scheme = new Static_Data_Scheme();
static_Data_Scheme = static_Data_Scheme.Start();
Dynamic_Data_Scheme dynamic_Data_Scheme = new Dynamic_Data_Scheme();
Save_Parser.Parse_Save(#"file_path", static_Data_Scheme, ref dynamic_Data_Scheme);
}
What I wanna try to achieve is something like that :
public MainWindow()
{
InitializeComponent();
Static_Data_Scheme static_Data_Scheme = new Static_Data_Scheme();
static_Data_Scheme = static_Data_Scheme.Start();
}
private void Save_Loader_Click(object sender, RoutedEventArgs e)
{
Dynamic_Data_Scheme dynamic_Data_Scheme = new Dynamic_Data_Scheme();
Save_Parser.Parse_Save(#"file_path", static_Data_Scheme, ref dynamic_Data_Scheme);
}
But this doesn't work due to the fact that the Save_Parser.Parse_Save method lack the static_Data_Scheme variable and I can't add it to the Save_Loader_Click method either.
So my question is how do I tell my ave_Loader_Click method to get the static_Data_Scheme from the program?
You almost had it, just move the variable outside of your method:
Static_Data_Scheme static_Data_Scheme = new();
public MainWindow()
{
InitializeComponent();
/* static_Data_Scheme = ???? */static_Data_Scheme.Start();
}
private void Save_Loader_Click(object sender, RoutedEventArgs e)
{
var Data_Scheme = new Dynamic_Data_Scheme();
Save_Parser.Parse_Save(#"file_path", static_Data_Scheme, ref dynamic_Data_Scheme);
}
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
}
There is a MenuItem click event MainMenu_Define_Material which opens a window called Material. I get info from a textbox called txt_density and I save that info in density and return to main window via OK-button having click event Material_btn_OK_Click.
My question is, how I can avoid passing the last session values every time I open the window Material?
I want, once density is set, every time I open Material window I want to see the values of previous session.
private void MainMenu_Define_Material(object sender, RoutedEventArgs e)
{
newWin_material = new Material();
newWin_material.btn_OK.Click += new RoutedEventHandler(Material_btn_OK_Click);
if (density != -1)
{
newWin_material.txt_density.Text = density.ToString();
}
newWin_material.ShowDialog();
}
private void Material_btn_OK_Click(object sender, RoutedEventArgs e)
{
density = System.Convert.ToSingle(newWin_material.txt_density.Text);
newWin_material.Close();
}
Not very clear where that density comes from, but you can insert that field in uour data class what you can hold on data layer or may be like ObjectDataProvider for modelview.
There are a lor of different solutioms our there. The basic idea is:
Define common, shared data storage and keep there alll values you want to share between different windows.
Define a data class. I used here a static class with a static member Desnity.
public static class DataClass
{
public static double Desnsity;
}
After in any window you're able to access that field (read/write), like
DataClass.Density
Hope this is clear.
There is no concept of Session in WPF. You can try creating a static variable to do this or a app config value or pass a parameter via the constructor.
If you are going to new it ( e.g. new Material(); ) then it is going to start with zero information. That is what new does. What is wrong with passing the value in the ctor?
Try
MainWindow
{
private newWin_material = new Material(); // just new it ONCE
// this may need to be in MainWindow ctor.
newWin_material.btn_OK.Click += new RoutedEventHandler(Material_btn_OK_Click);
I have an application with a Listbox with files and a menu. When I right-click an item from my listbox I have a menu for example Send. When I press 'Send' I want another window to open (I already have the new window) and in the new window I want to have the item-path that I selected (I have this path in the main window).
private void MenuItemSend_Click(object sender, RoutedEventArgs e)
{
if (listBoxFiles.SelectedIndex == -1)
{
return;
}
string filePath = (listBoxFiles.SelectedItem).ToString(); --- my file path
StatisticsWindow sForm = new StatisticsWindow();
sForm.ShowDialog(); -- open the new window
}
How can I do it ?
Thanks
Why don't you create a constructor for the window?
Instead of
new IpStatisticsWindow();
this:
new IpStatisticsWindow(filePath);
// In the IpStatisticsWindow class
public IpStatisticsWindow(string path)
{
//do something with path
}
You could of course also create a property or a method which handles it, then you can pass it there, e.g.
IPsForm.Path = filePath;
IPsForm.HandlePath(filePath);
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();
}