I am having some problems with WPFs.
I have a project that has multiple windows, so to control this windows, I have created a controller class. This controller will have a instance of each windows:
this.mainWindow = new MainWindow();
this.loginWindow = new LoginWindow();
this.registerWindow = new RegisterWindow();
The problem comes when I callback from any of the windows to the controller class and from this controller I want to update the information of the window (for example update the value of a property), the information is not being updated
// In controller
public void login(String email, String pass)
{
....
this.loginWindow.showErrorInPassword();
}
// In LoginWindow
public void showErrorInPassword()
{
this.emailErrorImage.Visibility = System.Windows.Visibility.Visible;
}
... but if I send from the LoginWindow a reference of itself to the login function on the controller, the emailErrorImage will be shown
public void login(String email, String pass, LoginWindow lw)
{
....
lw.showErrorInPassword();
}
Seems that the instance that I have in the controller is not the same as the one that is being displayed when I do this.loginWindow.show()
Can someone tell me what am I doing wrong?
You are going to need to bind the UI objects to a MVVM class to update each window.
Use events to call back to the controller.
Here is a brief example. First create a class to contain event args. Doesn't really have to contain anything. It just differentiates between different delegates. Make it its own class in the namespace so everything has access to it.
public class SomeEventArgs: EventArgs
{
}
Inside the window class:
public event EventHandler<SomeEventArgs> CallBackToController;
protected virtual void OnCallBackEvent(object sender, SomeEventArgse)
{
EventHandler<SomeEventArgs> handle = CallBackToController;
if (handle != null)
{
handle(this, e);
}
}
In the controller class, after instantiating the window assign the event to a method.
this.loginWindow = new LoginWindow();
this.loginWindow.CallBackToController += new EventHandler<SomeEventArgs>(MethodToHandleEvent);
Then the Method must have the same form as expected:
private void MethodToHandleEvent(object sender, SomeEventArgs e)
{
// Do something in response.
}
Now anytime you call OnCallBackEvent(this, new SomeEventArgs()) from the window class, the controller class will catch the event and execute MethodToHandleEvent
For instance:
private void LoginWindowBtn_Click(object sender, RoutedEventArgs e)
{
// Logged in ok, let the controller know.
OnCallBackEvent(this, new SomeEventArgs ());
}
There are a ton of tutorials on this, I think this is a better approach to passing references of windows from window to window.
Related
I am developing an application in WPF. I need to load an instance of the Window class (which I call Win1 here) with which a form is filled. Then, when the Submit button is clicked, Win1 closes and only then can a new Win2 window be loaded (another class, also inherited from Window). The problem is that both of them open and I can not synchronize the data obtained from the first Win1 and pass them to the second Win2. I'm just messing up.
Someone can give me a generic idea indicating the tools and the pattern I need to do the above. For the specifications given to me, it is necessary that Win2 appears only after Win1 has finished its work.
Even though the application is more complex than I described it now, I would like to post some code, but I manage to confuse the ideas of who is reading me, so I tell you that at the moment I'm managing the windows inside the constructor of App.cs, while MainWindow.cs corresponds to Win2 and I created a new class to implement Win1.
public partial class App : Application
{
// Params...
public App()
{
Client = LoadNetwork();
User = LoadUser(Client); // Shows Win1
Games = LoadMinigames();
mainWindow = new MainWindow(User, Games);
Application.Current.MainWindow = mainWindow; // On XAML default is Hidden
mainWindow.Show(); // Shows Win2
}
// Other methods...
}
The biggest problem for me is to pass User data to MainWindow and I do not have many ideas on how to deal with this case.
Update
public partial class MainWindow : Window
{
public UserLoading ul;
public UserRegistering ur;
public User.UserProfile User;
private List<Game.Game> Games;
public Label Username;
public MainWindow(User.UserProfile user, List<Game.Game> games)
{
User = new UserProfile();
InitializeComponent();
User = user;
Games = games;
Username.Content = User.Username;
DrawList(Games);
}
//...
}
I realize I have explained myself a bit 'badly rereading my question several times. So I update it trying to be clearer by reporting here my answer to one of the comments.
The UserLoad method is not blocking, because inside it are instantiated classes that inherit Window (other windows for login and registration in other words) then the flow of execution proceeds and instantiates the MainWindow where naturally the argument "user" will result null because the forms have not been filled yet. I realize now that perhaps I had explained myself badly. The call of Win1 is not blocking and I would like it to return only when the user data is ready to be passed as an argument to Win2.
I have done this in the past. here is my solution:
Set Your Launch Window to Win1. Let It launch. Create a Static Method in App.cs to launch Win2. When Win1 is ok to shut down and you want Win2 to open call App.ShowMainWindow(this) from within Win1.
App.cs
public partial class App : Application
{
static internal void ShowWin2(Win1 win1)
{
Win2 win2 = new Win2();
// Copy Win1 stuff to Win2 here
Application.Current.MainWindow = win2;
win2.Show();
}
}
Win1
public partial class Win1 : Window
{
public Win1()
{
InitializeComponent();
}
private void CloseAndLaunchWin2()
{
App.ShowWin2(this);
this.Close();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
CloseAndLaunchWin2();
}
}
As User Nawed mentioned, you should read into MVVM. Syncing can be achieved by using the same model for two different views.
You could do something like this.
var sharedContext = new MyViewModel();
var viewOne = new MyWindow();
var viewTwo = new MyUserControl();
viewOne.DataContext = viewTwo.DataContext = sharedContext;
I am trying to open a new ViewController with a button click. I created a new secondViewController class and set the name to SecondViewController. I also added an Navigation Controller and connected it with the RootView Controller. However if I try to run it i get the following errors:
btn_Next.TouchUpInside += (object sender, EventArgs e) =>{
secondViewController controller = this.Storyboard.InstantiateViewController("SecondViewController") as secondViewController;
this.NavigationController.PushViewController(controller, true);
};
Did I miss something and is the Name the same thing like the Storyboard Id?
Looks like you never created a separate class for your secondViewController.
Make sure you explicitly create a new UIViewController class:
public partial class SecondViewController : UIViewController
{
}
Then in your storyboard, set the "Class" of your secondViewController to this class.
This will let you cast InstantiateViewController to your SecondViewController.
Alternatively, you can just change your code to this:
btn_Next.TouchUpInside += (object sender, EventArgs e) =>{
var controller = this.Storyboard.InstantiateViewController("SecondViewController");
this.NavigationController.PushViewController(controller, true);
};
If you don't care for having a separate class for your new controller.
I'm working on a Windows Forms app and I've come to a point where I can't understand what's happening.
I have something similar to an MVC architecture. Sometimes I want controls that belong to the view to stop listening to events. So inside the view code I've written a method that looks like this:
public void enableEventHandlers(bool enable)
{
if (enable)
{
control.someEvent += someEventHandler;
}
else
{
control.someEvent -= someEventHandler;
}
}
The thing is: when I want to remove an event handler I just call this method with false as a parameter.
If I call this method from inside the view code it works just fine. But if I call this method from inside the controller code, it doesn't work (the event handlers are not removed).
Just to give a little more context:
This works:
public partial class View : Form
{
public void enableEventHandlers(bool enable)
{
// The code I already showed
}
public void doSomething()
{
enableEventHandlers(false);
// do something
enableEventHandlers(true);
}
}
This doens't work:
public class controller
{
private View myView;
public void doSomething()
{
myView.enableEventHandlers(false);
// Do something... but somehow everything inside my Form is still paying attention to events
myView.enableEventHandlers(true);
}
}
Finally I found the problem. It seems that somehow I was attaching an event handler twice to the same Control. I couldn't find the exact line number where I was doing that anyway. The solution I found is to remove an event handler before adding a new one.
So the method enableEventHandlers looks now like this:
public void enableEventHandlers(bool enable) {
if (enable)
{
control.someEvent -= someEventHandler;
control.someEvent += someEventHandler;
}
else
{
control.someEvent -= someEventHandler;
}
}
Thanks for your answers.
I don't know if that's it but you didn't initialize your View. You just say "private View view", but that doesn't point to anywhere. You want to either make a new View by doing private View v = new View(), or let that view point to the view that you want to change the events.
Have been learning about MVP and have tried writing a test app using it in WinForms. I'm struggling to find a well explained example on how to navigate between my forms/views. As an example, the program starts and I want to show a login dialog then go into my main view if the login was successful. At the moment, my Main method looks something like this:
static void Main()
{
var loginView = Injector.Resolve<ILoginView>();
if (loginView.DoLogin() != LoginResult.OK) return;
var mainView = Injector.Resolve<IMainView>();
Application.Run(mainView); // won't work as mainView isn't a form
}
The Injector object is just a wrapper around an IoC tool (currently StructureMap). The thing is, I've read that I shouldn't really be manually creating instances via the Injector as they should really be done via constructor injection.
I've managed to do this up to a point but not when it comes to navigation. I can't think of an elegant way of moving through my views and was wondering if anyone here might shed some light on this? I've read a little on application controllers but have not found an example to show it clearly.
In regards to the navigation question:
I've managed to do this up to a point but not when it comes to
navigation. I can't think of an elegant way of moving through my views
and was wondering if anyone here might shed some light on this? I've
read a little on application controllers but have not found an example
to show it clearly.
Below is a simplified version of a construct I've used. Note that the setup and tear down hooks are called automatically when the NavigateTo method is called. Also, +1 to #AlexBurtsev, as his answer hints at this very same approach.
// Presenter can and should offer common services for the
// subclasses
abstract class Presenter
{
public void Display()
{
OnDisplay();
}
public void Dismiss()
{
OnDismiss();
}
protected virtual OnDisplay() // hook for subclass
{
}
protected virtual OnDismiss() // hook for subclass
{
}
private NavigationManager _navMgr;
internal NavigationMgr NavigationManager
{
get
{
return _navMgr;
}
set
{
_navMgr = value;
}
}
}
// NavigationManager is used to transition (or navigate)
// between views
class NavigationManager
{
Presenter _current;
// use this override if your Presenter are non-persistent (transient)
public void NavigateTo(Type nextPresenterType, object args)
{
Presenter nextPresenter = Activator.CreateInstance(nextPresenterType);
NavigateTo(nextPresenter);
}
// use this override if your Presenter are persistent (long-lived)
public void NavigateTo(Presenter nextPresenter, object args)
{
if (_current != null)
{
_current.Dismiss()
_current.NavigationMgr = null;
_current = null;
}
if (nextPresenter != null)
{
_current = nextPresenter;
_current.NavigationMgr = this;
_current.Display(args);
}
}
}
class MainMenuPresenter : Presenter
{
private IMainMenuView _mainMenuView = null;
// OnDisplay is your startup hook
protected override void OnDisplay()
{
// get your view from where ever (injection, etc)
_mainMenuView = GetView();
// configure your view
_mainMenuView.Title = GetMainTitleInCurrentLanguage();
// etc
// etc
// listen for relevent events from the view
_mainMenuView.NewWorkOrderSelected += new EventHandler(MainMenuView_NewWorkOrderSelected);
// display to the user
_mainMenuView.Show();
}
protected override void OnDismiss()
{
// cleanup
_mainMenuView.NewWorkOrderSelected -= new EventHandler(MainMenuView_NewWorkOrderSelected);
_mainMenuView.Close();
_mainMenuView = null;
}
// respond to the various view events
private void MainMenuView_NewWorkOrderSelected(object src, EventArgs e)
{
// example of transitioning to a new view here...
NavigationMgr.NavigateTo(NewWorkOrderPresenter, null);
}
}
class NewWorkOrderPresenter : Presenter
{
protected override void OnDisplay()
{
// get the view, configure it, listen for its events, and show it
}
protected override void OnDismiss()
{
// unlisten for events and release the view
}
}
I haven't used WinForms for a long time, but I'll try to answer this. I would use the same strategy as WPF Prism do.
About MainView and Application.Run:
Create a main Region (root Form), with empty container inside which can hold UserControl (I forgot exact class names), then when you need to switch root view, you do RootView.SetView(UserControl view) which will do something like Form.Clear(), Form.AddChild(view).
About the navigation and using container:
You could create a service for navigation: INavigationService which you inject in constructors with method like INavigationService.NavigateView(String(or Type) viewName, params object[] additionalData)
You can insert a method in mainView that returns the actual form.Then you can call
Mainview:IMainView
{
Form GetView()
{
//return new Form();
}
}
In Main you can call ,
Application.Run(mainView.GetView())
I have a web page (aspx). This page has a button , a UpdatePanel and a Timer. Now my problem is as follow suppose 10 users are on this page at the same time and suppose user number 3 and 8 click its button then all user’s UpdatePanel should get updated. What is right way to achieve this functionality?
Since each user is running its own copy of the web application so whats happenning on one user's browser can't be notified to the other user. One thing you could do is when one user clicks the button to update, you could save it, whereas all the user application could ping the server may be every 2 secs to know if updation happens and if so updates.
You could use a Global property. On that property, you can put an Observer pattern, and let the visitors subscribe. When you alter the Application property ( shared throughout all Sessions ), you call the Notify() method. The client Notify() method gets called, and there you put functionality to update the UpdatePanel.
This code is NOT TESTED, it is a guideline
// *** GLOBAL.ASAX
// This collection will contain all the updatepanels that need to be updated
private List<IUpdatePanelClient> _registeredClients = new List<IUpdatePanelClient>();
public static void RegisterClient(IUpdatePanelClient client)
{
_registeredClients.Add(client);
}
public static void UnregisterClient(IUpdatePanelClient client)
{
_registeredClients.Remove(client);
}
// Which client is triggering the update call ?
private IUpdatePanelClient _clientUpdating = null;
public static IUpdatePanelClient ClientUpdating
{
get { return _clientUpdating ; }
set { _clientUpdating = value; Notify(); }
}
// Notify the clients
public static void Notify()
{
foreach(IUpdatePanelClient client in _registeredClients)
{
client.Update();
}
}
// *** IUPdatePanelClient.CS
interface IUpdatePanelClient // Interface to make the calls
{
void Update();
}
// *** Your codepage
public class MyUpdatePanelPage : Page, IUpdatePanelClient // Inherit the interface
{
public void Page_Load(Object sender, EventArgs e)
{
MyUpdatePanelPage.Global.RegisterClient(this);
}
public void Btn_Click(Object sender, EventArgs e)
{
MyUpdatePanelPage.Global.ClientUpdating = this;
}
public void Update()
{
this._updatePanel1.Update();
}
}
Your question doesn't have enough information for anyone to answer properly. If there is information that you want to keep all users update-to-date on, store that information in a database. When one user edits the data from their user session, whenever other user's get their page refreshed, they will have the most updated data. If you want to have their page refreshed periodically, use a javascript timer.