I always set the modifiers on my form to private,I don't like internal nor public.
Till now I used to Invoke like this:
public string Addtext
{
if(InvokeRequired)
{
Invoke((MethodInvoker)delegate
{
textbox.text = value;
});
}
else
textbox.text = value;
}
But adding such property for every member on my form is just not Object Orientated at all.
I want to create a function that will Invoke the parameter(action).I tried my best,but I failed - it requires the form members to be public or internal :(
public void PerformActionOnForm(Action<FormMain> action)
{
var form = Form.ActiveForm as FormMain;
object s = action.Clone();
if (form != null)
{
form.PerformAction(action);
}
}
public void PerformAction(Action<FormMain> action)
{
if (InvokeRequired)
Invoke(action, this);
else
action(this);
}
two problems in my code:
It requires the property I'd like to change to be != private :(
Doesn't work if the form is not on focus.
In what way is adding properties for data that needs to be accessed or set outside of the scope of the form "not object oriented at all?" This is really your only option. Code in an anonymous delegate (or any delegate, for that matter) executes in the context in which it was declared. The only facility for getting around visibility issues is reflection, and that is big smelly code smell. Create your properties and use them as appropriate.
As for your second option, I'm assuming that you want to execute this on your "main form". You have two options here: assume that there is only one instance and keep that as a static property on the class, assigning it in the instance constructor.
public partial class MainForm : Form
{
private static MainForm singletonInstance;
public static MainForm SingletonInstance
{
get { return singletonInstance; }
}
public MainForm() : base()
{
InitializeComponent();
singletonInstance = this;
}
}
public void PerformActionOnForm(Action<FormMain> action)
{
var form = MainForm.SingletonInstance;
// object s = action.Clone(); What was this for?
if (form != null)
{
form.PerformAction(action);
}
}
The other only works if all of your forms are properly "owned" and the only form with no owner is your main form. In that instance you could do this:
public void PerformActionOnForm(Action<FormMain> action)
{
var form = Form.ActiveForm.TopLevelControl as FormMain;
// object s = action.Clone(); What was this for?
if (form != null)
{
form.PerformAction(action);
}
}
Calling a UI component from a non-UI thread
Assuming you only have one message loop (99% that's the case), then:
public static class SynchronizedInvoker
{
public static void Invoke(Action action)
{
Form form = Application.OpenForms.Cast<Form>().FirstOrDefault();
if (form != null && form.InvokeRequired)
form.Invoke(action);
else
action();
}
}
Calling the code:
SynchronizedInvoker.Invoke(() => myForm.Text = myText);
Accessing private UI components
Accessing private UI members is not different from accessing other private members to .NET objects. It's in the nature of a private member not to be accessed from other objects. If you still want access, you'll eitherway have to pass the reference of the UI component to the caller or use reflection to resolve the path to the private object.
An example of passing the reference of the UI component to the caller:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
ThreadPool.QueueUserWorkItem(delegate { MyWorker.Run(button1); });
}
}
class MyWorker
{
public static void Run(Button button)
{
SynchronizedInvoker.Invoke(() => button.Text = "running");
Thread.Sleep(5000); // do some important work here
SynchronizedInvoker.Invoke(() => button.Text = "finished");
}
}
Using reflection is technically possible, but not ideal. You need to know the path to the private member, and that requires information about the internals of an object. You should then question yourself why you've made it private in the first place.
Why do you have so many possible entry points to your form that could be called from other threads? Do the thread marshaling lower down (a controller for your form might help) and you won't have to worry about boilerplate code like this.
Related
My problem is I have a variable that I need the window to send back, so I am using out to accomplish this. Here is an example of the constructor for the WPF window.
public CustomYesNo(out bool FormFilled)
{
InitializeComponent();
FormFilled = false;
}
The problem i'm having is I want it so one of the other methods in the class will be able to modify the FormFilled variable that gets sent back to the calling class like below.
private void Button_Yes_Click(object sender, RoutedEventArgs e)
{
FormFilled = true;
Close();
}
Obviously the Button_Yes_Click method does not have access to the FormFilled variable, and I am trying to figure out how I could possible change the value of the FormFilled variable from this method since this variable is only in the constructor's scope. Is what I am trying to do possible using 'out' or do I need to go another route?
Try this pattern
Calling Method:
class foo
{
public void bar()
{
DialogForm myDialogForm = new DialogForm();
myDialogForm.ShowDialog();
if (myDialogForm.DialogResult)
{
//Its true
}
}
}
Form window:
public partial class DialogForm : Window
{
public DialogForm()
{
InitializeComponent();
}
void submitButton_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = true;
}
}
The calling method in WPF is slightly different for checking the result:
bool? result = myDialogForm.ShowDialog();
if (result.HasValue && result.Value)
I have this code on each form in my app that has textboxes to prevent the textboxes that are ReadOnly from being tabbed to:
private void FrmInventory_Load(object sender, EventArgs e)
{
foreach (var txtbx in Controls.OfType<TextBox>())
{
txtbx.TabStop = (!txtbx.ReadOnly);
}
}
It would be good to only have this code in one place, but how can I do that, as each time that "external" method was called, it would touch TextBoxes on the calling form, which smells a little fishy. Is an extension method the way to go, something like:
public static bool TextboxIsReadOnly(this TextBox txtbx)
{
return txtbx.ReadOnly;
}
...and then call it like this:
foreach (var txtbx in Controls.OfType<TextBox>())
{
txtbx.TabStop = TextboxIsReadOnly(txtbx);
}
?
That doesn't seem like it's of much value - I still would have to put most of the code in each form, just as things stand now. Creating a custom textbox that is both ReadOnly and TabStop = false seems a little overkillish...
Is there a way to have this logic execute for every TextBox-containing form, without reproducing the code all throughout the project?
Having a base class that performs that step and making it the base for all your forms would work, although you would need to be careful about calling the base version of the overloaded methods.
You can create a baseForm and Inherit that form in each of your forms.
Add a new Windows Form to your project(baseForm) and create load event
public class baseForm: Form
{
public baseForm()
{
this.Load += baseForm_Load;
}
void baseForm_Load(object sender, EventArgs e)
{
var t = GetAll<TextBoxX>(this);
foreach (var txtbx in Controls.OfType<TextBox>())
{
txtbx.TabStop = (!txtbx.ReadOnly);
}
}
public static List<T> GetAll<T>(Form f1)
{
List<T> f = new List<T>();
try {
if (f1 != null) {
CheckInner<T>(f1.Controls, ref f);
}
} catch (Exception ex) {
f.Clear();
}
return f;
}
}
And finally in each form you can do like this
public partial class FrmInventory : baseForm
{
}
Just to elaborate on the Extension Method solution you hinted at.
Extension Method
public static partial class MyExtensions
{
public static void UpdateTabStop(this TextBox txtBox)
{
txtBox.TabStop = !(txtBox.ReadOnly);
}
public static void UpdateTabStop(this Form frm)
{
foreach (var txtBox in frm.Controls.OfType<TextBox>())
{
txtBox.UpdateTabStop();
}
}
}
Then, on any Form you would do this.UpdateTabStop()... you should of course do this in an event after the controls are initialized, like Load.
On my form, I have one Panel container, named "panelShowList".
On my project, i added a new class, which look like this:
class myNewClass
{
private int newPanelPos = 30;
private const int spaceBetweenElements = 30;
private const int panelWidth = 90;
private const int panelHeight = 40;
private int elementPos = 0;
private ArrayList myPanels = new ArrayList() { };
// some irelevant methods
public void addElementPanels(Panel dataPanel, Panel nextPanel)
{
myPanels.Add(dataPanel);
myPanels.Add(nextPanel);
}
public void displayPanels()
{
foreach (Panel tmp in myPanels)
{
// here i'm stuck
// i need to do something like this :
// myMainForm.panelShowList.Controls.Add(tmp);
// of course this is wrong! but i need a method to acces that control
}
}
}
Basically, I need a way to add all Panels from my ArrayList on "panelShowList" control from my form.
I tried something like this:
public void displayPanels()
{
frmMain f = new frmMain();
foreach (Panel tmp in myPanels)
{
f.display(tmp);
// where display(Panel tmp) is a function in my Form, who access
// "panelShowList" control and add a new Panel
}
}
But it only works if i do this:
f.ShowDialog();
and another form is open.
Any suggestions will be appreciated.
Maybe a bit late, but by all means, here is another approach, that's still more clean than David's approach:
You should add an EventHandler in your MyNewClass. Then you can subscribe to that event from within your form.
public partial class Form1 : Form
{
private readonly MyNewClass _myNewClass;
public Form1()
{
InitializeComponent();
_myNewClass = new MyNewClass();
_myNewClass.DisplayPanelsInvoked += DisplayPanelsInvoked;
}
private void DisplayPanelsInvoked(object sender, DisplayPanelsEventArgs e)
{
var panels = e.Panels; // Add the panels somewhere on the UI ;)
}
}
internal class MyNewClass
{
private IList<Panel> _panels = new List<Panel>();
public void AddPanel(Panel panel)
{
_panels.Add(panel);
}
public void DisplayPanels()
{
OnDisplayPanels(new DisplayPanelsEventArgs(_panels));
}
protected virtual void OnDisplayPanels(DisplayPanelsEventArgs e)
{
EventHandler<DisplayPanelsEventArgs> handler = DisplayPanelsInvoked;
if (handler != null)
{
handler(this, e);
}
}
public event EventHandler<DisplayPanelsEventArgs> DisplayPanelsInvoked;
}
internal class DisplayPanelsEventArgs : EventArgs
{
public DisplayPanelsEventArgs(IList<Panel> panels)
{
Panels = panels;
}
public IList<Panel> Panels { get; private set; }
}
In my opinion it's a better solution, because you don't need to provide a reference of the form to the MyNewClass instance. So this approach reduces coupling, because only the form has a dependency to the MyNewClass.
If you always want to "update" the form whenever a panel is added, you could remove the DisplayPanels-method and shorten the code to this:
public partial class Form1 : Form
{
private readonly MyNewClass _myNewClass;
public Form1()
{
InitializeComponent();
_myNewClass = new MyNewClass();
_myNewClass.PanelAdded += PanelAdded;
}
private void PanelAdded(object sender, DisplayPanelsEventArgs e)
{
var panels = e.AllPanels; // Add the panels somewhere on the UI ;)
}
}
internal class MyNewClass
{
private IList<Panel> _panels = new List<Panel>();
public void AddPanel(Panel panel)
{
_panels.Add(panel);
OnPanelAdded(new DisplayPanelsEventArgs(_panels, panel)); // raise event, everytime a panel is added
}
protected virtual void OnPanelAdded(DisplayPanelsEventArgs e)
{
EventHandler<DisplayPanelsEventArgs> handler = PanelAdded;
if (handler != null)
{
handler(this, e);
}
}
public event EventHandler<DisplayPanelsEventArgs> PanelAdded;
}
internal class DisplayPanelsEventArgs : EventArgs
{
public DisplayPanelsEventArgs(IList<Panel> allPanels, Panel panelAddedLast)
{
AllPanels = allPanels;
PanelAddedLast = panelAddedLast;
}
public IList<Panel> AllPanels { get; private set; }
public Panel PanelAddedLast { get; private set; }
}
and another form is open
That's because you're creating an entirely new form:
frmMain f = new frmMain();
If you want to modify the state of an existing form, that code will need a reference to that form. There are a number of ways to do this. One could be to simply pass a reference to that method:
public void displayPanels(frmMain myMainForm)
{
foreach (Panel tmp in myPanels)
{
// myMainForm.panelShowList.Controls.Add(tmp);
// etc.
}
}
Then when your main form invokes that method, it supplies a reference to itself:
instanceOfNewClass.displayPanels(this);
Though, to be honest, it's not really clear what sort of structure you're going for here. If code is modifying a form then I imagine that code should be on that form. It can certainly be organized into a class, but perhaps that can be an inner class of that form since nothing else needs to know about it.
I'm also concerned that your implementation of myNewClass requires methods to be invoked in a specific order. Any given operation on an object should fully encapsulate the logic to complete that operation. Some of that initialization logic may belong in the constructor if the object isn't in a valid state until that logic is completed.
This is all a bit conjecture though, since the object structure isn't clear here.
I have a windows forms application with some controls added to the designer. When I want to change something (LIKE) enabling a text box from inside the Form1.cs, I simply use:
textBox1.Enabled = true;
but now I have a separated class called class1.cs.
How could I enable textBox1 from a static function class1.cs?
NOTE: I did not try any code because I am totally clueless about doing this.
EDIT: Lot of edit.
public partial class Form1 : Form
{
// Static form. Null if no form created yet.
private static Form1 form = null;
private delegate void EnableDelegate(bool enable);
public Form1()
{
InitializeComponent();
form = this;
}
// Static method, call the non-static version if the form exist.
public static void EnableStaticTextBox(bool enable)
{
if (form != null)
form.EnableTextBox(enable);
}
private void EnableTextBox(bool enable)
{
// If this returns true, it means it was called from an external thread.
if (InvokeRequired)
{
// Create a delegate of this method and let the form run it.
this.Invoke(new EnableDelegate(EnableTextBox), new object[] { enable });
return; // Important
}
// Set textBox
textBox1.Enabled = enable;
}
}
This is just another method:
TextBox t = Application.OpenForms["Form1"].Controls["textBox1"] as TextBox;
You shouldn't really change UI controls in your Form from your class1, but instead create a method or a property in class1 that would tell if the textbox should be enabled or not.
Example:
// I changed the name class1 to MySettings
public class MySettings
{
public bool ShouldTextBoxBeEnabled()
{
// Do some logic here.
return true;
}
// More generic
public static bool SetTextBoxState(TextBox textBox)
{
// Do some logic here.
textBox.Enabled = true;
}
// Or static property (method if you like)
public static StaticShouldTextBoxBeEnabled { get { return true; } }
}
Then in your form:
MySettings settings = new MySettings();
textBox1.Enabled = settings.ShouldTextBoxBeEnabled();
// Or static way
textBox1.Enabled = MySettings.StaticShouldTextBoxBeEnabled;
// Or this way you can send in all textboxes you want to do the logic on.
MySettings.SetTextBoxState(textBox1);
You can pass the instance of your Form to the class
MyForm frm = new MyForm();
MyClass c = new MyClass(frm);
Then your class can take that instance and access the textbox
public class MyClass
{
public MyClass(MyForm f)
{
f.TextBox1.Enabled = false;
}
}
The design does not look OK
It is better to call the class in your form and based on the value returned, manipulate the textbox
//MyForm Class
MyClass c = new MyClass();
c.DoSomethings();
if(c.getResult() == requiredValue)
textBox1.enabled = true;
else
textBox1.enabled = false;
//MyForm Class ends here
UPDATE
public class Class1
{
public static int SomeFunction()
{
int result = 1;
return result;
}
public static void SomeFunction(out int result)
{
result = 1;
}
}
Usage
if(Class1.SomeFunction() == 1)
textBox1.Enabled = true;
else
textBox1.Enabled = false;
OR
int result = 0;
Class1.SomeFunction(out result);
if(result == 1)
textBox1.Enabled = true;
else
textBox1.Enabled = false;
You could let your class1 have an event to enable the Textbox.
public class Class1
{
public event Action<object, EventArgs> subscribe ;
private void raiseEvent()
{
var handler = subscribe ;
if(handler!=null)
{
handler(this,EventArgs.Empty);//Raise the enable event.
}
}
}
Let the class containing the TextBox subscribe to it somehow. In TextBox wrapper class
public class TextBoxWrapper
public void EnablePropertyNotification(object sender, EventArgs args)
{
TextBox1.Enabled = true ; //Enables textbox when event is raised.
}
public TextBoxWrapper()
{
class1Instance.subscribe+=EnablePropertyNotification ;
}
To access/modify a Form Element property, just write this in your outside Class.
Form1.ActiveForm.Controls["textBox1"].Enabled = true;
Where textBox1 is the variable name of TextBox.
What this actually does: Gets the active Form object's control specified by the name in string.
WARNING: Active form means the form which is currently open and focused on. If you do something else on your computer, with your minimized WindowsForm application, the Form1.ActiveForm will not get the form, instead, it will give null, which can lead to errors later. Be careful!
based on the answer from #vr_driver you can do that to avoid problems with other containers like groupbox, panels...
TextBox t = Application.OpenForms["Form1"].Controls.Find("textBox1", true)[0] as TextBox;
In this example you have a form called Main.cs and a class called MyClass:
In MyClass (Note: the name of my Form Class = 'Main'):
Main ui = new Main();
ui.toolStripProgressBarStickers.PerformStep();
In (FormName).Designer.cs so in my case Main.designer.cs change the appropriate control from 'private' to 'public':
public System.Windows.Forms.ToolStripProgressBar toolStripProgressBarStickers;
This solved it for me.
Thanks, Ensai Tankado
I had to do this at work and didn't find that any of these answers matched what I ended up doing, so I'm showing how I made it work.
First, initialize a copy of your class in your load event.
NameOfClass newNameofClass;
Then you want to bind to your class (in the load event):
textBox1.DataBindings.Add(new Binding("Enabled", newNameofClass, "textBox1Enabled"));
In your class, do the following:
private bool textBox1Enabled = false;
public bool TextBox1Enabled
{
get
{
return textBox1Enabled;
}
set
{
textBox1Enabled = value;
}
}
The false setting will initialize your textbox to being disabled
Set textBox1Enabled to true if you want to enable by default.
If you have other logic to enable/disable the textbox, simply modify the value of textBox1Enabled accordingly.
Very easy:
Create an Instance of your Form Object where want to access the Elements from.
Form1 ui = new Form1();
and now change the Form Elements to "public" - like this in the Designer Code:
...
public System.Windows.Forms.TextBox textBox6;
...
Now you can access them like this in your Code:
ui.textBox6 ...
I had the same problem. I used windows forms & Visual Studio to generate a UI in a utility with textbox, checkbox, and button controls but ALL the code was in the same class.
I'm rewriting the utility now that I know "more" OOP concepts and created actual objects and separate classes. I too had problems getting the separate classes to be able to access the form controls and any shared methods that are in the form class. I tried the various suggestions in this thread as well as other threads but none of those solutions worked for me.
What worked for me (not sure if its the right thing to do or not) was I had each class that needed to access the controls and forms methods inherit from the Form.
Here is the relevant part of the Form.cs file:
namespace Utility
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void WriteNote(string noteText, bool asHeading = false)
{
//Writes Messages to the Message box in the UI
Font font1 = new Font(this.ResultsTB.Font, FontStyle.Bold);
Font font2 = new Font(this.ResultsTB.Font, FontStyle.Regular);
if (asHeading)
this.ResultsTB.Font = font1;
else
this.ResultsTB.Font = font2;
this.ResultsTB.AppendText(noteText + "\r\n");
}
My Form contains a textbox called DirTB and a method called "WriteNote" that writes info to another textbox called ResultsTB. Here is the class (at least as far down as the first successful call of the WriteNote method from the Form:
namespace Utility
{
public class AppServerDTO : Form1
{
#region App Server attributes
//attributes listed here
#endregion App Server attributes
#region AppServerDTO Constructor
public AppServerDTO()
{
//These methods verify and set all the attributes
VerifyInstallFolder();
}//end of constructor AppServer
#endregion AppServerDTO Constructor
#region AppServerDTO class methods
public void VerifyInstallFolder()
{
string keypath = string.Empty;
string locationVerification = DirTB.Text + #"\SomeText";
for (int i = 0; i < 4; i++) //allows 3 attempts to get the install folder right
{
if (Directory.Exists(locationVerification))
{
i = 4;//Kills the loop
}
else if (!Directory.Exists(locationVerification))
{
locationVerification = DirTB.Text + #"\SomeMoreText";
}
else if (!Directory.Exists(locationVerification))
{
WriteNote("The directory listed in the Install Directoy box is not reachable.");
WriteNote("Please select the correct directory.");
WriteNote("The correct directory is the folder that contains the ApplicationUpdates & UpdateManager folders.");
WriteNote(#"i.e. C:\Somewhere or D:\Someplace\Somewhere");
var folderpath = FolderPrompt(#"C:\");
DirTB.Text = folderpath; //updates the install folder textbox to the new location
keypath = folderpath;
i++;
}
}//end for loop
}//end VerifyInstallFolder
As long as you are very careful with what you mark as public vs private, it should be ok.
This is how you should do :
I wrote the code below in my form class :
public static Form1 form = null;
private delegate void SetImageDelegate(Image image);
public Form1()
{
InitializeComponent();
form = this;
}
public static void SetStaticImage(Image image)
{
if (form != null)
form.pic1.Image = image;
}
private void setImage(Image img)
{
// If this returns true, it means it was called from an external thread.
if (InvokeRequired)
{
// Create a delegate of this method and let the form run it.
this.Invoke(new SetImageDelegate(setImage), new object[] { img });
return; // Important
}
// Set textBox
pic1.Image = img;
}
and the code below should be in anouther class :
Form1 frm= Form1.form;
frm.pic1.Image = image;
Note that i changed private static Form1 form = null; to public static Form1 form = null;
Good Luck ... Written by Hassan Eskandari :)
I'd like to know what's the best way (read most elegant) to have a single instance of a given Window per application in WPF.
I'm a newcomer to .NET and WPF and what I came up with looks pretty lame.
private static readonly Object MUTEX = new Object();
private static AboutWindow INSTANCE;
public static AboutWindow GetOrCreate() {
lock (MUTEX) {
if (INSTANCE == null) {
INSTANCE = new AboutWindow();
}
INSTANCE.Show();
return INSTANCE;
}
}
private AboutWindow() {
InitializeComponent();
}
private void AboutWindow_Closed(object sender, EventArgs e) {
// the Closed events are handy for me to update values across
// different windows.
lock (MUTEX) {
INSTANCE = null;
}
}
Thing is... this looks like utter crap. There must be some way to achieve the same goal in a much more elegant way, right?
PS: I'm often using the Closed event to change values in other open windows. For instance I have the SettingsWindow with the "Account" button. When I push that button, the AccountWindow pops up. When I close AcountWindow, I want something in the SettingsWindow to change (a label). Hence the constant creation of windows.
Besides, Close is something you always have to deal with because of the X button on the window frame...
there are probably better ways to do this, but here is a relatively simple way....
put a static bool on your window class to flag if its open or not. then, in the load() event set it to true, and on the close event set it to false. Then, in the code that opens the window, check the flag.
here is some pseudo-code to give you an idea...
public class AboutWindow
{
public static bool IsOpen {get;private set;}
onLoadEvent(....)
{
IsOpen = true;
}
onUnloadEvent(...)
{
IsOpen = false;
}
}
public void OpenAbout()
{
if ( AboutWindow.IsOpen ) return;
AboutWindow win = new AboutWindow();
win.Show();
}
If you truly need to enforce a single instance of a window, then a static instance (some flavor of what you have) with a factory creation method is certainly a viable option, much like a single DataContext instance when working with a database.
You could also write your own WindowManager class, although that seems like overkill, and will essentially be the same thing (except the Factory methods would be in a single class).
However, re-reading your post, I wonder if this is a case of missing the forest for the trees. Your mentioning of your SettingsWindow, which in turn calls AccountWindow, makes me think that you should simply be using ShowDialog(). This opens a window modally, meaning that there can be no interaction with the calling window (or any other window in your application). You simply set a property in that dialog, set the DialogResult to true when the OK button is pressed, and read that property in the parent window.
Basically, you just use the ShowDialog like this. I am leaving out a lot of the implementation details, as far as binding vs. hard-coding to controls. Those details aren't as important as just seeing how ShowDialog works.
For simplicity, assume that you have a class called MyAppOptions that, well, reflect the options of your application. I will leave off most of the implementation details of this for simplicity, but it would likely implement INotifyPropertyChanged, have methods and fields and properties, etc.
public class MyAppOptions
{
public MyAppOptions()
{
}
public Boolean MyBooleanOption
{
get;
set;
}
public String MyStringOption
{
get;
set;
}
}
Then, let's make this simple, and assume that you want to show an Options dialog when you press a button on some window. Furthermore, I will assume that there are variables that have been set with your options, which were loaded at startup.
void btnOptions_Click(object sender, RoutedEventArgs e)
{
MyAppOptions options = new MyAppOptions();
options.MyBooleanOption = mSomeBoolean;
options.MyStringOption = mSomeString;
OptionsDialog optionsDialog = new optionsDialog(options);
if (optionsDialog.ShowDialog() == true)
{
// Assume this function saves the options to storage
// and updates the application (binding) appropriately
SetAndSaveOptions(optionsDialog.AppOptions);
}
}
Now assume that the OptionsDialog is a window you've created in your project, and it has a CheckBox on it related to MyBooleanOption and a TextBox for MyStringOption. It also has an Ok button and a Cancel button. The code-behind will likely use Binding, but for now we'll hard code the values.
public class OptionsDialog : Window
{
public OptionsDialog(MyAppOptions options)
{
chkBooleanOption.IsChecked = options.SomeBooleanOption;
txtStringOption.Text = options.SomeStringOption;
btnOK.Click += new RoutedEventHandler(btnOK_Click);
btnCancel.Click += new RoutedEventHandler(btnCancel_Click);
}
public MyAppOptions AppOptions
{
get;
set;
}
void btnOK_Click(object sender, RoutedEventArgs e)
{
this.AppOptions.SomeBooleanOption = (Boolean) chkBooleanOption.IsChecked;
this.AppOptions.SomeStringOption = txtStringOption.Text;
// this is the key step - it will close the dialog and return
// true to ShowDialog
this.DialogResult = true;
}
void btnClose_Click(object sender, RoutedEventArgs e)
{
// this will close the dialog and return false to ShowDialog
// Note that pressing the X button will also return false to ShowDialog
this.DialogResult = false;
}
}
This is a pretty basic example as far as implementation details. Search online for ShowDialog for more details. The important keys to remember are:
ShowDialog opens a window modally,
meaning it is the only window in your
application that can be interacted
with.
Setting DialogResult to true
will close the dialog, which can be
checked for from the calling parent.
Setting DialogResult to false will
also close the dialog, in which case
you skip updating the values in the
calling window.
Pressing the X button
on the window automatically sets the
DialogResult to false
You can have public properties in the dialog window that can be set before doing the ShowDialog, and can get values from after the dialog disappears. It will be available while the dialog is still in scope.
The following extends on the above solution to reshow the window if it is already open. In this case it is a help window.
///<summary>
/// Show help from the resources for a particular control by contextGUID
///</summary>
///<param name="contextGUID"></param>
private void ShowApplicationHelp(string contextGUID = "1")
{
if (HelpWin != null)
{
if (HelpWin.IsOpen)
{
HelpWin.BringToFront();
return;
}
}
HelpWin = new MigratorHelpWindow();
HelpWin.Owner = Application.Current.MainWindow;
HelpWin.ResizeMode = ResizeMode.CanResizeWithGrip;
HelpWin.Icon = new Image()
{
Source =
new BitmapImage(
new Uri(
"pack://application:,,,/ResourceLibrary;component/Resources/Images/Menu/Help.png",
UriKind.RelativeOrAbsolute))
};
HelpWin.Show();
HelpWin.BringToFront();
}
This code is all in a viewmodel (MVVM) associated with the window. It is called by an ICommand hooked to a button on the window (naturally, it shows a question mark!!)
The following property is involved (in this case it is a Telerik RadWindow but it can be any window object, and you can probably also just store the window handle but using this property permits manipulation of the object more smoothly e.g. HelpWin.BringToFront() as in the above example...
...
...
private Telerik.Windows.Controls.RadWindow **HelpWin**
{
get;
set;
}
...
...
In the window itself (WPF window)
///<summary>
/// Flag to indicate the window is open - use to prevent opening this particular help window multiple times...
///</summary>
public static bool IsOpen { get; private set; }
...
...
...
private void HelpWindowLoaded(object sender, RoutedEventArgs e)
{
IsOpen = true;
}
private void HelpWindowUnloaded(object sender, RoutedEventArgs e)
{
IsOpen = false;
}
and in the view Xaml
...
...
DataContext="{Binding Path=OnlineHelpViewModelStatic,Source={StaticResource Locator}}"
RestoreMinimizedLocation="True"
**Loaded="HelpWindowLoaded" Unloaded="HelpWindowUnloaded"** >
Here's an alternative approach that doesn't require a static property to set and update in each of your window:
public static bool IsWindowInstantiated<T>() where T : Window
{
var windows = Application.Current.Windows.Cast<Window>();
var any = windows.Any(s => s is T);
return any;
}
Usage:
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
if (IsWindowInstantiated<SettingsWindow>())
return;
var window = new SettingsWindow();
window.Show();
}
How about using a Singleton?
public class MyWindow : Window {
private static MyWindow instance;
public static MyWindow Instance
{
get
{
if (instance == null)
{
instance = new MyWindow();
}
return instance;
}
}
}
Then just use
MyWindow.Instance.Show() and MyWindow.Instance.Hide()
I found this because I am trying to ensure my users do not open multiple instances of an rtsp stream window. I like Aybe's answer, it works well and is easy to understand.
I have built on it a bit as I wanted to bring the window into focus if it is open.
Here is my code:
public static void OpenWindow<T>() where T: Window
{
var windows = System.Windows.Application.Current.Windows.Cast<Window>();
var any = windows.Any(s => s is T);
if (any)
{
var win = windows.Where(s => s is T).ToList()[0];
if (win.WindowState == WindowState.Minimized)
win.WindowState = WindowState.Normal;
win.Focus();
}
else
{
var win = (Window)Activator.CreateInstance(typeof(T));
win.Show();
}
}
I am also quite new to C# and WPF so I am sure this can be improved even more.
Call it using
OpenWindow<SettingsWindow>();
public static void ShowWindow<T>() where T : Window, new()
{
var existingWindow = Application.Current.Windows.OfType<T>()
.SingleOrDefault();
if (existingWindow == null)
{
new T().Show();
return;
}
existingWindow.WindowState = WindowState.Normal;
existingWindow.Activate();
}
Usage:
ShowWindow<AboutWindow>();
When windows is created then Window.IsLoaded == true. My implementation of singleton windows is:
public partial class MySingletonWindow : Window
{
private static MySingletonWindow _instance = null;
private MySingletonWindow()
{
InitializeComponent();
}
public static MySingletonWindow Show(System.Windows.Window owner = null)
{
// On First call _instance will be null, on subsequent calls _instance will not be null but IsLoaded is false if windows was closed.
if (_instance == null || !_instance.IsLoaded)
_instance = new MySingletonWindow();
_instance.Owner = owner; // Optional owner
_instance.Show(); // Display the window
_instance.Focus(); // Bring it to front
return _instance; // Return instance if user needs it
}
}
Simply show windows using this call:
MySingletonWindow.Show(ParentWindow);
OR
MySingletonWindow.Show();