I am building a simple text editor program. When the text has been edited a window shows up and asking if you want to save the file. This has been implemented using forms and booleans stored in the popup window.
If i press yes, no or cancel the first time the setters doesn't work. But if i press the second the setters work perfectly. Why doesn't it work the first time?
Code for the popup window.
public bool Exit { set; get; }
public bool Save { set; get; }
public bool _Open { set; get; }
case ("Yes"):
{
if (form1.Operation() == "Exit")
{
Exit = true; //Bool that tells the program to exit.
MessageBox.Show(Exit.ToString()); //This prints the correct value
}
Save = true; //Bool that states if file should be saved.
return;
}
Code for main textedit window. Example with a user pressed the exit button.
public partial class Form1 : Form
{
Form3 form3 = new Form3(); //the popup class.
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
{
if (_edited)
{
_operation = "Exit";
form3.ShowDialog();
MessageBox.Show(form3.Exit.ToString()); //Always false on first entry?
if (form3.Exit)
{
if (form3.Save)
{
Save();
}
}
else
{
e.Cancel = true;
}
}
}
}
}
In your question you say "yes, no or cancel" but in the second block of code it seems to only set Exit = true for the "Yes" case. Could this be the issue?
You could try setting breakpoints and debugging the code to be able to figure what is going on. If you remove auto properties and add backing field manually, you could also set breakpoints to see when the setters are called. You could write your Exit like this:
private bool _exit;
public bool Exit
{
get => _exit;
set
{
_exit = value;
}
}
This would allow you to set a breakpoint and see exactly when the setter is called.
If you need help with debugging, please provide some information about which code editor you are using.
Related
I am looking for a solution to not close my dialog window when there is a duplicate ID. I guess it has to do with DialogResult, however, I cannot make neither true or false work.
I am opening the "New Store"-window from a button click in my main window.
NewStoreWindow newStore = new NewStoreWindow();
newStore.ShowDialog();
As you can see on the screenshots below, you can fill in the ID and the Name. I have successfully made an input validation, so you cannot press "OK" when the 2 textboxes are empty.
When you press "OK" and the ID is already in my listview (gridview), it will give the user the below error message. However, it will also close the "New Store"-window. As said, I would like that the window is kept open until it successfully is added, so that the user can just edit the ID instead of having to open the "New Store"-window again and typing it again.
It should only close the window when it is successfully added or cancel/X is pressed.
I have tried to play around with closing-event for "New Store", but it does not seem to work.
Is this just the designed behavior? And is there a way to bypass/work around it?
CS
public partial class NewStoreWindow : Window
{
public static bool itemAlreadyAdded;
//public MainWindow mainWin = new MainWindow();
public bool IsDefault { get; set; }
public NewStoreWindow()
{
InitializeComponent();
//SetButtonState();
}
// Data Binding
public static readonly DependencyProperty SidProperty = DependencyProperty.Register("Sid", typeof(string), typeof(NewStoreWindow), new UIPropertyMetadata(String.Empty));
public string Sid
{
get { return (string)GetValue(SidProperty); }
set { SetValue(SidProperty, value); }
}
// Data Binding
public static readonly DependencyProperty SNameProperty = DependencyProperty.Register("SName", typeof(string), typeof(NewStoreWindow), new UIPropertyMetadata(String.Empty));
public string SName
{
get { return (string)GetValue(SNameProperty); }
set { SetValue(SNameProperty, value); }
}
private void cmdOK_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
MainWindow mainWin = new MainWindow();
// Check if any items exist in listview
if (MainWindow._list.Count == 0)
{
MainWindow._list.Add(new MainWindow.Data() { Sid = Sid, SName = SName });
}
else // items exist
{
itemAlreadyAdded = false; // use for boolean checking
foreach (var item in mainWin.lvStores.Items.OfType<MainWindow.Data>()) // loop through all items in listview
{
if (item.Sid == Sid) // Check if new item already exists
{
itemAlreadyAdded = true;
}
if (itemAlreadyAdded) // Show messagebox if it exists
{
MessageBox.Show("ID needs to be unique, please respecify!", "Duplicate ID",
MessageBoxButton.OK, MessageBoxImage.Error);
break;
}
}
if (!itemAlreadyAdded) // If it does not already exist, add it
{
// if it does not exist, create it from the textbox values
MainWindow._list.Add(new MainWindow.Data() { Sid = Sid, SName = SName });
// Refresh listview
mainWin.lvStores.Items.Refresh();
// Close Window
Close();
}
}
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true;
this.Hide();
}
public void Close()
{
this.Closing -= Window_Closing;
base.Close();
}
In our app we use WPF and Caliburn Micro. We use a custom WindowManager:
public class OurWindowManager : Caliburn.Micro.WindowManager
{
protected override Window EnsureWindow(object model, object view, bool isDialog)
{
var window = base.EnsureWindow(model, view, isDialog);
if (isDialog) window.ResizeMode = ResizeMode.NoResize;
window.Icon = new BitmapImage(new Uri("pack://application:,,,/NWB.ico"));
// TODO: Change to dynamic minWidth/minHeight based on window
window.MinWidth = 600;
new WindowSettingsBehavior().Attach(window);
return window;
}
}
In our code we mostly use this WindowManager like so:
public void SomeMethod()
{
var result = _windowManager.ShowDialog(new ConfirmDialogViewModel("some title",
"some text"));
if(result == true){ // if OK is pressed
// do something on OK
}
// do nothing
}
In one of my recent methods I want to do the following (in semi pseudo-code):
public void SomeOtherMethod()
{
_windowManager.ShowDialog(new ConfirmDialogViewModel("some title", "some text"));
//if window is closed without pressing any of the buttons
return; // do nothing
//if OK is pressed {
// do something on OK
}
// if Cancel is pressed: do something else
}
Unfortunately, ShowDialog also returns false if the Window is closed (even though the ShowDialog returns a Nullable bool (bool?)).
So, what I did so far is just completely remove the Close Button by making a new Window-Behavior, and I've added it to the OurWindowManager class inside the if(isDialog):
if (isDialog)
{
window.ResizeMode = ResizeMode.NoResize;
new WindowHideBarBehavior().Attach(window);
}
This works, and I now got a Window with just a title, and without a Close (X) button. Unfortunately, the window can still be closed with Alt+F4 and such. I thought about catching Alt+F4 and cancel the closing, but since Alt+F4 is standard Window behavior, I don't think users will appreciate it a lot and I find it a bit unintuitive for the users to disable it..
So, my question: How can I accomplish the pseudo-code in SomeOtherMethod mentioned above. Is there a way to get the difference between closing a Dialog or canceling a Dialog. (NOTE: As mentioned above, keep in mind we use Caliburn.Micro.WindowManager, not the default C# WPF one. Don't know if there are a lot of differences, but I guess there are at least some.)
EDIT:
I also know I can catch the closing event and cancel the closing:
window.Closing -= DisableDialogClosing;
if (isDialog)
{
window.ResizeMode = ResizeMode.NoResize;
new WindowHideBarBehavior().Attach(window);
window.Closing += DisableDialogClosing;
}
...
private static void DisableDialogClosing(object sender, CancelEventArgs e)
{
e.Cancel = true;
}
But then it also cancels the closing when I want it to close (for example when the Cancel/OK button is pressed). Maybe I can add some kind of Property-flag to this overridden Closing-EventHandler, but perhaps you guys/girls have other suggestions to accomplish the same results.
You can fulfil your requirements if you just implement your own dialog Window by extending the Window class. From inside your custom Window, you can handle the Closed event and set the Window.DialogResult property to null in that case. For the normal Ok and Cancel states, you can simply attach Click handlers to those Buttons and set the Window.DialogResult property to true and false accordingly.
private void CustomDialogWindow_Close(object sender, RoutedEventArgs e)
{
DialogResult = null;
}
private void OkButton_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
}
private void CancelButton_Click(object sender, RoutedEventArgs e)
{
DialogResult = false;
}
You could then check for the Window closed state like this:
if (CustomDialogWindow.DialogResult == null) DoSomethingUponDialogWindowClose();
You can find further helpful information in the following pages on MSDN:
Dialog Boxes Overview
Window.DialogResult Property
After #Sinatr's suggestion I've added a ClosedBy property to my ConfirmDialogViewModel:
(before):
public sealed class ConfirmDialogViewModel : Screen
{
public ConfirmDialogViewModel(string title, string message)
{
DisplayName = title;
Message = message;
}
public string Message { get; set; }
public void Ok()
{
TryClose(true);
}
public void Cancel()
{
TryClose(false);
}
}
(after):
public sealed class ConfirmDialogViewModel : Screen
{
public ClosedBy CloseReason { get; private set; }
public ConfirmDialogViewModel(string title, string message)
{
DisplayName = title;
Message = message;
CloseReason = ClosedBy.Other;
}
public string Message { get; set; }
public void Ok()
{
CloseReason = ClosedBy.Ok;
TryClose(true);
}
public void Cancel()
{
CloseReason = ClosedBy.Cancel;
TryClose(false);
}
}
public enum ClosedBy
{
Other,
Ok,
Cancel
}
And I now use it like so:
public void SomeOtherMethod()
{
var confirmDialog = new ConfirmDialogViewModel("some title", "some text");
var result = _windowManager.ShowDialog(confirmDialog);
if(result == null || confirmDialog.CloseReason == ClosedBy.Other) return;
if(result == true && confirmDialog.CloseReason == ClosedBy.Ok){
// Do something on OK
}
// Do something on cancel
}
I still kept the behavior to remove the close button, and also added window.ShowInTaskbar = false; to the OurWindowManager inside the if(isDialog).
IDE: vs 2010, c# .net , winforms
Hi, I am creating a toggle button userControl which will be having ON and OFF state, also I have created a property which set the initial state of the button, (SAME as checkbox IsChecked).
public bool Checked
{
get { return isToggleOn; }
set { isToggleOn = value;
onPropertyChanged();
}
}
private void onPropertyChanged()
{
this.BackgroundImage = isToggleOn ? Properties.Resources.toggleOnMIUI : Properties.Resources.toggleOffMIUI;
IsChecked = isToggleOn ? true : false;
}
This is working, in design time, Now in form1 I have added this control and created OnClickEvent to check the current state:
private void ucTglOverStepping_Click(object sender, EventArgs e)
{
if (ucTglOverStepping.Checked)
{
doWork = true; //do work is bool variable in form1.cs
}
else if (!ucTglOverStepping.Checked)
{
doWork = false;
}
}
Now the problem is when the toggle button is in on state it is going into else condition.
because onPropertyChanged() is executing before this ucTglOverStepping_Click(object sender, EventArgs e) function, is there any way to execute the onPropertyChanged() after the execution of ucTglOverStepping_Click() function, or any other technique to fix this issue.
PROBLEM Explanation:
Test Case 1: suppose the current state is ON
user clicked on Button -> it will go in off state (checked should become false, it should go in else condition of OnClickEvent. but checked is getting true.)
you need to call function in set;get; of property
public bool Checked
{
get
{
return isToggleOn;
}
set
{
onPropertyChanged();
isToggleOn = value;
}
}
If onPropertyChanged() only call when you set/change value or Checked properties ..
In your function seems you set property again at last line .. so it call function recursive.. you not need to set IsChecked value again
private void onPropertyChanged()
{
this.BackgroundImage = isToggleOn ? Properties.Resources.toggleOnMIUI : Properties.Resources.toggleOffMIUI;
// not need this
// IsChecked = isToggleOn ? true : false;
}
and your Question is
private void ucTglOverStepping_Click(object sender, EventArgs e)
{
if (ucTglOverStepping.Checked)
{
doWork = true; //do work is bool variable in form1.cs
}
else if (!ucTglOverStepping.Checked)
{
doWork = false;
}
}
when you get property value it run only first part it will not go to call
`onPropertyChanged()'
it will run only
public bool Checked
{
get
{
return isToggleOn;
}
}
UPDATE
it it sill confusing ..
but why you not use CheckBox1_CheckedChanged Event ?
I'm having trouble getting the value of a textbox of a popped up dialog. I've followed the advice from other StackOverflow questions that said to create a public variable in program.cs:
public static string cashTendered { get; set; }
Then I created my dialog like this:
Cash cashform = new Cash();
cashform.ShowDialog();
And when the user presses the button on the dialog, this is called:
if (isNumeric(textBox1.Text, System.Globalization.NumberStyles.Float))
{
Program.cashTendered = textBox1.Text;
this.Close();
}
else
{
MessageBox.Show("Please enter a valid amount of cash tendered. E.g. '5.50'");
}
Yet Program.cashTendered stays null. Am I doing something wrong? Thanks!
For starters your form called Cash should use an object oriented design. It should have a public property called CashEntered or something similar of type decimal instead of string. You would call the Form like so:
using (var cashDialog = new CashDialog())
{
// pass a reference to the Form or a control in the Form which "owns" this dialog for proper modal display.
if (cashDialog.ShowDialog(this) == DialogResult.OK)
{
ProcessTender(cashDialog.CashEntered);
}
else
{
// user cancelled the process, you probably don't need to do anything here
}
}
Using a static variable to hold the results of a temporary dialog is a bad practice. Here is the better implementation of a dialog:
public class CashDialog : Form
{
public decimal CashEntered { get; private set; }
private void ok_btn_Clicked
{
decimal value;
if (Decimal.TryParse(cashEntered_txt.Text, out value))
{
// add business logic here if you want to validate that the number is nonzero, positive, rounded to the nearest penny, etc.
CashEntered = value;
DialogResult = DialogResult.OK;
}
else
{
MessageBox.Show("Please enter a valid amount of cash tendered. E.g. '5.50'");
}
}
}
On your main form that you want to get the value for, you'd have some sort of code like this;
var cashTendered;
using (var frm = new Cash())
{
if (frm.ShowDialog() == DialogResult.OK)
cashTendered = frm.GetText()
}
Then on your dialog form, you'd have something like this:
public string GetText()
{
return textBox1.Text;
}
public void btnClose_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.OK;
this.Close();
}
public void btnCancel_Click(object sender, EventArgs e)
{
this.Close();
}
Alternatively, you can just perform those lines in the btnClose_Click event in the FormClosing event instead if you don't have a button for them to click to 'submit' their value.
Edit You might want to add some sort of validation on your textbox inside the btnClose event, such as:
decimal myDecimal;
if (decimal.TryParse(textBox1.Text, out myDecimal))
{
this.DialogResult = DialogResult.OK;
this.Close();
}
else
{
MessageBox.Show("Invalid entry", "Error");
textBox1.SelectAll();
}
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();