MVVM pattern for deliving intermediate user input from ViewModel to Model - c#

I'm new to MVVM and am converting a WinForms project to WPF using the MVVM Light framework. Most introductions to MVVM emphasize that a business model should have no knowledge of a view model. So I'm modifying my business models to support my new view models through the addition of public properties and property-changed events.
But this feels awkward when I just want to get user input that I'm not going to save in the model. In WinForms, I would do it this way in my business model:
dlg.ShowDialog();
string someValue = dlg.SomeValue;
// Use someValue in a calculation...
Is this really anathema to MVVM:
window.ShowDialog();
string someValue = _ViewModelLocator.MyVm.SomeValue;
It saves me from having to create a public property in the business model for what only really needs to be a local variable.
Thanks for advice & insights.

Here's a post I wrote on unit testing a user-interaction (i.e. dialogs).
I recommend using an interface to wrap around your user interaction logic.
Leveraging a user interface with delegates will provide an object oriented solution.
The thought process is to unit test your user interaction without user intervention.
In addition, I added this implementation for discovery on Nuget.
I believe the class name on that dll that you want to use is called UserInteraction.
public delegate MessageBoxResult RequestConfirmationHandler(object sender, ConfirmationInteractionEventArgs e);
public interface IConfirmationInteraction
{
event RequestConfirmationHandler RequestConfirmation;
MessageBoxResult Confirm();
}
public class ConfirmationInteraction : IConfirmationInteraction
{
#region Events
public event RequestConfirmationHandler RequestConfirmation;
#endregion
#region Members
object _sender = null;
ConfirmationInteractionEventArgs _e = null;
#endregion
public ConfirmationInteraction(object sender, ConfirmationInteractionEventArgs e)
{
_sender = sender;
_e = e;
}
public MessageBoxResult Confirm()
{
return RequestConfirmation(_sender, _e);
}
public MessageBoxResult Confirm(string message, string caption)
{
_e.Message = message;
_e.Caption = caption;
return RequestConfirmation(_sender, _e);
}
}
}
public class ConfirmationInteractionEventArgs : EventArgs
{
public ConfirmationInteractionEventArgs() { }
public ConfirmationInteractionEventArgs(string message, string caption, object parameter = null)
{
Message = message;
Caption = caption;
Parameter = parameter;
}
public string Message { get; set; }
public string Caption { get; set; }
public object Parameter { get; set; }
}

Related

Get info from another window in Caliburn.Micro, WPF, MVVM

Im making Login Window in my app based on Caliburn.Micro mvvm framework. So, how to return an property (for example, true if user passed good data or false, if he pass bad credentials) from TryClose() method from my Login Window that is initialize by Caliburn.Micro? How to get information from window opened in IWindowManager.ShowDialog()?
First, my MainWindowViewModel.cs:
using Caliburn.Micro;
namespace TaskManager.ViewModels
{
class MainWindowViewModel : Conductor<IScreen>.Collection.OneActive
{
protected override void OnViewLoaded(object view)
{
IWindowManager manager = new WindowManager();
//Login page, context is data with user's lists
LoginViewModel loginView = new LoginViewModel(context);
manager.ShowDialog(loginView, null, null);
//here i want to get info, if i get logged properly or not
}
public void LoadUserInfoPage() //here starts "main" program
{
ActivateItem(new UserInfoViewModel());
}
//and so on...
}
}
My LoginViewModel.cs:
namespace TaskManager.ViewModels
{
class LoginViewModel : Screen
{
public string Login { get; set; }
public string Password { get; set; }
public LoginViewModel(FakeData context)
{
this.context = context;
}
public void LoginButton()
{
bool check = Services.Login.IsValid(Login, Password, context);
if(check) //if login is OK, check == true
TryClose();
}
private FakeData context { get; set; } //data is here
}
}
Then, my IsValid() Method:
namespace TaskManager.Services
{
static class Login
{
static public bool IsValid(string login, string password, FakeData context)
=> context.users.Any(i => i.Login == login);
//i know it is bad, but its only example
}
}
Buttons, opening and closing windows works great (reading from textboxes too). I want only get info (maybe by reference?) if user is pass good data.
THanks for your advices!
You can make use of EventAggregator for the purpose.
"An Event Aggregator is a service that provides the ability to publish
an object from one entity to another in a loosely based fashion. "
The first step would be create instance of EventAggregator in your ViewModels and subscribe to it. You can do it via DI in constructor of both ViewModels.
For LoginViewModel,
private IEventAggregator _eventAggregator;
public LoginViewModel(FakeData context,IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
}
And MainWindowViewModel,
private IEventAggregator _eventAggregator;
public MainWindowViewModel (IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
_eventAggregator.Subscribe(this);
}
The next step is to create a Message Object, which can transmit the required information between the ViewModels.
public class OnLoginAttemptMessage
{
string UserName { get; set; }
bool IsLoginSuccessful { get; set; }
}
Finally, it is time to put everything together. In youg LoginButton method in LoginViewModel, we modify the code to raise the event on successfull login.
public void LoginButton()
{
bool check = Services.Login.IsValid(Login, Password, context);
if(check) //if login is OK, check == true
{
_eventAggregator.PublishOnUIThread(new OnLoginAttemptMessage
{
UserName = Login,
IsLoginSuccessful = check;
});
TryClose();
}
}
The last step is in MainWindowViewModel, where you need to implement the IHandle interface.
class MainWindowViewModel : Conductor<IScreen>.Collection.OneActive, IHandle<OnLoginSuccessMessage>
{
public void Handle(OnLoginSuccessMessage message)
{
if(message.IsLoginSuccessful)
{
// Login is successfull, do next steps.
}
}
}
You can read more on EventAggregator here (https://caliburnmicro.com/documentation/event-aggregator)

MVVM - Where to call function on button click?

So I am a little confused as to how the MVVM architecture can help me and how to use it in this situation:
I am using Xamarin and have created my view and view controller in iOS as an example. I have implemented MVVMLight toolkit as well, and have created my ViewModel for the view and view controller.
I am creating a login screen, so the user inputs their username and password and they are updated in the model through RaisePropertyChanged() events. My question is where I need to call the function to validate this information and actually log them into the system?
I have implemented a RelayCommand that will call a method on the ViewModel whenever the button is clicked as I have seen in other tutorials and such, but I am not sure if I am supposed to call the validation code here.
Some examples of what I have:
LoginViewModel.cs:
public class LoginViewModel : ViewModelBase
{
private string _username;
private string _password;
public RelayCommand LoginButtonCommand { get; private set; }
public bool CanExecuteLoginCommand { get; set; }
public LoginViewModel()
{
LoginButtonCommand = new RelayCommand(HandleLoginButtonCommand, () => CanExecuteLoginCommand);
CanExecuteLoginCommand = true;
}
public string Username
{
get
{
return _username;
}
set
{
_username = value;
RaisePropertyChanged(() => Username);
}
}
public string Password
{
get
{
return _password;
}
set
{
_password = value;
RaisePropertyChanged(() => Password);
}
}
private void HandleLoginButtonCommand()
{
CanExecuteLoginCommand = false;
//Validate login?
CanExecuteLoginCommand = true;
}
}
LoginViewController.cs:
public partial class LoginViewController : UIViewController
{
private Binding _usernameTextFieldBinding;
private Binding _passwordTextFieldBinding;
private LoginViewModel _viewModel;
public LoginViewController(IntPtr handle) : base(handle)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
_viewModel = Application.Locator.Login;
HideKeyboardHandling(UsernameTextField);
HideKeyboardHandling(PasswordTextField);
_usernameTextFieldBinding = this.SetBinding(
() => _viewModel.Username)
.ObserveSourceEvent("EditingDidEnd")
.WhenSourceChanges(() => _viewModel.Username = UsernameTextField.Text);
_passwordTextFieldBinding = this.SetBinding(
() => _viewModel.Username)
.ObserveSourceEvent("EditingDidEnd")
.WhenSourceChanges(() => _viewModel.Password = PasswordTextField.Text);
Loginbutton.SetCommand("TouchUpInside", _viewModel.LoginButtonCommand);
}
public override void DidReceiveMemoryWarning()
{
base.DidReceiveMemoryWarning();
// Release any cached data, images, etc that aren't in use.
}
void HideKeyboardHandling(UITextField textField)
{
textField.ShouldReturn = TextField =>
{
TextField.ResignFirstResponder();
return true;
};
var gesture = new UITapGestureRecognizer(() => View.EndEditing(true));
gesture.CancelsTouchesInView = false;
View.AddGestureRecognizer(gesture);
}
}
It all depends on how strict you want to be with Single Responsibility Principle(SPR). Which in turn depends on how complex your application is. The more complex the application is, the more separated the responsibilities should be.
A typical MVVM implementation handles the commands in the ViewModel. And the ViewModel forwards the call into the Model. But his still puts two responsibilities(e.g. presentation and command handling) into a single component, a.k.a the ViewModel.
A more strict approach will be to have the ViewModel only handle presentation logic. Create a separate controller to host all the command handlers. And have the command handlers forward the calls to the Model.
A more relaxed approach will be to simply implement the business logic in the ViewModel. This implies you don't have a business logic layer. Which is fine if your application is simple enough that a business logic layer does not worth the effort.

Permission based security model

In a windows forms payroll application employing MVP pattern (for a small scale client) I'm planing user permission handling as follows (permission based) as basically its implementation should be less complicated and straight forward.
NOTE : System could be simultaneously used by few users (maximum 3) and the database is at the server side.
This is my UserModel. Each user has a list of permissions given for them.
class User
{
string UserID { get; set; }
string Name { get; set; }
string NIC {get;set;}
string Designation { get; set; }
string PassWord { get; set; }
List <string> PermissionList = new List<string>();
bool status { get; set; }
DateTime EnteredDate { get; set; }
}
When user log in to the system it will keep the current user in memory.
For example in BankAccountDetailEntering view, I can control the permission to access command button as follows.
public partial class BankAccountDetailEntering : Form
{
bool AccountEditable {get; set;}
public BankAccountDetailEntering ()
{
InitializeComponent();
}
private void BankAccountDetailEntering_Load(object sender, EventArgs e)
{
cmdEditAccount.enabled = false;
OnLoadForm (sender, e); // Event fires...
If (AccountEditable )
{
cmdEditAccount.enabled=true;
}
}
}
In this purpose my all relevant presenters (like BankAccountDetailPresenter) should aware of UserModel as well in addition to the corresponding business Model it is presenting to the View.
class BankAccountDetailPresenter
{
BankAccountDetailEntering _View;
BankAccount _Model;
User _UserModel;
DataService _DataService;
BankAccountDetailPresenter( BankAccountDetailEntering view, BankAccount model, User userModel, DataService dataService )
{
_View=view;
_Model = model;
_UserModel = userModel;
_DataService = dataService;
WireUpEvents();
}
private void WireUpEvents()
{
_View.OnLoadForm += new EventHandler(_View_OnLoadForm);
}
private void _View_OnLoadForm(Object sender, EventArgs e)
{
foreach(string s in _UserModel.PermissionList)
{
If( s =="CanEditAccount")
{
_View.AccountEditable =true;
return;
}
}
}
public Show()
{
_View.ShowDialog();
}
}
So I'm handling the user permissions in the presenter iterating through the list. Should this be performed in the Presenter or View? Any other more promising ways to do this?
Thanks.
"The presenter acts upon the model and the view. It retrieves data from repositories (the model), and formats it for display in the view." - MVP
So the presenter formats the data, but for me it looks like presenter contains kind of business logic - it really checks if user can modify account. What if you forget this check in one of the forms? So it should be in the underlying layer (probably, service).

Passing Data from WinForm to WPF

I'm looking to pass data to a WPF window from a WinForm and receive a message back from the WPF window.
My code is a mix of random online tutorials and HighCore's log viewer. I have a WinForm that launches my new WPF window in the following fashion:
private void openTransactionViewToolStripMenuItem_Click(object sender, EventArgs e)
{
var transactionViewWindow = new TransactionViewer.MainWindow();
ElementHost.EnableModelessKeyboardInterop(transactionViewWindow);
transactionViewWindow.Show();
transactionViewWindow.Test = "test"; // testing out data passing
transactionViewWindow.AddTest();
}
My MainWindow.xaml.cs looks like:
public partial class MainWindow : Window
{
public ObservableCollection<Session> SessionList { get; set; }
public string Test{ get; set; }
public MainWindow()
{
InitializeComponent();
SessionList = new ObservableCollection<Session>();
SessionList.Add(new Session() { BeginLine = 0, EndLine = 1, Message = "some message" });
SessionList.Add(new Session() { BeginLine = 2, EndLine = 3, Message = "another message" });
SessionItems.ItemsSource = SessionList; // the ItemsControl
}
public void AddTest()
{
SessionList.Add(new Session() { BeginLine = 4, EndLine = 5, Message = Test });
}
}
public class Session : PropertyChangedBase
{
public int BeginLine { get; set; }
public int EndLine { get; set; }
public string Message { get; set; }
}
where PropertyChangedBase inherits from INotifyPropertyChanged. I have an ItemsControl bound to Message. My output looks like:
some message
another message
test
"Data passing" is successful! Eventually, when the WPF window loads I want to pass a List<Session> from my WinForm that will be used to populate the ItemsControl. I also want to have a button on the WinForm that will send a List to repopulate/refresh the data in the WPF. From the current behaviour I think this will be possible even with my current, simple implementation (just updating SessionList).
Is there a more appropriate way of doing this? Events, for example? Do I need to fire off an event in order to tell my WinForm that the WPF has successfully added all Session objects, or whenever a user clicks on a specific one?
Any benefit to using MVVM here?
I've been developing for WinForms for a while and finding the transition to WPF quite confusing. Hopefully someone can help out with some guidance or code examples.
Edit: for future reference, a decent MVVM tutorial targeted to people like me can be found here: http://jpreecedev.com/2013/06/08/wpf-mvvm-for-winforms-devs-part-1-of-5/
You approach seems OK to me. It's not perfect, but it is workable enough.
An optimal approach, IMO, would be to create a ViewModel for the WPF Window, instead of directly referencing the Window itself when passing data back and forth.
The idea is:
public class MyForm: Form
{
public TransactionViewerViewModel TransactionViewer {get;set;}
//... other code...
//Form constructor:
public MyForm()
{
InitializeComponent();
//Create ViewModel:
TransactionViewer = new TransactionViewerViewModel();
}
private void openTransactionViewToolStripMenuItem_Click(object sender, EventArgs e)
{
//Create WPF View:
var transactionViewWindow = new TransactionViewer.MainWindow();
//Interop code
ElementHost.EnableModelessKeyboardInterop(transactionViewWindow);
//Set DataContext:
transactionViewWindow.DataContext = TransactionViewer;
//Show Window:
transactionViewWindow.Show();
//Call methods on the ViewModel, rather than the View:
TransactionViewer.Test = "test"; // testing out data passing
TransactionViewer.AddTest();
}
}
So, the ViewModel would be something like:
public class TransactionViewerViewModel : PropertyChangedBase
{
public ObservableCollection<Session> SessionList { get; set; }
public string Test{ get; set; }
public TransactionViewerViewModel()
{
SessionList = new ObservableCollection<Session>();
SessionList.Add(new Session() { BeginLine = 0, EndLine = 1, Message = "some message" });
SessionList.Add(new Session() { BeginLine = 2, EndLine = 3, Message = "another message" });
}
public void AddTest()
{
SessionList.Add(new Session() { BeginLine = 4, EndLine = 5, Message = Test });
}
}
This achieves a perfect separation between the WPF UI and the actual data / business logic, to the point that you can even create Unit Tests for your ViewModel.
And, since you're setting the ViewModel as the Window's DataContext, you will need to access all your ViewModel properties via DataBinding, rather than procedural code:
<ItemsControl ItemsSource="{Binding SessionList}"/>
Then, you may want to introduce delegates or events in the ViewModel, and listen to these in your Form, thus achieving WPF => winforms communication.

Using Unity I wish to check a bool flag before setting value to the properties in a Type?

I wish to implement suspend and resume binding in silverlight using a flag while doing heavy operation in a binding scenario.
My problem is in the existing project uses heavy binding drawing mechanismm.
Using UpdateTrigger() in silverlight we could achieve manual triggering of binding.
But its a huge product. Its not possible to update and find the locations for manual triggering of binding and so on and change the project.
So we planned to use Microsoft Unity to quickly fix by suspend and resume binding using a flag on heavy load drawing operation. This may be quick fix for the current performance issue while drawing binding objects.
I wish to check a bool flag before setting value to the properties for different Type?
I googled so much and tired to find Interception before property setter. But not found a way. Still fighting. This is my exact requirement.
Anybody to help?
Added the sample code trying,
//Empty Interface may be used in interface interception, not sure.
public interface ISetter
{
}
//Implementation of ISetter, this type needs to be intercepted while setting the FirstName //property
public class Man : ISetter
{
private string firstName;
public Man()
{
}
[NotifyHandler] //Expected: this handler should be called when FirstName property set
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
}
public class NotifyHandler : ICallHandler
{
public Boolean Before { get; set; }
public Boolean After { get; set; }
public String Message { get; set; }
int ICallHandler.Order { get; set; }
IMethodReturn ICallHandler.Invoke(IMethodInvocation input,
GetNextHandlerDelegate getNext)
{
if (this.Before == true)
{
Debug.WriteLine(this.Message);
}
IMethodReturn result = getNext()(input, getNext);
if (result.Exception != null)
{
Debug.WriteLine(result.Exception.Message);
}
else
{
if (this.After == true)
{
Debug.WriteLine(this.Message);
}
}
return (result);
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property
| AttributeTargets.Method)]
Public class NotifyHandlerAttribute : HandlerAttribute
{
public NotifyHandlerAttribute()
{
}
public override ICallHandler CreateHandler(IUnityContainer ignored)
{
return new NotifyHandler();
}
}
public abstract class HandlerAttribute : Attribute
{
/// Derived classes implement this method. When called, it creates a
/// new call handler as specified in the attribute configuration.
/// The parameter "container" specifies the IUnityContainer
/// to use when creating handlers, if necessary.
/// returns a new call handler object.
public abstract ICallHandler CreateHandler(IUnityContainer container);
private int executionorder;
/// <summary>
/// Gets or sets the order in which the handler will be executed.
/// </summary>
public int Order
{
get { return this.executionorder; }
set { this.executionorder = value; }
}
}
//Interception registered in the application start up
private void Application_Startup(object sender, StartupEventArgs e)
{
IUnityContainer container = new UnityContainer();
container.AddNewExtension<Interception>();
container.RegisterType<Man>().Configure<Interception>();
var m1 =container.Resolve<Man>();
m1.FirstName = "test";
Man m = new Man();
m.FirstName = "fine";
}
In above both the FirstName setter property does not call the NotifyHandler.

Categories

Resources