How to appply a template to a generic control? - xamarin.forms

I have a generic control:
public class BarChart<T> : ContentView
{
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
}
}
and an associated template:
<ControlTemplate x:Key="BarChartControlTemplate">
[...]
</ControlTemplate>
<Style TargetType="{x:Type controls:BarChart`1}">
<Setter
Property="ControlTemplate"
Value="{StaticResource BarChartControlTemplate}" />
</Style>
Apparently, what I am trying to do is not possible (the method OnApplyTemplate() is never called).
If I specialize my control (like this for instance public class IntBarChart : ContentView), there is obviously no problem.
But it's not a solution that suits me (if you tell me it's the only solution, I'll go with it, but I'll think it's a shame).
I thought of making a base control (not generic off course) but again, no method is called.
public class BarChart : ContentView
{
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
}
}
public class BarChart<T> : BarChart
{
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
}
}
Is there a way to apply a global template to a generic control?
Thanks in advance.

You can refer to the document Xamarin.Forms control templates and it gives the way to make the custom control. In addition, I used the generic way to use the ControlTemplate and it can not work. This is not an effective way to custom the templates.

Related

Proper Page.Loaded event in Xamarin.Forms

For the past 2 month I have been searching tirelessly for a way to implement a proper Page.Loaded event when using Xamarin.Forms but I couldn't implement or find a way to do it.
Most people suggest overriding Page.OnAppearing or adding an event handler for Page.Appearing both of which are not the answers or the proper way to achieve the desired effect and don't event behave as a real Page.Loaded event would.
I would like to know the following:
Why doesn't Xamarin.Forms have a built-in Page.Loaded event?
Is there's a work around?
Can I implement it from the native side?
Edit:
What I mean by "proper Page.Loaded" event is:
It must be called ONCE AND ONLY ONCE the page has loaded all of it's controls, laid them out, initialized them and rendered them for the first time.
It must NOT be called when returning from modal pages.
1.Why not load the data/controls in the constructor of the ContentPage? The constructor method is call only once and it is also called before Page.OnAppearing.
Can I implement it from the native side?
Yes, I think you can.
In iOS, override the ViewDidLoad method in custom renderer:
[assembly:ExportRenderer (typeof(ContentPage), typeof(MyPageRenderer))]
namespace App487.iOS
{
public class MyPageRenderer : PageRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
//call before ViewWillAppear and only called once
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
}
}
}
In Android, try to override the OnAttachedToWindow method:
[assembly: ExportRenderer(typeof(ContentPage), typeof(MyPageRenderer))]
namespace App487.Droid
{
public class MyPageRenderer : PageRenderer
{
public MyPageRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
{
base.OnElementChanged(e);
}
protected override void OnAttachedToWindow()
{
base.OnAttachedToWindow();
}
}
}
Currently Xamarin.Forms doesn't not provide a proper/complete life cycle events to fulfill all specific requirements, but things are improving, the Dev team is currently working on to address this issue, below mentioned issues and recent pull request on the official GitHub Repos (you may follow, get ideas and maybe implement it yourself before they even ship it), they will for sure provide that in the future, although it is not clear when it will be ready.
Specification: Enhancement Add better life cycle events #2210.
Issue: LifeCycle events for controls #556.
Pull request: Life cycle events for controls
GitHub Branch where currently working on.
MAUI repo (Next evolution of Xamarin) Cross-Platform LifeCycle.
Specification Add Loaded/Unloaded to VisualElement.

SysOperation Framework - CanGoBatchJournal

When canGoBatchJournal returns true, a RunBaseBatch can be created in Ax via the System administartion > Inquiries > Batch > New > Task > New >[ClassName:MyRunBaseBatch].
I have a couple of features which have been created using the SysOperation framework however. This method doesn't inherit the canGoBatchJournal method. Is there a way to make them visible in the above mentioned menu as well?
I took a dive into how to form control retrieves it's data. There is an SysOperationJournaledParametersAttribute attribute which you can use.
Below is an example of how the attribute would be applied to a controller. This example shows how the controller calls the custom service. The controller can then be used in as a batch task or you could call the controller from a menu to get the batch dialog to display.
[SysOperationJournaledParametersAttribute(true)]
class YourCustomController extends SysOperationServiceController
{
public void new()
{
super();
this.parmClassName(classStr(YourCustomService));
this.parmMethodName(methodStr(YourCustomService,processOperation));
this.parmDialogCaption("dialog caption");
}
public ClassDescription caption()
{
return "class description";
}
public static void main(Args args)
{
YourCustomController controller;
controller = new YourCustomController();
controller.startOperation();
}
}
Below would be the custom service the controller calls.
class YourCustomToolService extends SysOperationServiceBase
{
public void processOperation()
{
// Call your code to do run your custom logic
}
}
If you implement the SysOperation framework, it should already be good as SysOperationController implements the Batchable interface.
You can refer to this white paper: https://www.microsoft.com/en-us/download/details.aspx?id=29215

ASP.NET User Control inner content

I have a user control which accepts a title attribute. I would also like that input inner HTML (ASP Controls also) inside of that user control tag like so:
<uc:customPanel title="My panel">
<h1>Here we can add whatever HTML or ASP controls we would like.</h1>
<asp:TextBox></asp:TextBox>
</uc:customPanel>
How can I achieve this? I have the title attribute working correctly.
Thanks.
Implement a class that extends Panel and implements INamingContainer:
public class Container: Panel, INamingContainer
{
}
Then, your CustomPanel needs to expose a property of type Container and another property of type ITemplate:
public Container ContainerContent
{
get
{
EnsureChildControls();
return content;
}
}
[TemplateContainer(typeof(Container))]
[TemplateInstance(TemplateInstance.Single)]
public virtual ITemplate Content
{
get { return templateContent; }
set { templateContent = value; }
}
Then in method CreateChildControls(), add this:
if (templateContent != null)
{
templateContent.InstantiateIn(content);
}
And you will be using it like this:
<uc:customPanel title="My panel">
<Content>
<h1>Here we can add whatever HTML or ASP controls we would like.</h1>
<asp:TextBox></asp:TextBox>
</Content>
</uc:customPanel>
You need to make sure EnsureChildControls is called. There are number of ways of doing that such as through the CreateChildControls base method but you can simply do this to get the inner contents of a custom control to render. It gets more complicated when you need to remember state and trigger events but for plain HTML this should work.
protected override void Render(HtmlTextWriter writer) {
EnsureChildControls();
base.Render(writer);
}

Injecting Lower Layer Dependency in Presenter in an ASP.NET MVP Application

I recently read Phil Haack's post where he gives an example of implementing Model View Presenter for ASP.NET. One of the code snippets shows how the code for the view class.
public partial class _Default : System.Web.UI.Page, IPostEditView
{
PostEditController controller;
public _Default()
{
this.controller = new PostEditController(this, new BlogDataService());
}
}
However, here the view constructs the instance of the BlogDataService and passes it along to the presenter. Ideally the view should not know about BlogDataService or any of the presenter's lower layer dependencies. But i also prefer to keep the BlogDataService as a constructor injected dependency of the presenter as it makes the dependencies of the presenter explicit.
This same question has been asked on stackoverflow here.
One of the answers suggests using a service locator to get the instance of the BlogDataService and passing it along to the presenter's constructor.This solution however does not solve the problem of the view knowing about the BlogDataService and needing to explicitly get a reference to it.
Is there a way to automatically construct the presenter object using an IoC or DI container tool such that the view does not have to deal with explicitly creating the BlogDataService object and also injecting the view and service instances into the presenter's constructor. I prefer to use the constructor injection pattern as far as possible.
Or is there a better design available to solve the problem?. Can there be a better way to implement this If i am building a WinForms application instead of a ASP.NET WebForms application?
Thanks for any feedback.
Yes there is. For example using StructureMap in a webform constructor:
public partial class AttributeDetails : EntityDetailView<AttributeDetailPresenter>, IAttributeDetailView
{
public AttributeDetails()
{
_presenter = ObjectFactory.With<IAttributeDetailView>(this).GetInstance<AttributeDetailPresenter>();
}
....
}
and as you can see here presenter needs view and service injected
public AttributeDetailPresenter(IAttributeDetailView view, IAttributeService attributeService)
{
MyForm = view;
AppService = attributeService;
}
You can also use StructureMap BuildUp feature for webforms so that you can avoid using ObjectFactory directly in your view.
I did exactly this. The solution is based on Autofac, but can be implemented on top of any container.
First, define an interface representing the authority for presenting views in a request to the MVP system:
public interface IMvpRequest
{
void Present(object view);
}
Next, create a base page which has a property of that type:
public abstract class PageView : Page
{
public IMvpRequest MvpRequest { get; set; }
}
At this point, set up dependency injection for pages. Most containers have ASP.NET integration, usually in the form of HTTP modules. Because we don't create the page instance, we can't use constructor injection, and have to use property injection here only.
After that is set up, create event arguments representing a view which is ready to be presented:
public class PresentableEventArgs : EventArgs
{}
Now, catch the events in PageView and pass them to the request (present the page as well):
protected override bool OnBubbleEvent(object source, EventArgs args)
{
var cancel = false;
if(args is PresentableEventArgs)
{
cancel = true;
Present(source);
}
else
{
cancel = base.OnBubbleEvent(source, args);
}
return cancel;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
Present(this);
}
private void Present(object view)
{
if(MvpRequest != null && view != null)
{
MvpRequest.Present(view);
}
}
Finally, create base classes for each type of control you'd like to serve as a view (master pages, composite controls, etc.):
public abstract class UserControlView : UserControl
{
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
EnsureChildControls();
RaiseBubbleEvent(this, new PresentableEventArgs());
}
}
This connects the control tree to the MVP system via IMvpRequest, which you'll now have to implement and register in the application-level container. The ASP.NET integration should take care of injecting the implementation into the page. This decouples the page entirely from presenter creation, relying on IMvpRequest to do the mapping.
The implementation of IMvpRequest will be container-specific. Presenters will be registered in the container like other types, meaning their constructors will automatically be resolved.
You will have some sort of map from view types to presenter types:
public interface IPresenterMap
{
Type GetPresenterType(Type viewType);
}
These are the types you will resolve from the container.
(The one gotcha here is that the view already exists, meaning the container doesn't create the instance or ever know about it. You will have to pass it in as a resolution parameter, another concept supported by most containers.)
A decent default mapping might look like this:
[Presenter(typeof(LogOnPresenter))]
public class LogOnPage : PageView, ILogOnView
{
// ...
}

Alternatives to System.web.ui.page

I have an ASP.Net application and I've noticed through using profilers that there is a sizable amount of processing that happens before my page even runs. In my application, we don't used viewstate, asp.Net session, and we probably don't require most of the overhead that comes of a consequence of using the asp.net page lifecycle. Is there some other class I can easily inherit from that will cut out all the Asp.Net stuff, and just let my handle writing to the page by myself?
I've heard that ASP.Net MVC can cut down page loads considerably, simply because it doesn't use the old asp.net lifecycle, and handles pages differently. Is there an easy way, maybe by simply having my web pages inherit some other class to take advantage of something like this. I would like a solution that works in ASP.Net 2.0 if at all possible.
Most articles I found where talking about using the Page as a base class and implementing functionality on top of that, it looks like you need to make your own MyPage class that implements IHttpHandler
From the MSDN article
using System.Web;
namespace HandlerExample
{
public class MyHttpHandler : IHttpHandler
{
// Override the ProcessRequest method.
public void ProcessRequest(HttpContext context)
{
context.Response.Write("This is an HttpHandler Test.");
context.Response.Write("Your Browser:");
context.Response.Write("Type: " + context.Request.Browser.Type + "");
context.Response.Write("Version: " + context.Request.Browser.Version);
}
// Override the IsReusable property.
public bool IsReusable
{
get { return true; }
}
}
}
Again, from the article: To use this handler, include the following lines in a Web.config file.
<configuration>
<system.web>
<httpHandlers>
<add verb="*" path="handler.aspx" type="HandlerExample.MyHttpHandler,HandlerTest"/>
</httpHandlers>
</system.web>
</configuration>
I'd take a look at the source code for System.web.ui.page and take a look at what it does to guide you. My guess is that it mostly just calls the different methods in the asp.net page lifecycle in the right order. You'd do something similar by calling your own page_load from the ProcessRequest method. That would route to your individual implementation of your classes that implement your MyPage.
I've never thought of doing something like this before and it sounds pretty good since I really don't use any of the bloated webforms functionality. MVC may make this whole exercise futile, but it does seem pretty neat.
My Quick Example
new base:
using System.Web;
namespace HandlerExample
{
// Replacement System.Web.UI.Page class
public abstract class MyHttpHandler : IHttpHandler
{
// Override the ProcessRequest method.
public void ProcessRequest(HttpContext context)
{
// Call any lifecycle methods that you feel like
this.MyPageStart(context);
this.MyPageEnd(context);
}
// Override the IsReusable property.
public bool IsReusable
{
get { return true; }
}
// define any lifecycle methods that you feel like
public abstract void MyPageStart(HttpContext context);
public abstract void MyPageEnd(HttpContext context);
}
Implementation for the page:
// Individual implementation, akin to Form1 / Page1
public class MyIndividualAspPage : MyHttpHandler
{
public override void MyPageStart(HttpContext context)
{
// stuff here, gets called auto-magically
}
public override void MyPageEnd(HttpContext context)
{
// stuff here, gets called auto-magically
}
}
}
If you don't need all this "asp.net stuff", you may wish to implement a custom IHttpHandler. Afaik, there are no other standard IHttpHandlers to reuse except the Page class.
To do this you should start by looking at the System.Web.UI.PageHandlerFactory class and the corresponding System.Web.IHttpHandlerFactory interface.
From there you will probably look at the System.Web.IHttpHandler inferface and the System.Web.UI.Page class.
Basically you will write your own IHttpHandlerFactory that generates IHttpHandlers that handles the page requests.

Resources