I am building a Windows Forms application that I would like to potentially port to WPF and GTK# in the future. I am interested in using the MVP pattern to accomplish this.
For a simple preferences dialog I have a designer created form that implements a view interface with events that the presenter can listen for when the dialog is saved or closed. I use the designer to create data bindings between the preferences frame's controls and the .NET project settings, so I am doing supervising presenter.
interface IPreferencesDialogView
{
event EventHandler Save;
event EventHandler Cancel;
}
public partial class PreferencesDialog : Form, IPreferencesDialogView
{
private PreferencesDialogPresenter presenter = null;
public event EventHandler Save;
public event EventHandler Cancel;
public PreferencesDialog()
{
InitializeComponent();
presenter = new PreferencesDialogPresenter(this);
}
private void PreferencesDialog_FormClosing(object sender, FormClosingEventArgs e)
{
if (this.DialogResult == DialogResult.OK)
{
Save?.Invoke(this, EventArgs.Empty);
}
else
{
Cancel?.Invoke(this, EventArgs.Empty);
}
}
}
My model uses the .NET project settings to store application settings since it is available in Mono and I can use it with both WPF and GTK#.
class PreferencesDialogPresenter
{
private readonly IPreferencesDialogView view;
public PreferencesDialogPresenter(IPreferencesDialogView view)
{
this.view = view;
view.Save += (o, e) => { Properties.Settings.Default.Save(); };
view.Cancel += (o, e) => { Properties.Settings.Default.Reload(); };
}
}
On my main form I also some very specific code to Windows Forms, a cascade button which cascades all open MDI windows. It's very simple using the LayoutMdi method provided by Windows Forms (something Java Swing does not have).
private void cascade_Click(object sender, EventArgs e)
{
this.LayoutMdi(MdiLayout.Cascade);
}
This to me seems to be working great so far. The view knows nothing about the model or the presenter and the model knows nothing about the view or the presenter. However, I have a few questions.
Is there anyway to simplify my event patterns? I really don't like having to pass arguments I do not use.
Ideally I would have only a single event, Closed, and I would forward the dialog result to the presenter. I do not like the Save/Cancel logic being in the view. However, the DialogResult type is Windows Forms specific, so I can't use it with GTK#. Could I create my own wrapper type? Is that what is usually done?
How would I go about showing this dialog? For example, on the Main Form I have a "Preferences" menu item. When it is clicked, who should the main form's presenter tell to open the preferences dialog, should it tell the preference dialog's view or presenter to show itself? This question arises because ShowDialog is obviously Windows Forms specific.
How would I even go about putting the MDI cascade logic into my presenter or is it not even worth it to bother in this case?
Is there anyway to simplify my event patterns? I really don't like having to pass arguments I do not use.
Ideally I would have only a single event, Closed, and I would forward the dialog result to the presenter. I do not like the Save/Cancel logic being in the view. However, the DialogResult type is Windows Forms specific, so I can't use it with GTK#. Could I create my own wrapper type? Is that what is usually done?
Yes, what I do and believe is the best practice is to create specific events that are related to the action occurring. NOT just passing events up from the UI. So, a single Close event including a simple enum to indicate whether to save or cancel. Your presenter would contain the logic to determine based on that enum whether to do Properties.Settings.Default.Save(); or Properties.Settings.Default.Reload();
Then in your non Windows Forms view, you would still need to invoke that event, but it would be up to the view to decide whether to save or cancel all the time, or whether to implement a custom Save/Cancel dialogue to get this info from the user.
Related
Hi im hoping someone can assist im still new to programming and this is a noob question but i have created a Visual studio - C# (Windows Form Application) and now the question reads to Create a void method for each of my buttons i created in the form and telling me even what to name the method.
but on my research The void keyword is used in method signatures to declare a method that does not return a value.
LinkToAddresses () will be my void method for address the (button), so my question is do i just put in this void method and its going to do nothing?
im just going to link the full question maybe im just really not understanding this>?
''
The below form will represent the main form from which the user will navigate to the other forms. Meaning each button should be linked to the appropriate form. E.g. If button Manage Addresses is clicked the form managed addresses should be displayed. The Exit button should successfully terminate the program.
Create a void method for each button and name them as follow: LinkToAddresses (), LinkToCustomers (), LinkToDrivers (), LinkToStatus (), and LinkToFreight (). The methods should be called under the appropriate button. For the exit button create a void method named AppExit () this should terminate the program.
''
I would appreciate any help or guidance, thank you in advance.
Visual studio usually handles the button actions easily. Just place the buttons on your form, then rename the buttons to LinkToAddresses, LinkToCustomers, LinkToDrivers, LinkToStatus, LinkToFreight and AppExit. Then simply just double click on the each button and visual studio will create a void method for their click event.
using System;
using System.Windows.Forms;
namespace YourApp
{
public partial class FormMain : Form
{
private FormManagedAddresses formManagedAddresses = null;
public FormMain()
{
InitializeComponent();
}
private void LinkToAddresses_Click(object sender, EventArgs e)
{
if (formManagedAddresses != null)
{
formManagedAddresses.Close();
}
formManagedAddresses = new FormNews();
formManagedAddresses.Show();
}
private void AppExit_Click(object sender, EventArgs e)
{
Application.Exit();
}
}
}
The closest thing to a buttons function, is the Click Event Handler. While specific names vary based on Display technology (WinForms, WPF/UWP, ASP.Net), that is the general pattern for Graphical User Interfaces. It is called event driven programming. Even things that have a different programm flow like Games and Web Applications usually try to imitate it.
The signature of a event is given during its definition and must be strictly followed. Usually void NameOfTheEvent(object sender, SampleEventArgs e).
A return type of void is extremely common with events. If there is to be any output, that usually is handeled via a property in the Event Args or by directly doing stuff with the other GUI Elements.
If you want a button to do nothing, you just never give it a event handler. Every single button you ever used, was given a implicit or explicit event handler to do exactly what it did. If you want it to conditionally do nothing, either disable the Button so it can not be clicked, or put a proper if-statement into the event Handler.
A advanced topic would be the command pattern, where there is a bunch of commands in code behind. And each button, menu item and key combination is meerely a way to trigger said command - a representation for hte user to call the command.
You can share a single event across any number of Elements. AS you can see above, the pattern for events includes object sender as argument. This means you can check if it is a specific Button instance that called the event. Or even "unpack" the specific button, do look at stuff like Display String, Tag to get data from it. However, as a general rule retrieving data from the GUI is a bit frowned - ideally the GUI should only represent the data in the backend.
I have a scenario, I am having a MainWindow.xaml. This window has menus (which are initially disabled) and user controls. On one of the user control I have a login button clicking which Login pop up window open up. After successful login I want to Enable my main window disabled menu items. How can I achieve this functionality in WPF MVVM?
Are you using any kind of MVVM Framework?
If so.
Most of them have some kind o event service. For example Caliburn.Micro has EventAggregator.
I would call such event in child view model and subscribe to it in parent one.
In your scenario it would be something like that (in Caliburn.Micro):
class ChildViewModel
{
...
public void HandleLogin()
{
...
_eventAggregator.Publish(new LoginEventArgs);
}
}
class ParentViewModel : IHandle<LoginEventArgs>
{
public void Handle(LoginEventArgs args)
{
..
}
}
With the use of such event service you achieve separation of your ViewModels and you much flexibility because you can handle such LoginEvents in other view models in your application.
As mentioned before many MVVM frameworks has such service:
Caliburn
Prism
MVVM Light
If not.
You can assembly your own event aggregator.
Here are some sources which can be helpful in such homebrew implementation:
SO
MSDN
I have an issue when it comes working with events and/or delegates. I saw very similar questions but still the real implementation is not clear to me. So please when you answer be more specific so I can try and eventually understand how exactly creating/handling of public/custom events work by doing it in a code I know.
What I have is a User Control which is simply a text box and a button I need to change a record in a database using the value from the text box. I'm using this control for many forms so I need to know which entity exactly I'm using and be able to call it's own save method. Doing all that will be easier if I just can use the click event of the button from my User Control and then call the Save() method of the current form.
This is my User Control :
namespace UserControls.CommonControls
{
public delegate void ClickMe(string message);
public partial class ChangeCode : UserControl
{
public event ClickMe CustomControlClickMe;
public ChangeCode()
{
InitializeComponent();
}
private void btnChange_Click(object sender, EventArgs e)
{
if (CustomControlClickMe != null)
CustomControlClickMe("Hello");
//ToDo fill
//MessageBox.Show("To Do: Write the busieness logic.");
}
public void SetTextBoxMask(MaskedTextBox txtBox)
{
txtChange.Mask = txtBox.Mask;
}
}
}
I post it with the last attempt I made to try and implement what I need.
This is one of the form that need to use the Click event from the User Control and more specific the Constructor because if I understand right there is the place where I have to subscribe for the event :
public MaterialEdit()
{
InitializeComponent();
UserControls.CommonControls.ChangeCode. += new ClickMe(button2_Click);
}
UserControls.CommonControls.ChangeCode - this is how I reach my User Control it's named ChangeCode.
From what you pasted it is not clear that you added ChangeCode control to your form. To use the control and it's events and properties, first you must create new instance to it and add it to the form. This is done:
In designer, by dragging control from Toolbox to the form
In code editor, by invoking control constructor and adding new object to control collection
Only then can you handle event of that object. Let's say that you dropped ChangeCode control to a form, and that Visual Studio named it ChangeCode1. You attach a handled to CustomControlClickMe event like this:
ChangeCode1.CustomControlClickMe += new ClickMe(button2_Click);
Code you pasted (UserControls.CommonControls.ChangeCode. += new ClickMe(button2_Click);) is incorrect for several reasons:
Syntactically, left hand side expression ends with . which makes it incorrect assignment target (UserControls.CommonControls.ChangeCode.)
Event name is not provided, only the control name (you need to end left hand side of assignment with what you want to assign to - .CustomControlClickMe)
You are trying to attach handler to a class and not an object
I am making a windows phone 7 and trying to do it using MVVM. I would like to keep my view model as clean as possible but I am unsure on how to make a dialog box. I am using MVVM light and I know they have Messaging system or something but not really sure how to use it.
I would like to use Guide.BeginShowMessageBox as this seems to give more features than the standard dialog box.
How can I do this without breaking the MVVM pattern. AS when I load up the view I want to have a loaded trigger to be triggered and then check some conditions. If conditions are met show the Dialog.
// Vm
public RelayCommand MainPageLoaded
{
get
{
if (!NetworkInterface.GetIsNetworkAvailable())
{
// breaks MVVM now as have view code in viewmodel. Need to take out somehow
Guide.BeginShowMessageBox("Test", "Test network", new List<string>() { "Yes", "No" }, 0, MessageBoxIcon.Warning, asyncResult =>
{
int? returned = Guide.EndShowMessageBox(asyncResult);
// if yes then work offline mode? Maybe another property in ViewModel will get set to say offline mode?
}, null);
}
return null;
}
set
{
// Not sure what to put here.
}
}
// View
<i:Interaction.Triggers>
<i:EventTrigger>
<GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding MainPageLoaded}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Edit
Another problem I am having is. I have a list that is bound to some data that is stored in this property
public ObservableCollection<ContactGroup> ContactGroups { get; set; }
then on tap I have a relaycommand that should be triggered
public ICommand GroupContactTapped
{
get
{
return new RelayCommand<GestureEventArgs>(e =>
{
var selectedTextBlock = e.OriginalSource as TextBlock;
MessageBox.Show(selectedTextBlock.Tag.ToString());
});
}
}
Yet I don't know how to find which object was "tapped" without casting the source to a textblock.
Assuming that you have one mainpage/view that hosts all the other views, like a mainwindow:
I send a message event from the viewmodels, and the dialog box is handled in the code behind of the main window. This is the only codebehind I have in my project so I find it acceptable that the rest of the project can be strictly MVVM, with this one exception.
I send the message with the following (converted from VB so it might need work):
object message = new DialogMessage("YourMessage", YourFunctionThatHandlesCallback) {
Button = MessageBoxButton.YesNo,
Caption = "Caption Goes Here"
};
Messenger.Default.Send(message);
I register for the dialog box with the following in the main page code behind:
Partial Public Class MainWindow
Inherits Window
Public Sub New()
InitializeComponent()
''single initialization of messanger for catching message box
Messenger.[Default].Register(Of DialogMessage)(Me, Sub(msg)
Dim result = MessageBox.Show(msg.Content, msg.Caption, msg.Button, MessageBoxImage.Warning)
''Send callback
msg.ProcessCallback(result)
End Sub)
End Sub
End Class
I could not succesfully convert the C# lambda so I had to leave it in VB. Hope this helps
There is a MessageBoxService in the Cimbalino Phone Windows Toolkit!
You can use that in a MVVM architecture.
What it truly means to follow "the MVVM pattern" is a very subjective thing.
For instance, some people will say you shouldn't show/launch a messagebox (of any type) from the VM, while others will say this is fine.
As with any ambiguity, you'll need to balance adherence to a pattern, with what's most appropriate for a specific project, with what's appropriate for the people developing and maintaining the code base.
In terms of MvvmLight, the messaging system it uses is for communicating from a viewmodel to either another viewmodel or a view, not for displaying messages to the user.
If you are going to use Guide.BeginShowMessageBox, particularly from a viewmodel, beware that it is non-blocking. If you want it to behave like a "regular" MessageBox you'll need to use it with a ManualResetEvent so that it's not possible to continue to interact with the app while the messagebox is displayed.
I'm new to Caliburn.Micro and I'm wondering what is the best way to handle user Login/Logout cycles in my application. I saw some suggestions online to implement this using an empty Shell-View which switches between the LoginView and the main application view, each with a custom ViewModel of course.
I don't really like this solution, because for me these are 2 separate windows with very different properties (Title, Icon, Size) and it seems an unclean solution two change one window to look like the other. Another problem is, that the Login Window comes from an utility library which I don't control and which doesn't use Caliburn.Micro, it's a plain old Window which gives me an event when the user clicks "Login".
I also saw suggestions to display this Dialog in the Bootstrapper startup method, but the problem I see with that is that the user can choose to "Logout" of the application which should display the Login dialog again. It seems wrong to me to handle the switching between the Views in the Bootstrapper.
What I would like is to have some sort of ApplicationViewModel or ApplicationController which works like a Caliburn Conductor, but instead of switching between Views inside a Window, it should switch between the LoginWindow and the MainWindow and should also handle Closing of the whole application (which also requires a Logout). On Activation it would show the LoginWindow, handle the Login event and then switch to the Main Window (Shell). If the user chooses to "LogOut", the event should bubble up to the ApplicationViewModel/Controller again which would deactivate/close the MainWindow, perform the Logout and then show the LoginDialog again. Similar a Close event would do the Logout, but then Shutdown the whole application.
So my questions are:
What do you think about this solution and do you have another/better one?
How do I implement this? ;-)
Thanks a lot!
I think the solution to your problem is fairly easy.
In a nutshell you are creating one ViewModel as Shell which is represented with a Login Window when the application starts. If the user logs in successfully this window closes and the same instance of the viewModel is displayed in a Content Window. If the user is doing a logout, the Login Window is shown again.
First of all create an interface IShell which exposes two delegates LoginSuccessful and Logout
public interface IShell
{
Action LoginSuccessful { get; set; }
Action Logout { get; set; }
}
Next create a class ShellViewModel which implements IShell
public class ShellViewModel : Screen, IShell
{
public ShellViewModel()
{
LoginSuccessful = delegate { };
Logout = delegate { };
}
public Action LoginSuccessful { get; set; }
public Action Logout { get; set; }
public void DoLogin()
{
LoginSuccessful();
}
public void DoLogout()
{
Logout();
}
}
The methods DoLogin and DoLogout are Actions which can be bound to a Button or whatever control appropriate for you.
Next step is to override the OnStartupMethod in your Bootstrapper. This premises that you have an instance of the WindowManager and ShellViewModel exported by an IoC Framework of your choice.
protected override void OnStartup(object sender, StartupEventArgs e)
{
var windowManager = IoC.Get<IWindowManager>();
var viewModel = IoC.Get<IShell>();
viewModel.LoginSuccessful =
() => GuardCloseAndReopen("Content");
viewModel.Logout =
() => GuardCloseAndReopen("Login");
windowManager.ShowWindow(viewModel, "Login");
}
private void GuardCloseAndReopen(string shellViewMode)
{
var windowManager = IoC.Get<IWindowManager>();
var shellScreen = IoC.Get<IShell>() as Screen;
Application.ShutdownMode = ShutdownMode.OnExplicitShutdown;
shellScreen.TryClose();
Application.ShutdownMode = ShutdownMode.OnLastWindowClose;
windowManager.ShowWindow(shellScreen, shellViewMode);
}
The trick to this is: If the DoLogout method is called, the current window gets closed by calling TryClose on the ShellViewModel. At the same time you prevent the application from being shutdown by setting the Application.ShutdownMode to OnExplicitShutdown. Then using the windowmanager, you create another window in Login Mode by passing "Login" as Context information to the windowManager. This is actually the same ViewModel, however, with a different visual representation.
For Logout you are doing the same thing just around.
To get this working using Caliburn Conventions, you need a special project structure as seen here (and explained there):
Now I challenge you to take this code and create a little sample application. Create a Login View (which does Login with a Button or whatever) and create a Content View with a Logout Button using the LoginSuccessful/ Logout Methods.
This will solve your issue with a minimum of Code and classes. Hope this will be helpful to you.
I've had a go at creating something that basically works but probably needs a bit more work to be really usable. The fully comments and source can be found on this post Caliburn.Micro Login Window sample on my website.
I used the IEventAggregator of Caliburn.Micro to control the transition between the two windows. You get this code to open the login screen:
public void Handle(LoginEvent message)
{
LoginWindow loginWindow = new LoginWindow();
loginWindow.Login += new EventHandler<LoginEventArgs>(this.LoginWindow_Login);
loginWindow.Cancel += new EventHandler(LoginWindow_Cancel);
loginWindow.ShowDialog();
}
this same source is used for both the first time the app opens and when the Logout event is published. the Logout event looks like this:
public void Handle(LogoutEvent message)
{
Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
message.Source.TryClose();
Application.Current.ShutdownMode = ShutdownMode.OnLastWindowClose;
this.events.Publish(new LoginEvent());
}
When a login is successful it uses this code to open the main window which is based on a ViewModel:
ContentViewModel viewModel;
viewModel = IoC.Get<ContentViewModel>();
viewModel.Username = e.Username;
this.windowManager.ShowWindow(viewModel);