I'm new to MVP design pattern so i'm following this guideā
So I'm trying to recreate the guide for a LoginModule (just for trying out the new pattern). Now the thing is i do everything the same as in the guide, but still i'm running into Interface implementation issues. Ill attach my code below:
Presenter class
public class UserPresenter
{
AuthenticateView iObjAuthView;
public void add(AuthenticateView ObjAuthView)
{
iObjAuthView = ObjAuthView;
}
public void Authenticate(User _model)
{
if (_model.UName == "" && _model.UPassword == "")
{
iObjAuthView.ResponseOnAuthenticate(AuthEnums.Authorized);
}
else if (_model.UName == "" && _model.UPassword != "")
{
iObjAuthView.ResponseOnAuthenticate(AuthEnums.InCorrectPassWord);
}
else
{
iObjAuthView.ResponseOnAuthenticate(AuthEnums.UserNotFound);
}
}
}
Interface class
public interface AuthenticateView
{
void ResponseOnAuthenticate(AuthEnums _authEnum);
}
UIpage(aspx page)
public partial class login : System.Web.UI.Page, AuthenticateView
{
private UserPresenter objPresenter = new UserPresenter();
protected void Page_Load(object sender, EventArgs e)
{
objPresenter.add(this);
}
protected void LoginBtn_Click(object sender, EventArgs e)
{
objPresenter.Authenticate(new StockBO.User(EmailBox.Text, PasswordBox.Text));
}
public void Auth(AuthEnums _auth)
{
Label3.Text = _auth.ToString();
}
}
On the UI page the compiler throws an Error that i don't implement the ResponseToAuthenticate method defined in the interface. So for the last or so I'm trying to figure out whats wrong.
Is it the guide that is wrong or is it me that is doing something wrong?
As the error states, you're bound to the contract that implementing the interface AuthenticateView implies.
Add your method ResponseOnAuthenticate(authEnum) { //... } to class login and you should be fine.
On a side note: use Pascal casing for class names (by convention)
Related
First off, I'm using Xamarin Studio 6.1.3 in case that makes any difference.
I'm creating a simple app and want a login form to appear as a sheet. I followed the Xamarin tutorial for creating sheets (https://developer.xamarin.com/guides/mac/user-interface/working-with-dialogs/#Creating_a_Custom_Sheet) but am running into an issue.
Per the tutorial, I have created a class:
using System;
using Foundation;
using AppKit;
namespace SampleProject
{
public partial class UserLoginController : NSViewController
{
private NSViewController _presentor;
public string Username
{
get { return TxtUsername.StringValue; }
set { TxtUsername.StringValue = value; }
}
public string Password
{
get { return TxtPassword.StringValue; }
set { TxtPassword.StringValue = value; }
}
public NSViewController Presentor
{
get { return _presentor; }
set { _presentor = value; }
}
public UserLoginController(IntPtr handle) : base(handle)
{
}
private void CloseDialog()
{
Presentor.DismissViewController(this);
}
partial void BtnCancelClick(NSObject sender)
{
RaiseDialogCanceled();
CloseDialog();
}
partial void BtnLoginClick(NSObject sender)
{
RaiseDialogAccepted();
CloseDialog();
}
public EventHandler DialogAccepted;
internal void RaiseDialogAccepted()
{
if (this.DialogAccepted != null)
this.DialogAccepted(this, EventArgs.Empty);
}
public EventHandler DialogCanceled;
internal void RaiseDialogCanceled()
{
if (this.DialogCanceled != null)
this.DialogCanceled(this, EventArgs.Empty);
}
}
}
And I have added an override for PrepareForSegue in my ViewController class:
public override void PrepareForSegue(NSStoryboardSegue segue, NSObject sender)
{
base.PrepareForSegue(segue, sender);
switch (segue.Identifier)
{
case "UserLoginSegue":
UserLoginController loginSheet = segue.DestinationController as UserLoginController;
loginSheet.Username = ""; //This line throws NullReferenceException unless I set a breakpoint and expand loginSheet.Base before allowing this line to execute.
loginSheet.Password = "";
loginSheet.Presentor = this;
loginSheet.DialogAccepted += (object s, EventArgs e) => { Console.WriteLine("OK Clicked"); };
loginSheet.DialogCanceled += (object s, EventArgs e) => { Console.WriteLine("Cancel Clicked"); };
break;
}
}
See the comment in the above code block. I basically set a breakpoint on that line and when it triggers, I inspect the loginSheet object. If I expand the Base object to inspect it and then continue execution, everything works as expected. If I don't, I get a NullReferenceException whenever code tries to access any fields/properties/methods in the UserLoginController class.
I am completely baffled as to why this is happening. I set a breakpoint in the constructor of UserLoginController and verified it is being called with a handle and that the base constructor should be called as well.
I've read through the tutorial several times and don't see anything that I'm missing. Can't seem to find anybody else having the same problem.
My ultimate question is: What can I do to make the code work as expected?
For the sake of learning (which may shed light on the problem): What is going on behind the scenes when I inspect the base object of my UserLoginController class while debugging?
When you expand an object in the debugger, all properties not marked up with certain attributes are read via reflection so that we can display them in the IDE.
Maybe one of those properties has a side effect? You should be able to reproduce the effect using reflection, then bisect the property list to see who's effecting your behavior.
The solution turned out to be that I need to check the View property of my UserLoginController.
I added the following line:
var theView = loginSheet.View;
and everything works as expected. I have yet to dig into the View property to see what it is doing behind the scenes that makes everything work.
Here is the modified, working PrepareForSegue override method:
public override void PrepareForSegue(NSStoryboardSegue segue, NSObject sender)
{
base.PrepareForSegue(segue, sender);
switch (segue.Identifier)
{
case "UserLoginSegue":
UserLoginController loginSheet = segue.DestinationController as UserLoginController;
var theView = loginSheet.View;
loginSheet.Username = "";
loginSheet.Password = "";
loginSheet.Presentor = this;
loginSheet.DialogAccepted += (object s, EventArgs e) => { Console.WriteLine("OK Clicked"); };
loginSheet.DialogCanceled += (object s, EventArgs e) => { Console.WriteLine("Cancel Clicked"); };
break;
}
}
I'm building a MVVM application in which a ToBeListened class has a couple of properties, PropertyA and PropertyB, and I want to listen to them.
public class ToBeListened : INotifyPropertyChanged
{
private double _propertyA;
private string _propertyB;
/*Here I'm omitting the update part where NotifyPropertyChanged gets called*/
public double PropertyA{get; set; }
public double PropertyB{get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Those two properties are listened by a Listener class, so I've implemented an EventHandler in it, that listens to a ToBeListened object.
public class Listener
{
private ToBeListened toBeListenedObject;
public Listener()
{
toBeListenedObject = new ToBeListened();
toBeListenedObject.PropertyChanged += newPropertyChangedEventHandler(PropertyListener_PropertyChanged);
}
private void PropertyListener_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch(e.PropertyName)
{
case "PropertyA":
{
/*...DO SOMETHING...*/
}
case "PropertyB":
{
/*...Do something else...*/
}
}
The thing is, I don't really like this solution I've found. A switch-case isn't polymorphism-friendly, so
is there a better way to do this? Maybe something that uses overloading? (Like private void PropertyListener_PropertyChanged(double sender, PropertyChangedEventArgs e)
most of all, is it right to code a ViewModel like this?
I like Josh Smith's PropertyObserver, which you can get at http://mvvmfoundation.codeplex.com/ (some documentation from Josh at https://joshsmithonwpf.wordpress.com/2009/07/11/one-way-to-avoid-messy-propertychanged-event-handling/). It's a nice class that encapsulates the plumbing logic you're talking about, so you can focus on just handling changes to certain properties. So in your case, you could write code like:
var observer = new PropertyObserver<ToBeListened>(toBeListenedObject)
.RegisterHandler(tbl => tbl.PropertyA, tbl => HandlePropertyA(tbl))
.RegisterHandler(tbl => tbl.PropertyB, tbl => HandlePropertyB(tbl));
You can start using it by installing the MVVM Foundation nuget package into your solution. The ID is MvvmFoundation.Wpf.
In the past I've used a little class derived from Dictionary<string, Action> for this purpose. It was something like this:
public class PropertyChangedHandler : Dictionary<string, Action>
{
public PropertyChangedHandler(INotifyPropertyChanged source)
{
source.PropertyChanged += Source_PropertyChanged;
}
private void Source_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Action toDo;
if (TryGetValue(e.PropertyName, out toDo))
{
toDo();
}
}
}
Then your listener looks like this:
public class Listener
{
private ToBeListened toBeListenedObject = new ToBeListened();
PropertyChangedHandler handler;
public Listener()
{
handler = new PropertyChangedHandler(toBeListenedObject)
{
{ "PropertyA", DoA },
{ "PropertyB", DoB }
};
}
private void DoB()
{
}
private void DoA()
{
}
}
This is just an example to give you an idea - it can be expanded for more complex purposes, of course.
i think MVVM Light framework (or library?) has what you need. Take a look at their ObservableObject class http://www.mvvmlight.net/help/SL5/html/d457231f-6af7-601d-fa1f-1fe7c9f60c57.htm
Basically what it does, is making your object observable.
I'm about to inject a repository instance into some Web.UI.WebControls.Image derived type:
public class CustomImageControl : Image
{
[Import]
public ICachedNameRepository Repo { get; set; } // Null reference here
private void DynamicImage_PreRender(object sender, EventArgs e)
{
ImageUrl = {some ICachedNameRepository usage}
}
}
Also here is my default page I have implemented for testing purposes:
public partial class _Default : Page
{
[Import]
public ICachedNameRepository Repo { get; set; } // Totally ok here
protected void Page_Load(object sender, EventArgs e)
{
{some ICachedNameRepository usage}
}
}
I have implemented container bootstraping according to official guide with respect of usage Control registering instead of Page:
private void BootStrapContainer()
{
var container = new Container();
container.Options.PropertySelectionBehavior = new ImportAttributePropertySelectionBehavior();
container.Register<ICachedNameRepository, CachedNameRepository>();
container.Register<CustomImageControl>(); // Also I have tried Control and Image types
container.Register<Page>();
var cc = container.GetInstance<CustomImageControl>(); // Correctly instantiated CachedNameRepository instance in Repo field in cc object
container.Verify(); // OK here
Global.Container = container;
}
I left ControlInitializerModule, ImportAttributePropertySelectionBehavior and InitializeHandler routines completely copypasted from guide mentioned earlier
At page loading I ended up with correctly resolved default page instance with CachedNameRepository injected into the right place, but my CustomImageControl suffering from null reference.
This can be done by hooking into the InitComplete event of the Page. This is the code I've used to prove this.
I changed CustomImageControl to inherit from UserControl:
public partial class CustomImageControl : UserControl
{
[Import]
public ICachedNameRepository Repo { get; set; }
private void DynamicImage_PreRender(object sender, EventArgs e)
{
}
}
Here's the updated InitializeHandler
public class Global : HttpApplication
{
private static Container container;
public static void InitializeHandler(IHttpHandler handler)
{
if (handler is Page)
{
Global.InitializePage((Page)handler);
}
}
private static void InitializePage(Page page)
{
container.GetRegistration(page.GetType(), true).Registration
.InitializeInstance(page);
page.InitComplete += delegate { Global.InitializeControl(page); };
}
private static void InitializeControl(Control control)
{
if (control is UserControl)
{
container.GetRegistration(control.GetType(), true).Registration
.InitializeInstance(control);
}
foreach (Control child in control.Controls)
{
Global.InitializeControl(child);
}
}
And the 2 other changes from the documentation. Be sure to call RegisterWebPagesAndControls in your bootstrapper
private static void RegisterWebPagesAndControls(Container container)
{
var pageTypes =
from assembly in BuildManager.GetReferencedAssemblies().Cast<Assembly>()
where !assembly.IsDynamic
where !assembly.GlobalAssemblyCache
from type in assembly.GetExportedTypes()
where type.IsSubclassOf(typeof(Page)) || type.IsSubclassOf(typeof(UserControl))
where !type.IsAbstract && !type.IsGenericType
select type;
pageTypes.ToList().ForEach(container.Register);
}
class ImportAttributePropertySelectionBehavior : IPropertySelectionBehavior
{
public bool SelectProperty(Type serviceType, PropertyInfo propertyInfo)
{
// Makes use of the System.ComponentModel.Composition assembly
return (typeof(Page).IsAssignableFrom(serviceType) ||
typeof(UserControl).IsAssignableFrom(serviceType)) &&
propertyInfo.GetCustomAttributes<ImportAttribute>().Any();
}
}
I'm very new to Caliburn Micro and would like to access ViewModel properties during OnExit.
public class AppBootstrapper : Bootstrapper<MainViewModel>
{
protected override void OnExit(object sender, EventArgs e)
{
if (mainViewModel.MyParam == 42)
{
}
base.OnExit(sender, e);
}
From the default WP7 template (without Caliburn) i'm used to have App.ViewModel, which is a static field with a singleton get accessor, where the viewmodel will be created on the first access.
(See next code snippet)
public partial class App : Application
{
private static MainViewModel viewModel = null;
public static MainViewModel ViewModel
{
get
{
// Delay creation of the view model until necessary
if (viewModel == null)
viewModel = new MainViewModel();
return viewModel;
}
set
{
viewModel = value;
}
}
Now i try to use Caliburn Micro 1.1 with a WPF project and don't know how this should be done.
I need access to the ViewModel during OnExit inside AppBootStrapper.
I assume, that this should be possible, because my AppBootstrapper is inherited from Bootstrapper, but can't find the right way of doing this..
Any hints, how this can be done in WPF are very welcome?
Thanks
Rob
Try
MainViewModel mainViewModel = IoC.Get<MainViewModel>();
Here is how it would look in your code:
public class AppBootstrapper : Bootstrapper<MainViewModel>
{
protected override void OnExit(object sender, EventArgs e)
{
// Get the Main View Model
MainViewModel mainViewModel = IoC.Get<MainViewModel>();
if (mainViewModel.MyParam == 42)
{
//Do work
}
base.OnExit(sender, e);
}
}
This assumes Two Things:
Your MainViewModel class is exporting typeof(MainViewModel) and not something different, like typeof(IShell)
You are using the default MEF implementation of C.M.
After searching a little bit more i think i've found the solution to my own question: Added SimpleContainer.cs from here: link
and added this to my AppBootstrapper code:
public class AppBootstrapper : Bootstrapper<MainViewModel>
{
private SimpleContainer container;
protected override void Configure()
{
container = new SimpleContainer();
container.RegisterSingleton(typeof(MainViewModel), null, typeof(MainViewModel));
container.RegisterSingleton(typeof(IWindowManager), null, typeof(WindowManager));
}
protected override object GetInstance(Type service, string key)
{
return container.GetInstance(service, key);
}
Would be great to hear some comment, whether this is ok or not.
I am implementing MVP pattern in my application.
But I am getting NullReferenceException on the Page_Load of my view class.
Here is my presenter class:
using Microsoft.Practices.CompositeWeb;
namespace PresenterDLL
{
public class NamePresenter : Presenter<IProduct>
{
public void SayHello()
{
View.Name = 200;
}
}
public interface IProduct
{
int Name { get; set; }
}
}
and here is code behind class of my view:
using System;
using PresenterDLL;
using Microsoft.Practices.ObjectBuilder;
public partial class _Default : BasePage, IProduct
{
private NamePresenter _presenter;
[CreateNew]
public NamePresenter Presenter
{
set
{
this._presenter = value;
_presenter.View = this;
}
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
this._presenter.OnViewInitialized();
this._presenter.SayHello();
}
this._presenter.OnViewLoaded();
}
public int Name
{
get
{
return 10;
}
set
{
TextBox1.Text = value.ToString();
}
}
}
while running the application I am getting NullreferenceException in the Page_Load method,
as _presenter is null. Because it's never called. So, what should i do so that ObjectBuilder can call it before the page life cycle begins..
My base page class is:
public class BasePage : Microsoft.Practices.CompositeWeb.Web.UI.Page
{
public BasePage()
: base()
{
// _ctlForm = this;
// WebClientApplication.BuildItemWithCurrentContext(this);
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
//Disable all caching
Response.CacheControl = "no-cache";
Response.AddHeader("Pragma", "no-cache");
Response.Expires = -1;
}
protected override void OnPreInit(EventArgs e)
{
//This has been moved to the top since the preinit in the base clase is responsible
//for dependency injections.
//ObjectFactory.BuildUp(this);
base.OnPreInit(e);
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
}
}
Can someone please figure out where is the problem...
I think you may have one of two following problems: setter of the Presenter property is not called at all or it is called but null is assigned. I think you should try to put a break point in the setter of Presenter property to see what is happening.
You can try overriding PreInit (http://dotnetchris.wordpress.com/2009/02/16/creating-a-generic-model-view-presenter-framework/):
protected override void OnPreInit(EventArgs e)
{
ObjectFactory.BuildUp(this);
base.OnPreInit(e);
}