I get an error trying to send a string from one window to another in my wpf application:
Unable to cast object of type 'WpfApplication4.LoginWindow' to type 'WpfApplication4.MainWindow'.
In my login window the error is on this line:
((MainWindow)Application.Current.MainWindow).StudentID = UserAuthenticationID;
In my main window I have this to test:
public string StudentId { get; set; }
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
label1.Content = StudentID;
}
What am I doing wrong?
EDIT:
No answer has made sense so far, which will be due to my obscure question, I created a wpf application and I have two windows MainWindow.xaml and LoginWindow.xaml.
I want to pass a string (student id) from the login window to the main window after authentication.
I thought the above method was how to do it, as I read it: ((MainWindow)Application.Current.MainWindow).StudentID says where I am intending the string UserAuthenticationID to be sent to?
Then in the MainWindow.xaml I get the string UserAuthenticationID and set it, I then assign a labels content to this public string?
Presumably you create your login window from your main window. If you need to reference your main window from your login window then pass a main window reference to your login window when you construct it. Eg
void MainWindow_Loaded(object sender, RoutedEventArgs e) {
LoginWindow login = new LoginWindow(this);
login.ShowDialog();
}
class LoginWindow : Window {
MainWindow app_window;
public LoginWindow(MainWindow app_window) {
Owner = app_window;
this->app_window = app_window;
}
}
Error coming because its trying to convert Application.Current.MainWindow to MainWindow, which is infact LoginWindow.
suggestion when you are coverting one object other made check before it
if(Application.Current.MainWindow is MainWindow)
{
/// than do the code
}
It looks like your trying to feed the UserAuthenticationID from your login dialog back to your main window where you store it as StudentId.
Why not handle that back in the main window Eg:
if (login.ShowDialog())
StudentId = login.UserAuthenticationID;
It seems that your Application.Current.MainWindow is of Type LoginWindow. But you want to cast it to MainWindow. Both derive (maybe) from Window. But you can't cast a derived class to another derived class.
//Edit
try
((LoginWindow)Application.Current.MainWindow)
or change your Application.Current.MainWindow to a object of type MainWindow
//Edit 2
I think i understood what your intention was. You could try the following:
In your LoginWindow where you press the Button "Login" handle the Buttonevent (Click), get the ID whereever it comes (TextBox) from, put it in a new instance of MainWindow and set the
Application.Current.MainWindow
to the new instance of MainWindow. If I'm wrong on how you will do it, you should give more facts.
Other solution is the LoginDialog solution from Ricibob.
You need to restructure the design of your program. What you want to do isn't terribly hard; you're essentially asking for a good way to communicate between forms.
First off, I suggest not using Application.Current.MainWindow unless you're really sure you need to. You can get apps to work using it, but it's not good design and leads to problems.
We'll start off with the definition of some OtherWindow that will be the popup.
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for OtherWindow.xaml
/// </summary>
public partial class OtherWindow : Window
{
public OtherWindow()
{
InitializeComponent();
}
public string SomeData
{
get
{
//you'll probably want to return the value of a textbox or something else
//the user fills in.
return "Hello World!";
}
}
}
}
From a communication point of view, all that's really important is that it has a property (or properties) with the information that the main form will need to access. Obviously I've omitted all of the code for actually collecting the data to return and instead hard code a value for this example.
Now for the mainform. (All I've added is a button and a label.)
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
OtherWindow other = new OtherWindow();
other.Closed += (sender2, e2) =>
{
label1.Content = other.SomeData;
};
//either of the methods below, depending on desired behavior.
other.Show();
//other.ShowDialog();
}
}
}
While there are quite a lot of ways of making different windows/forms communicate, this is my favorite. All of the logic for the interaction between the windows is in once place (the button click event). The main form clearly "owns" OtherWindow, and OtheWindow doesn't need to know a thing about MainWindow, it only need to know how to collect some information. MainWindow will take care of pulling out the information that it needs.
The code for the button click even reads just like what you want it to do: create a new window, when it's closed, set its content to my label, and show the form. It's all in once place, and in the order that I logically think through it (you can attach the event handler after Show if you want).
Oh, and this is exactly the same way that I would do this in a winforms app too, in case anyone cares, barring the fact that some of the class/property names will be different.
Related
I'm making a snake game in WPF C# and I created a few user controls for different "views", it means I have the MainWindow which
inherits from the Window class and several user controls with their xaml files.
One user control represents the main menu, another represents option view and so on.
public partial class MainWindow: Window
{
public static Menu menu;
public MainWindow()
{
InitializeComponent();
menu = new Menu();
this.Content = menu;
}
}
like above, in MainWindow I create Menu object (it's a one of the user controls - class and xaml file) and set the content of Window to content of Menu. And then in menu class I do the same, for example like user click on button with text "options", he goes to options user control. I just do
this.Content = new Options(); //"this" is now menu class
when he clicks the button with text "singleplayer", he goes to user control with singleplayer game
this.Content = new Game();
and so on.
In this way everything works fine, I can switch between different user controls and "load" different contents to application Window, but every time I create new object and it's the problem. When I go to options and then back to menu, a new object of menu class is creating, I cannot remember the previous settings and etc. I would like to create this only once and then reference to this - load existing object content. I tried using binding but it doesn't work.
Ho can I do this? How can I switch between different user controls without data loss and creating new object every time?
You should use singletons.
Singletons allow you to have only one instance of a class. This way, every time you manipulate the instance, you manipulate the same one. This allow you to keep / update the state of the same instance through your code.
It's looking like this, and it's thread safe.
public sealed class YourClass
{
private static readonly YourClass instance = new YourClass();
/* Explicit static constructor to tell C# compiler
* not to mark type as beforefieldinit */
static YourClass()
{
}
private YourClass()
{
}
public static YourClass Instance
{
get
{
return instance;
}
}
}
EDIT: OP's mistake was to set Content of the usercontrol istelf insteand of MainWindow usercontrol. You can get the current window containing usercontrol by using this line of code inside usercontrol.
Window yourParentWindow = Window.GetWindow(this);
I write Application with WPF, MVVM, Prism and Unity. From of one Window I start secondary Window:
public void ShowForm(IPrescriptionViewModel viewModel)
{
var view = new PrescriptionForm();
view.SetDataContext(viewModel);
view.ShowDialog();
}
Method SetDataContext
public void SetDataContext(IPrescriptionViewModel viewModel)
{
if (viewModel == null) return;
DataContext = viewModel;
if (viewModel.CloseAction == null)
viewModel.CloseAction = new Action(this.Close);
}
In BTMPrescriptionViewModel is a Property
public Action CloseAction { get; set; }
and CloseCommandExecute
public ICommand CloseCommand => new RelayCommand(CloseCommandExecute);
private void CloseCommandExecute()
{
CloseAction();
}
It works fine, but only once - the first. After closing the secondary window and again open it, it is no longer closed with command button, only with close button of the window. After closing and opening of the parent window, secondary window can I again close with command button, but again only once.
Lacking a good Minimal, Complete, and Verifiable code example that reliably reproduces the problem, it's impossible to say for sure what the issue is. But, based on the code you posted here, it appears that you are creating a new window each time, but only setting the CloseAction property once.
Because the CloseAction delegate value you assign captures this to call the Close() method, it is always calling Close() on the first window you create, not any of the ones you create afterward.
Without a more complete code example, it's not clear what the best way for you to accomplish your goal is. But the basic issue would probably be solved if you just took out the null check and always assigned the value:
public void SetDataContext(IPrescriptionViewModel viewModel)
{
if (viewModel == null) return;
DataContext = viewModel;
viewModel.CloseAction = this.Close;
}
Note that you also don't need to explicitly call the delegate constructor. The compiler has inference rules for dealing with delegate types, and simply referring to the method name is sufficient.
I have a C# application that allows the user to log certain events that occur in a game. For simplicity I'll call them ParentForm and ChildForm.
ParentForm is used 99% of the time, to log common events. This is represented as the user clicking a PictureBox and the Tag property of that PictureBox being added to a ListBox. When a "rare" event occurs, the user can click a "log rare event" button on ParentForm to open ChildForm which opens a set of "rare event" PictureBoxes, which function the same as in the ParentForm. The challenge is that I want these common and rare events to be logged to the same ListBox, so I am trying to find out how I would get a PictureBox click (and subsequent Tag from this PictureBox) on the ChildForm to the ListBox on the ParentForm.
The ParentForm does not close while ChildForm is open, and needs to stay open.
In the ParentForm code, I already have the code needed to capture one of the PictureBox clicks and grabbing the Tag, as well as handling dealing with adding it to the ListBox, so it'd be nice if I could just use these.
Here's what I've tried so far for the Parent:
// This file is EventLogger.cs
using rareEvent;
namespace mainWindow {
public partial class EventLogger : Form {
// In the ParentForm (listeners for PictureBox clicks are handled elsewhere)
public void pictureBox_Click(object sender, EventArgs e) {
PictureBox pbSender = (PictureBox) sender;
// Open new window and handle "rare" drops
if (pbSender.Tag.ToString() == "rare") {
// Open rare form
EventLogger.RareForm rare = new EventLogger.RareForm();
rare.Show();
}
}
}
}
and here's the child:
// This file is Rare.cs
using EventLogger;
namespace rareEvent {
public partial class rareEventForm : Form {
// In the ChildForm
private void pictureBox_Click(object sender, EventArgs e) {
// Does not compile if form is not instantiated, but I do not
// want a new instance
EventLogger form;
form.pictureBox_Click(sender, e);
}
}
}
I figured something like this would work, but it gives the error
The type or namespace name 'EventLogger' does not exist in the namespace
'mainWindow' (are you missing an assembly reference?)
Any help would be much appreciated. All the other examples I've found of value passing between forms all seem to create new instances which I don't want or were 8 years old and didn't work.
Appreciate it!
Edit: Code updated to have using <namespace> in each file. The problem still exists of not being able to send values between both forms without using new. (See comment to this answer)
In the first form create an instance (of it) here like my form1. It must be static and all datatypes you want to access should be public.
//FORM1
public partial class Form1 : Form
{
//Instance of this form
public static Form1 instance;
//For testing
public string myProperty = "TEST";
//Assign instance to this either in the constructor on on load like this
public Form1()
{
InitializeComponent();
instance = this;
}
//or
private void Form1_Load(object sender, EventArgs e)
{
//Assign the instance to this class
instance = this;
}
Then in form2 when calling EventLogger.RareForm rare = new EventLogger.RareForm(); instead of new form do
EventLogger.RareForm rare = EventLogger.RareForm.instance
Or in my case
Form1 frm = Form1.instance;
I then check the property of form 1 FROM form2 like so
Console.WriteLine(frm.myProperty);
Output was "Test"
Any trouble shout.
I add a window class in my WPF project as below:
public partial class ParameterInput : Window
{
public ParameterInput()
{
InitializeComponent();
}
public void show()
{
bool ac= Activate();
}
}
And I do create a object of this class, and I hope it can be activated.
void myTap(object sender, MouseButtonEventArgs e)
{
ParameterInput ParameterInputDialog= new ParameterInput();
ParameterInputDialog.show();
//bool ac = ParameterInputDialog.Activate();
}
But I find this method do not work, the return value (ac) is false. why? Do anybody know how to solve this problem? I just want to open the dialog which I defined.
this is because in Windows Forms and in WPF as well, to show a Window you should call its Show method not just the Activate one, try to call the Show Method instead, see an example here:
How to open second window from first window in wpf?
This question has be solved, please see: https://msdn.microsoft.com/en-us/library/system.windows.window.owner%28v=vs.110%29.aspx.
So I change my code into:
ParameterInput ParameterInputDialog= new ParameterInput();
ParameterInputDialog.Owner = this;
ParameterInputDialog.Show();
And it do works well. Thanks for all your help.
I'm a student learning C# with WPF using the MVVM pattern. Recently I have been working on a [art of my application (a custom splash screen) that should not be closed when I don't want it to.
I have been searching the web for a good way of doing this without code-behind. Unfortunately after days I still did not find a satisfying way.
Then I came to think of a way to do it myself, with help of just one line of code in the constructor of my view. It still makes my code testable and decouples the code from the View. The question is, is there a better way of doing what I'm trying to do:
My interface for my ViewModel
public interface IPreventCloseViewModel
{
bool PreventClose { get; set; }
}
The extension for the View
public static class PreventCloseViewModelExtension
{
/// <summary>
/// Use this extension method in the constructor of the view.
/// </summary>
/// <param name="element"></param>
public static void PreventCloseViewModel(this Window element)
{
var dataContext = element.DataContext as IDisposable;
if (dataContext is IPreventCloseViewModel)
{
element.Closing += delegate(object sender, CancelEventArgs args)
{
if (dataContext is IPreventCloseViewModel)
{
args.Cancel = (dataContext as IPreventCloseViewModel).PreventClose;
}
};
}
}
}
The code-behind for the View
public partial class SplashScreen
{
public SplashScreen()
{
InitializeComponent();
this.PreventCloseViewModel();
}
}
MVVM does not mean that you cannot use Code-Behind.
MVVM means that your application logic should not be tied to UI elements.
You can perfectly well handle events in code behind (such as Window.Closing), and "send messages" or execute methods in the ViewModel to react to that.
Here, you are not breaking MVVM by placing the event handler in code behind. You would be breaking MVVM if you were placing the logic that determines whether the application can be closed in code behind. That is a responsibility of the application logic, and the application logic lives in ViewModels, not Views.
I usually have a generic Shell class which subclasses Window and does something like:
public Shell()
{
InitializeComponent();
this.Closing += (s,e) =>
{
var canClose = Content as ICanClose;
if (canClose != null)
e.Cancel = !canClose.CanClose;
}
}
That way it does not matter what kind of view model you put in, if it implements the interface that will be taken into account.
Don't see much point in externalizing the logic, and it's fine in terms of the MVVM pattern.