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);
}
Related
Starting from the Caliburn Micro "Simple MDI" example. I would like to achieve the following:
I would like to share the reference of a class between the ViewModels. The shareme.count should be passed as reference to all the ViewModels. This would allow me to change it from within each ViewModel.
How can I achieve this close to Caliburn Micro convention?
ShellViewModel.cs
public class ShellViewModel : Conductor<IScreen>.Collection.OneActive {
SharedClass _shareme;
public SharedClass shareme {
get { return _shareme; }
set {
_shareme = value;
NotifyOfPropertyChange(() => shareme);
}
}
public ShellViewModel() {
shareme.count = 1;
}
public void OpenTab() {
ActivateItem(new TabViewModel {
DisplayName = "Tab " + shareme.count++
});
}
TabViewModel.cs
public class TabViewModel : Screen {}
AppBootstrapper.cs
public class AppBootstrapper : BootstrapperBase {
SimpleContainer container;
public AppBootstrapper() {
Initialize();
}
protected override void Configure() {
container = new SimpleContainer();
container.Singleton<IWindowManager, WindowManager>();
container.Singleton<IEventAggregator, EventAggregator>();
container.PerRequest<IShell, ShellViewModel>();
}
protected override object GetInstance(Type service, string key) {
var instance = container.GetInstance(service, key);
if (instance != null)
return instance;
throw new InvalidOperationException("Could not locate any instances.");
}
protected override IEnumerable<object> GetAllInstances(Type service) {
return container.GetAllInstances(service);
}
protected override void BuildUp(object instance) {
container.BuildUp(instance);
}
protected override void OnStartup(object sender, System.Windows.StartupEventArgs e)
{
DisplayRootViewFor<IShell>();
}
}
The will also serve as answer to your duplicate question.
EventAggregator isn't just for "events" you can message pass data to any or all viewmodels that are listening for message or event signature in question.
public class ViewModelA : Screen, IHandle<ShareMeMessageA>
{
private readonly IEventAggregator _events;
private int _sharemecount;
public class ViewModelA(IEventAggregator events){
_events = events;
_events.Subscribe(this);
}
//... other bits out for brevity
//-- EDIT --
public void SomeEventClick(){
_event.PublishOnUiThread(new ShareMeMessageB(){ ... etc ... });
}
protected override void Deactivated(bool close){
_events.Unsubscribe(this);
}
private void Handle(ShareMeMessageA msg)
{
if(msg != null)
sharemecount = msg.Count;
}
}
as this is just an example you don't have to pass the class object at all you can pass any type you want bool, int, float, etc..
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 have created a custom binding for the FocusChange event on an EditText using MvvmCross. I can get the event bound and firing, but I can't figure out how to pass the event args. My Custom Binding is this
using Android.Views;
using Android.Widget;
using Cirrious.MvvmCross.Binding;
using Cirrious.MvvmCross.Binding.Droid.Target;
using Cirrious.MvvmCross.Binding.Droid.Views;
using Cirrious.MvvmCross.ViewModels;
using System;
namespace MPS_Mobile_Driver.Droid.Bindings
{
public class MvxEditTextFocusChangeBinding
: MvxAndroidTargetBinding
{
private readonly EditText _editText;
private IMvxCommand _command;
public MvxEditTextFocusChangeBinding(EditText editText) : base(editText)
{
_editText = editText;
_editText.FocusChange += editTextOnFocusChange;
}
private void editTextOnFocusChange(object sender, EditText.FocusChangeEventArgs eventArgs)
{
if (_command != null)
{
_command.Execute( eventArgs );
}
}
public override void SetValue(object value)
{
_command = (IMvxCommand)value;
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
_editText.FocusChange -= editTextOnFocusChange;
}
base.Dispose(isDisposing);
}
public override Type TargetType
{
get { return typeof(IMvxCommand); }
}
protected override void SetValueImpl(object target, object value)
{
}
public override MvxBindingMode DefaultMode
{
get { return MvxBindingMode.OneWay; }
}
}
}
I wire it up in my ViewModel as this:
public IMvxCommand FocusChange
{
get
{
return new MvxCommand(() =>
OnFocusChange()
);
}
}
private void OnFocusChange()
{
//Do Something
}
Is there a way to do something like
public IMvxCommand FocusChange
{
get
{
return new MvxCommand((e) =>
OnFocusChange(e)
);
}
}
private void OnFocusChange(EditText.FocusChangeEventArgs e)
{
//Do Something
}
What I tried to do there doesn't work, but I was hoping that there was something similar that might work. I am able to pass the eventargs when the command fires in the custom binding with this line
_command.Execute( eventArgs );
I just can't figure a way to catch them in the ViewModel. Can anyone help me with this?
Jim
After trying many different arrangements, I found that the correct syntax to wire up your MvxCommand is
public IMvxCommand FocusChange
{
get
{
return new MvxCommand<EditText.FocusChangeEventArgs>(e => OnFocusChange(e));
}
}
private void OnFocusChange(EditText.FocusChangeEventArgs e)
{
if (!e.HasFocus)
{
//Do Something
}
}
Hope this helps!
I'm honestly not sure if databinding is the correct technique to achieve this, so if anyone could enlighten me I would be most grateful.
Basically what I'm trying to do is pass an object from the current page into a web user control as such (code simplified):
ExamplePage.aspx
<div>
<EC:AttachmentsView ID="AttachmentsView1" Attachments=<%# this.PageAttachments %> runat="server" />
</div>
ExamplePage.aspx.cs
public partial class ExamplePage : ProductBase
{
private LinkItemCollection _pageAttachments;
public LinkItemCollection PageAttachments
{
get { return _pageAttachments; }
}
public ExamplePage()
{
this.Load += new EventHandler(this.Page_Load);
}
protected void Page_Load(object sender, EventArgs e)
{
// Accessing and assigning attachments (EPiServer way)
_pageAttachments = CurrentPage["Attachments"] as LinkItemCollection ?? new LinkItemCollection();
}
}
The attachments view control has setters and getters for the Attachment and Label properties.
AttachmentsView.ascx.cs
namespace Example.Controls
{
[ParseChildren(false)]
public partial class AttachmentsView : EPiServer.UserControlBase
{
private string _label;
public string Label
{
get { return _label; }
set { _label = value; }
}
private LinkItemCollection _attachments;
public LinkItemCollection Attachments
{
get { return _attachments; }
set { _attachments = value; }
}
protected void Page_Load(object sender, EventArgs e)
{
if (_attachments == null)
{
_attachments = CurrentPage["DefaultAttachments"] as LinkItemCollection ?? new LinkItemCollection();
}
}
}
}
I'm at the stage where I would expect the page attachments from the ExamplePage to be passed into the AttachmentsView control but the _attachments property is null.
Is what I'm trying to do possible? Is databinding the right technique, if so does anyone know material which explains the concepts easier than the horrible MSDN documentation?
I know I can probably achieve this by programmatically rendering the control but I'd like to try this method first.
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)