My problem deals with the following 3 forms:
MainWindow.cs
SettingsWindow.cs
AuthenticationWindow.cs
Settings window contains information like "Ask for password during startup or not".
I call the Authentication Window from Settings Window in order to remove password (when the password is set).
I call the Authentication Window also during startup (when the password is set).
My Authentication Window interacts with the settings window using a Static variable(To say whether the authentication is successful or not).
But, in order to reuse the same code (that is, to call the same authentication window during startup), I am unable to tell the MainWindow whether the authentication is successful or not.
However, I must some how reuse the code.
My question is: Is it possible to notify the Child Window about whom the parent window is? If yes, Sample code please...
Hope my question is clear.
Kindly help!
I assume that Authentication Window is being used with ShowDialog() along the lines of:
AuthenticationWindow auth = new AuthenticationWindow();
if (auth.ShowDialog(this) == DialogResult.Ok)
{
// we know it was successful
}
Then within AuthenticationWindow when you've had success you'll call:
DialogResult = DialogResult.Ok;
Close();
to get the feedback above, or to signal that it failed by
DialogResult = DialogResult.Cancel;
Close();
Alternatively, you could set a property on AuthenticationWindow:
class AuthenticationWindow : Form
{
public bool Success { get; set;}
}
and set the value of Success appropriately from within the AuthenticationWindow code.
Lastly, if you want immediate feed back to be sent to your other windows, consider implementing an event:
class AuthenticationWindow : Form
{
public event Action<bool> SignalOutcome;
private OnSignalOutcome(bool result)
{
Action<bool> handler = SignalOutCome;
if (handler != null) handler(result);
}
}
Then you will have to subscribe to that event where you call the Authentication window:
AuthenticationWindow auth = new AuthenticationWindow();
auth.SignalOutcome += (outcome) => { /* do something with outcome here */ };
auth.ShowDialog(this);
ChildWindow c1=new ChildWindow();
c1.Owener=authenticationWindow;
c1.Show(); //or ShowDialog();
ChildWindow c2=new ChildWindow();
c1.Owener=anotherWindow;
c2.Show(); //or ShowDialog();
//to get the parent, use the property c.Owner
if(c.Owner is AuthenticationWindow) //AuthenticationWindow is the type of authenticationWindow instance
{
...
}
Related
I have a login window that appears in the following scenarios:
Application starts: if the user has not logged in ( client id that is stored in the Properties.Setting ), the login window appears. The user logs in, the login windows closes and the main opens upon success.
The user is already logged in, but now wants to log in as a different user: the user presses a the login button and the login window appears and the user logs in. Upon success, the login window closes, and the new user's information is displayed in a small textbox.
The problem here is that I want to use the same login window in both scenarios. What I imagined was a custom callback method for each of these scenarios that would be invoked upon logon success. I'm facing the problem of accessing LogonWindow through my LogonViewModel, where the logic of logging in happens. The reason for this, is that I want to close the window upon success and do other stuff.
Here's my code:
App.xaml.cs
private void check()
{
if (isloggedin)
{
Client c = new Client();
Main m = new Main(c);
m.Show(); //
}
else
{
LoginViewModel vm = new LoginViewModel();
vm.callback = (loginViewModel, client) =>
{
// the user logged in, now show the window and close the login window
Main m = new Main(client);
m.Show();
// close LoginWindow after we're done logging in.
// But where? Who has access to window?
};
LoginWindow lw = new LoginWindow(vm);
lw.Show();
}
}
LoginViewModel.cs
public class LoginViewModel : ViewModelBase
{
public Action<LoginViewModel, Client> callback { get; set; }
public LoginViewModel()
{
}
public ICommand SignIn
{
get
{
return new RelayCommand<object>(signin, o => canLogin());
}
}
private bool canLogin()
{
return true;
}
private void signin(object o)
{
if (clogin.login())
{
Console.WriteLine("Client Found!");
Client client = clogin.getClient();
// save settings
Properties.Settings.Default.clientid = client.Uid;
Properties.Settings.Default.Save();
Client = client;
// pass the new client, and pass the current view model?
callback(this, client);
}
}
}
The initial thought was the to pass the entire window object to the viewmodel, but I think that's breaking essence of MVVM and the be able to Close the window from within my callback method since I'm already passing in the LoginViewModel. My question, is there a way to achieve what I'm trying to do? Remember, the essence is to do something after the user's logged in successfully depending from which point in my application... Thanks for the input.
In your situation I would add two events to my ViewModel
public event Action<Client> SignInSucceded;
public event Action CloseApplication;
When you create your Viewmodel you can bind the actions you want to get performed onsign in to the events.
So for example you could either create the mainwindow and show it or grab an existing one and show it.
Same procedure with the close call. Just bind an method that performs a close for all views and components that arent needed.
Because of the fact that when you bind your viewmodel to this event you 've got a valid instance of it there should be no problem on binding it. I do not know what your client class is exactly but you could move the creation of this in the method that is bind to the event and just call the event with a uid.
Edit:
In your App.xaml.cs you keep a Property that beholds the instance for your windows then you can do sthh like:
viewModel.CloseApplication += () => this.mainWindow.Close();
Just pay attention to synchronization (Dispatcher)
I hope I did understand your intentions correctly.
I have the following snippet of code that allows me to pull the properties from an object in my list and assign them to variables in other forms. However, I need to be able to pull the data from my variables in the other form and use those to set the properties of the given object.
My class Account is used to populate my list accounts. On my next form AccountMenu I have a class Variables1 that contains accessible variables that are used throughout the rest of my forms to keep track of the checking balance and saving balance. When logging off from the AccountMenu, I want to be able to pass the values from Variables1 to the account that was initially used.
I know how to pass variables from one form to another, but I'm not really sure how to update the form automatically, without a button, on the original form. Thus, the solution that I see is that I have a button on my AccountMenu form that "logs" the user out, via this.close(); Additionally, I guessed that under that button, I need to have some code that assigns the variables as properties to the object. I'm just not sure how I can access the set properties of the object, since it is dynamically called with the code below.
Can someone help me figure out what I need to do? Below is some of the relevant code so that you can see how I have things set up. I am just not sure how to access "matches" from the other form in order to update that specific object properties. Thank you, anyone, who can help!
//variable that will be used to check textbox1.Text
string stringToCheck;
//array of class Account
List<Account> accounts = new List<Account>();
public MainMenu()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//set value to user's input
stringToCheck = textBox1.Text;
//set a var that only returns a value if the .Name already exists
var matches = accounts.FirstOrDefault(p => p.Name == stringToCheck);
//check through each element of the array
if (matches == null)
{
accounts.Add(new Account(stringToCheck));
textBox1.Text = "";
label3.Visible = true;
}
else if (matches != null)
{
//set variables in another form. not sure if these are working
Variables1.selectedAccount = matches.Name;
//is this calling the CheckBalance of the instance?
Variables1.selectedCheckBalance = matches.CheckBalance;
//same thing?
Variables1.selectedSaveBalance = matches.SaveBalance;
//switch to form
AccountMenu acctMenu = new AccountMenu();
this.Hide();
acctMenu.Show();
}
}
As per my understanding I think what you required is kind of trigger on your parent form that needs to be called from your child application.
If that is what you required than you can go with defining an event on your AccountMenu form. and register this event from your Accounts form.
Than simply raise this event from your AccountMenu subform.
Deletegates and Events are really works like magic :)
Let me show you some code how to do this.
Code required in AccountMenu window:
public delegate void PassDataToAccounts(string result);
public event PassDataToAccounts OnPassDataToAccount;
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
if (OnPassDataToAccount != null)
OnPassDataToAccount("result");
base.OnClosing(e);
}
Code required in Accounts window button1_Click event where the AccountMenu will open:
//set variables in another form. not sure if these are working
Variables1.selectedAccount = matches.Name;
//is this calling the CheckBalance of the instance?
Variables1.selectedCheckBalance = matches.CheckBalance;
//same thing?
Variables1.selectedSaveBalance = matches.SaveBalance;
//switch to form
AccountMenu acctMenu = new AccountMenu();
acctMenu..OnPassDataToAccount += childwindow_OnPassDataToAccount;
this.Hide();
acctMenu.Show();
}
void childwindow_OnPassDataToAccount(string result)
{
if (result == "result")
{
// Processing required on your parent window can be caried out here
//Variables1 can be processed directly here.
}
}
I've an editor window opened. And, I've a requirement to check that if it fails to connect to database or, connection is broken when window is opened, the window should be closed.
var window = new EditorWindow(group);
window .KeyDown += (sender, args) =>{
if (args.Key == Key.Escape)
window .Close();
};
DialogHelper.ShowDialog(window);
And, for the change of database connection I've:
public dbState dbState
{
get { return dbState ; }
private set
{
dbState = value;
FirePropertyChanged("dbState ");
}
}
I'm new to WPF so any help would be appreciated. Thank you in advance.
If you are wiring things up directly, you would add code inside your EditorWindow class subscribing to the dbState property change event, and when that even fires, in your handler you would call this.Close() method to close the window. See http://msdn.microsoft.com/en-us/library/ms748948.aspx for more details.
A cleaner way to do it would be to use an MVVM library and take advantage of event aggregation. For example, this is how Caliburn.Micro does it: http://caliburnmicro.codeplex.com/wikipage?title=The%20Event%20Aggregator.
Please can someone tell me the technique that would be used, for the following scenario.
I would like to authenticate users, before I allow my code to perform another action.
I have a method that opens a new window that contains my authentication form (username and password).
private bool userLogin()
{
Window loginInterface = new Window()
{
Title = "Please Login",
Content = new login(),
Height = 282,
Width = 300,
ResizeMode = ResizeMode.NoResize,
WindowStartupLocation = WindowStartupLocation.CenterOwner
};
loginInterface.Owner = this;
loginInterface.ShowDialog();
return true;
}
I'm calling this method like so, on button click:
private void perform_action(object sender, RoutedEventArgs e)
{
if (!userLogin())
{
// Failed login, do nothing
}
else
{
// Authentication successful, perform action
delete_item();
}
}
The window opens fine, but how can I now make my method return true or false based on the what the user does on the opened form?
So when the user clicks the login button named login_button, my code already validates the credentials, but I need the 'bool' value sent back.
Can I make my first window almost wait for an action to be performed on another window and get the response back?
The Window.ShowDialog() method actually already returns a bool?. This can be set at any point from within the Window by setting (for example) this.DialogResult = true. You can then close the window and access the value from the calling code.
To close the window with a result:
this.DialogResult = true;
...and then to use that result in the calling code:
var myWindow = /*create window*/;
var result = myWindow.ShowDialog();
if (result == true)
{
//...
}
To close the login screen you can set DialogResult to true or false, and ShowDialog returns this value. For other things you can create events on the second window and subscribe to them on the first.
userLogin should return something other than true.
I would do something like this (based on the code shown):
return loginInterface.WasSuccessful; // you'd have to add this property
My WinForms app has a simple modal login form, invoked at startup via ShowDialog(). When I run from inside Visual Studio, everything works fine. I can just type in my User ID, hit the Enter key, and get logged in.
But when I run a release build directly, everything looks normal (the login form is active, there's a blinking cursor in the User ID MaskedEditBox), but all keypresses are ignored until I click somewhere on the login form. Very annoying if you are used to doing everything from the keyboard.
I've tried to trace through the event handlers, and to set the focus directly with code, to no avail.
Any suggestions how to debug this (outside of Visual Studio), or failing that - a possible workaround?
Edit
Here's the calling code, in my Main Form:
private void OfeMainForm_Shown(object sender, EventArgs e)
{
OperatorLogon();
}
private void OperatorLogon()
{
// Modal dialogs should be in a "using" block for proper disposal
using (var logonForm = new C21CfrLogOnForm())
{
var dr = logonForm.ShowDialog(this);
if (dr == DialogResult.OK)
SaveOperatorId(logonForm.OperatorId);
else
Application.Exit();
}
}
Edit 2
Didn't think this was relevant, but I'm using Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase for it's splash screen and SingleInstanceController support.
I just commented out the splash screen code, and the problem has disappeared. So that's opened up a whole new line of inquiry...
Edit 3
Changed title to reflect better understanding of the problem
UI focus/redraw/etc. issues usually are rather straightforward to debug by using remote-debugging. I.e. use a second PC (virtual is just enough) where your application runs.
See this MSDN article for details.
Run this in your form code behind. It will tell you which control has focus by giving you the type and name of the control. Run it in form_shown because its the last event in the form load process.
private void Form1_Shown(object sender, EventArgs e)
{
Control control = FindFocusedControl(this);
MessageBox.Show("The focused control " + control.Name + " is of type " + control.GetType());
}
public static Control FindFocusedControl(Control control)
{
var container = control as ContainerControl;
while (container != null)
{
control = container.ActiveControl;
container = control as ContainerControl;
}
return control;
}
If the answer isn't obvious after that, tell us what you get.
I've found a hack...er...I mean...workaround, that fixes the problem. The solution was buried in one of the comments of this answer (thanks, P. Brian Mackey, for providing the link to the related question!)
The workaround is to minimize the main window while the splash screen is displayed, then set it's WindowState back to Normal before showing the login form.
In the code below, see the lines commented with "HACK".
public class SingleInstanceController : WindowsFormsApplicationBase
{
public SingleInstanceController()
{
this.IsSingleInstance = true;
}
/// <summary>
/// When overridden in a derived class, allows a designer to emit code that
/// initializes the splash screen.
/// </summary>
protected override void OnCreateSplashScreen()
{
this.SplashScreen = new SplashScreen();
}
/// <summary>
/// When overridden in a derived class, allows a designer to emit code that configures
/// the splash screen and main form.
/// </summary>
protected override void OnCreateMainForm()
{
// SplashScreen will close after MainForm_Load completed
this.MainForm = new OfeMainForm();
// HACK - gets around problem with logon form not having focus on startup
// See also OfeMainForm_Shown in OfeMainForm.cs
this.MainForm.WindowState = FormWindowState.Minimized;
}
}
public partial class OfeMainForm : Form
{
// ...
private void OfeMainForm_Shown(object sender, EventArgs e)
{
// HACK - gets around problem with logon form not having focus on startup
// See also OnCreateMainForm in Program.cs
this.WindowState = FormWindowState.Normal;
OperatorLogon();
}
// ...
}
This is working for now, but I'm wondering if I should explicitly open the Logon form from the SingleInstanceController, rather than from my main form.