Using Visual Studio, when selecting 'Zebble for Xamarin - Cross Platform Solution' a default project will be created with five pages. I've modified the fifth page to implement a signature pad. Below is the following Page-5.zbl code.
<?xml version="1.0"?>
<z-Component z-type="Page5" z-base="Templates.Default" z-namespace="UI.Pages"
z-partial="true" Title="About us" data-TopMenu="MainMenu" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="./../.zebble-schema.xml">
<z-place inside="Body">
<TextView Text="Hello world!" />
<SignaturePad Id="sigPad1" Enabled="true" LineThickness="4" Style.Border.Color="red" Style.Width="100" Style.Height="100"/>
</z-place>
</z-Component>
Which ends up adding this line to .zebble-generated.cs:
await Body.Add(sigPad1 = new SignaturePad { Id = "sigPad1", Enabled = true, LineThickness = 4 }
.Set(x => x.Style.Border.Color = "red")
.Set(x => x.Style.Width = 100)
.Set(x => x.Style.Height = 100));
I have been looking at this SignaturePad component package: https://github.com/xamarin/SignaturePad
If I wanted to use the Xamarian SignaturePad component or anyone else's SignaturePad component instead of the Zebble SignaturePad UI component, how would I do that?
To use a third party component, all you need to do is to create a Zebble wrapper around it. It's explained here:
http://zebble.net/docs/customrenderedview-third-party-native-components-plugins
Step 1: Creating Native Adapter(s)
You should first create a Zebble view class to represent an instance of your component using the following pattern. This class will be in the Shared project, available to all 3 platforms.
namespace Zebble.Plugin
{
partial class MyComponent : CustomRenderedView<MyComponentRenderer>
{
// TODO: Define properties, method, events, etc.
}
}
Note: To make the VS IntelliSense in ZBL files recognize this, you should create a .ZBL file for MyComponent as well:
<z-Component z-type="MyComponent" z-base="CustomRenderedView[MyComponentRenderer]" z-namespace="Zebble.Plugin"
z-partial="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="./../.zebble-schema.xml" />
The next step will be to create the renderer classes.
Step 2: Creating Native Renderers(s)
You need to create the following class each platform (UWP, iOS, Android).
public class MyComponentRenderer : ICustomRenderer
{
MyComponent View;
TheNativeType Result;
public object Render(object view)
{
View = (MyComponent)view;
Result = new TheNativeType();
// TODO: configure the properties, events, etc.
return Result;
}
public void Dispose() => Result.Dispose();
}
Using it in the application code
In the application code (App.UI) you can use MyComponent just like any other built-in or custom view type.
<Zebble.Plugin.MyComponent Id="..." Property1="..." on-Event1="..." />
I am using MVVMCross, and have a problem with MvxDialogFragment bindings.
I have a base service, which is resolved in Core PCL project, add custom services implementations in iOS and Android projects derived from the base service class.
In android service i construct and show MvxDialogFragment instance:
var top = (MvxFragmentActivity)Mvx.Resolve<IMvxAndroidCurrentTopActivity>().Activity;
if (top == null)
{
throw new MvxException("Cannot get current top activity");
}
var dlg = new AlertDialog.Builder(top);
dlg.Create().Show();
dialog = new MyDialog
{
Cancelable = false
};
dialog.Show(top.SupportFragmentManager, "");
And i have simple dialog layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/test_click_button"
android:text="Test"
app:MvxBind="Click TestClickCommand" />
</LinearLayout>
So my goal is to acces base service commands from dialogFragment, which is instantiated from service. How can i do that?
As an alternative, i want to handle my button click in service, but cannot find a way to do this, because my View, ViewModel or Dialog properties are null.
How it's possible to handle clicks in service, or implement self binding?
Finally i achieved the desired, through MvxDialogFragment subscription, and service injection:
public class MyDialog : MvxDialogFragment
{
private ISampleService _sampleService;
public MyDialog(ISampleService sampleService)
{
_sampleService = sampleService;
}
public override Dialog OnCreateDialog(Bundle savedInstanceState)
{
EnsureBindingContextSet(savedInstanceState);
var dialog = new AlertDialog.Builder(Activity);
var view = this.BindingInflate(Resource.Layout.MyDialog, null);
view.FindViewById(Resource.Id.test_click_button).Click += (sender, e) =>
{
_sampleService.TestClick();
Dismiss();
};
dialog.SetView(view);
return dialog.Create();
}
}
I am using stacklayout panel for the first time.
I read the gwt docs and found that parent for this should be a type of Layout.
Below is my code for parent widget("EncounterViewImpl") which creates a TabLayout panel and my widget "StudyViewImpl" is on one of the tabs of this tablayout.
EncounterViewImpl.ui.xml(Parent)
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder
xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui"
xmlns:hpi="urn:import:com.zoomcare.emrgwt.client.ui.encounter.hpi"
xmlns:history="urn:import:com.zoomcare.emrgwt.client.ui.encounter.history"
xmlns:medication="urn:import:com.zoomcare.emrgwt.client.ui.encounter.medication"
xmlns:physical="urn:import:com.zoomcare.emrgwt.client.ui.encounter.physical"
xmlns:diagnosis="urn:import:com.zoomcare.emrgwt.client.ui.encounter.diagnosis"
xmlns:wellness="urn:import:com.zoomcare.emrgwt.client.ui.encounter.wellness"
xmlns:study="urn:import:com.zoomcare.emrgwt.client.ui.encounter.study"
xmlns:dental="urn:import:com.zoomcare.emrgwt.client.ui.encounter.dental">
<g:TabLayoutPanel barUnit="PX" barHeight="30" width="100%" height="100%">
<g:tab visible="false">
<g:header>
Dental
</g:header>
<g:ScrollPanel width="98%" height="100%">
<dental:EncounterDentalViewImpl ui:field="encounterDentalView"/>
</g:ScrollPanel>
</g:tab>
<g:tab visible="false">
<g:header>
Study
</g:header>
<g:ScrollPanel width="98%" height="100%">
<study:StudyViewImpl ui:field="studyView"/>
</g:ScrollPanel>
</g:tab>
</g:TabLayoutPanel>
StudyViewImpl.ui.xml(Child)
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'>
<g:StackLayoutPanel height="100%" width="98%" ui:field="stackPanel" >
</g:StackLayoutPanel>
I want to add the widgets to stackpanel dynamically by its corresponding Activity class .StudyActivity calls setStudies(List list) after getting service response
Below is the code ..
StudyViewImpl.java
public class StudyViewImpl extends Composite implements StudyView {
private static LocalUiBinder uiBinder = GWT.create(LocalUiBinder.class);
interface LocalUiBinder extends UiBinder<StackLayoutPanel, StudyViewImpl> {
}
private StudyInfoActivityPiece presenter;
#UiField
StackLayoutPanel stackPanel ;
public StudyViewImpl() {
initWidget(uiBinder.createAndBindUi(this));
//stackPanel.setWidth("800");
}
public void setStudies(List<ScheduledStudyGWT> studies){
for(ScheduledStudyGWT study : studies) {
VerticalPanel vPanel = new VerticalPanel();
vPanel.setWidth("100%");
vPanel.setHeight("100%");
StudyInfoWidget infoWidget = new StudyInfoWidget();
infoWidget.setWidth("100%");
infoWidget.setHeight("100%");
infoWidget.populate(study);
infoWidget.setReadOnly(false);
ExamNoteWidget examNoteWidget = new ExamNoteWidget();
examNoteWidget.setWidth("100%");
examNoteWidget.setHeight("100%");
examNoteWidget.setExamNote(study.getExamNote());
examNoteWidget.setReadOnly(false);
InstructionsWidget instructionsWidget = new InstructionsWidget();
instructionsWidget.setInstructions(study.getInstructions());
instructionsWidget.setWidth("100%");
instructionsWidget.setHeight("100%");
instructionsWidget.setReadOnly(false);
vPanel.setVisible(true);
vPanel.add(infoWidget);
vPanel.add(examNoteWidget);
vPanel.add(instructionsWidget);
stackPanel.add(vPanel,createHeaderWidget(study.getOrderedLab().getLab().getName()),30);
}
}
private Widget createHeaderWidget(String text) {
HorizontalPanel hPanel = new HorizontalPanel();
hPanel.setHeight("100%");
hPanel.setSpacing(0);
hPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
HTML headerText = new HTML(text);
hPanel.add(headerText);
return new SimplePanel(hPanel);
}
}
When I run the above code I see that setStudies method is called by passing a proper list of objects but on the browser i see only headers.
Please help me figuring out the issue.
I got the issue fixed using StackPanel.I am running in quirk mode so shifted to StackPanel instead of StackLayoutPanel which is supported only in standard mode.
How to Integrate Struts Conventions with Tiles while maintaining conventions benefits?
The issue is that conventions links url-to-action-to-result automatically and does this nicely for jsp, velocity and freemarker results. It does not expect to deal with a tiles result.
When using tiles we typically want all our UI actions (as opposed the json/xml service actions) to use tiles but in doing so we lose the convention for the result component and need to use annotations. Annotations allow us to deviate from the expected, but in a large application when expecting to use tiles this is an annoyance. Further conventions allows us to create actions by only specifying a view. We would want to retain such benefit when using tiles as well. To rectify this we need to establish a convention that carries though to the tiles result such that we don't need to use annotations to tie the action to the tiles result and that we can continue to create JSPs without actions classes which will gain the benefits of conventions (no xml) and the benefits of tiles (all the boiler plate is factored into tiles).
How to achieve this?
This is a self answer to help others who wish to address this issue
Here are the steps needed:
Create custom tiles result which dynamically builds a "location" string (the location string is the value passed to tiles) which takes into account the namespace, actionName.
Create a package which uses this result (named "tiles") and have conventions use that as it's parent package
Implement and register a "com.opensymphony.xwork2.UnknownHandler", this step is the most critical as this handler is called when the result can't be resolved
Tiles definition(s) which make use of "location" passed in from the first step
The above steps require the following in struts.xml
<struts>
<constant name="struts.convention.default.parent.package" value="tiles-package"/>
<bean type="com.opensymphony.xwork2.UnknownHandler" name="tilesUnknownHandler" class="com.kenmcwilliams.tiles.result.TilesUnknownHandler"/>
<package name="tiles-package" extends="convention-default">
<result-types>
<result-type default="true" name="tiles" class="com.kenmcwilliams.tiles.result.TilesResult"/>
</result-types>
</package>
</struts>
Custom result-type implementation:
package com.kenmcwilliams.tiles.result;
import com.opensymphony.xwork2.ActionInvocation;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.dispatcher.ServletDispatcherResult;
import org.apache.tiles.TilesContainer;
import org.apache.tiles.access.TilesAccess;
import org.apache.tiles.request.ApplicationContext;
import org.apache.tiles.request.servlet.ServletRequest;
import org.apache.tiles.request.servlet.ServletUtil;
public class TilesResult extends ServletDispatcherResult {
private static final Logger log = Logger.getLogger(TilesResult.class.getName());
public TilesResult() {
super();
}
public TilesResult(String location) {
super(location);
}
#Override
public void doExecute(String location, ActionInvocation invocation) throws Exception {
//location = "test.definition"; //for test
log.log(Level.INFO, "TilesResult doExecute() location: {0}", location);
//Start simple conventions
//
if (/** tiles && **/location == null) {
String namespace = invocation.getProxy().getNamespace();
String actionName = invocation.getProxy().getActionName();
location = namespace + "#" + actionName + ".jsp"; //Warning forcing extension
log.log(Level.INFO, "TilesResult namespace: {0}", namespace);
log.log(Level.INFO, "TilesResult actionName: {0}", actionName);
log.log(Level.INFO, "TilesResult location: {0}", location);
}
//End simple conventions
setLocation(location);
ServletContext context = ServletActionContext.getServletContext();
ApplicationContext applicationContext = ServletUtil.getApplicationContext(context);
TilesContainer container = TilesAccess.getContainer(applicationContext);
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
ServletRequest servletRequest = new ServletRequest(applicationContext, request, response);
container.render(location, servletRequest);
}
}
TilesUnknownHandler Implementation:
package com.kenmcwilliams.tiles.result;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.Result;
import com.opensymphony.xwork2.XWorkException;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.config.entities.ResultConfig;
import com.opensymphony.xwork2.config.entities.ResultConfig.Builder;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
import flexjson.JSONSerializer;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletContext;
import org.apache.commons.lang.StringUtils;
import org.apache.struts2.convention.ConventionUnknownHandler;
public class TilesUnknownHandler extends ConventionUnknownHandler {
private static final Logger log = Logger.getLogger(TilesUnknownHandler.class.getName());
private static final String conventionBase = "/WEB-INF/content";
#Inject
public TilesUnknownHandler(Configuration configuration, ObjectFactory objectFactory,
ServletContext servletContext, Container container,
#Inject("struts.convention.default.parent.package") String defaultParentPackageName,
#Inject("struts.convention.redirect.to.slash") String redirectToSlash,
#Inject("struts.convention.action.name.separator") String nameSeparator) {
super(configuration, objectFactory, servletContext, container, defaultParentPackageName,
redirectToSlash, nameSeparator);
log.info("Constructed TilesUnknownHandler");
}
#Override
public ActionConfig handleUnknownAction(String namespace, String actionName)
throws XWorkException {
ActionConfig actionConfig;
log.info("TilesUnknownHandler: before handleUnknownAction");
ActionConfig handleUnknownAction = super.handleUnknownAction(namespace, actionName);
log.info("TilesUnknownHandler: after handleUnknownAction, returning with:");
log.log(Level.INFO, "...ActionConfig value: {0}", (new JSONSerializer().serialize(handleUnknownAction)));
log.log(Level.INFO, "Modifying handleUnknowAction result handler");
Map<String, ResultConfig> results = handleUnknownAction.getResults();
ResultConfig resultConfig = results.get("success");
Builder builder = new ResultConfig.Builder("com.opensymphony.xwork2.config.entities.ResultConfig", "com.kenmcwilliams.tiles.result.TilesResult");
Map<String, String> params = resultConfig.getParams();
String tilesResultString = null;
String location = params.get("location");
if (location != null && !location.isEmpty()) {
int length = conventionBase.length();
if(StringUtils.startsWith(location, conventionBase)){
String subString = location.substring(length); //chop off "/WEB-INF/content"
int count = StringUtils.countMatches(subString, "/");//TODO: maybe check for "//", although I don't know why it would be in the string
if (count == 1){//empty namespace
tilesResultString = subString.replaceFirst("/", "#"); //TODO: because I am doing a straight replacement of the last element the else can probably be removed
}else{ //replace the last slash between the namespace and the file with "#"
int lastIndex = subString.lastIndexOf("/");
//subString.substring(lastIndex, lastIndex);
String nameSpace = subString.substring(0, lastIndex);
String file = subString.substring(lastIndex + 1);
tilesResultString = nameSpace + "#" + file;
}
}
}
Map<String, String> myParams = new LinkedHashMap<String, String>();
myParams.put("location", tilesResultString);
builder.addParams(myParams);
ResultConfig build = builder.build();
Map<String, ResultConfig> myMap = new LinkedHashMap<String, ResultConfig>();
myMap.put("success", build);
log.log(Level.INFO, "\n\n...results: {0}\n\n", (new JSONSerializer().serialize(results)));
actionConfig = new ActionConfig.Builder(handleUnknownAction).addResultConfigs(myMap).build();
//className("com.kenmcwilliams.tiles.result.TilesResult")
return actionConfig;
}
#Override
public Result handleUnknownResult(ActionContext actionContext, String actionName,
ActionConfig actionConfig, String resultCode) throws XWorkException {
log.info("TilesUnknownHandler: before handleUnknownResult");
Result handleUnknownResult = super.handleUnknownResult(actionContext, actionName, actionConfig, resultCode);
log.info("TilesUnknownHandler: after handleUnknownResult, returning with:");
log.log(Level.INFO, "...Result value: {0}", (new JSONSerializer().serialize(handleUnknownResult)));
return handleUnknownResult;
}
}
An example of how to use our "location" string which is in the form of: NameSpace + "#" + ActionName + ".jsp", note this definition <definition name="REGEXP:(.*)#(.*)" extends="default"> in the following:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN" "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">
<tiles-definitions>
<definition name="default" template="/WEB-INF/template/template.jsp">
<put-list-attribute name="cssList" cascade="true">
<add-attribute value="/style/cssreset-min.css" />
<add-attribute value="/style/cssfonts-min.css" />
<add-attribute value="/style/cssbase-min.css" />
<add-attribute value="/style/grids-min.css" />
<add-attribute value="/script/jquery-ui-1.8.24.custom/css/ui-lightness/jquery-ui-1.8.24.custom.css" />
<add-attribute value="/style/style.css" />
</put-list-attribute>
<put-list-attribute name="jsList" cascade="true">
<add-attribute value="/script/jquery/1.8.1/jquery.min.js" />
<add-attribute value="/script/jquery-ui-1.8.24.custom/js/jquery-ui-1.8.24.custom.min.js" />
<add-attribute value="/script/jquery.sort.js" />
<add-attribute value="/script/custom/jquery-serialize.js" />
</put-list-attribute>
<put-attribute name="title" value="defaults-name" cascade="true" type="string"/>
<put-attribute name="head" value="/WEB-INF/template/head.jsp"/>
<put-attribute name="header" value="/WEB-INF/template/header.jsp"/>
<put-attribute name="body" value="/WEB-INF/template/body.jsp"/>
<put-attribute name="footer" value="/WEB-INF/template/footer.jsp"/>
</definition>
<definition name="REGEXP:(.*)#(.*)" extends="default">
<put-attribute name="title" cascade="true" expression="OGNL:#com.opensymphony.xwork2.ActionContext#getContext().name"/>
<put-attribute name="body" value="/WEB-INF/content{1}/{2}"/>
</definition>
</tiles-definitions>
With this in place you can create JSP's under /WEB-INF/content/someplace/my-action.jsp
Just as you would with conventions AND tiles will decorate it appropriately as well if you create an action class called com.myapp.action.someplace.MyAction without any result type this code will execute and the /WEB-INF/content/someplace/my-action.jsp result would still be rendered.
There you have it conventions + tiles with no more annotations (well for the normal case).
NOTES:
This answer certainly isn't perfect but it does provide a working example of the strategy which can be applied to other view technologies (sitemesh, others).
Currently you can see the ".jsp" is being appended in the tiles result NOT in the tiles definitions this is inflexible. The specific extension should be specified within tiles, that is the body attribute within the definition should append the specific view type (.jsp, .fml, .vm) because you should know best at that time.
It is important to note that definitions are tried in the order they are given,so you can override the normal case REGEXP:(.*)#(.*) by placing definitions between the default and REGEXP:(.*)#(.*) definitions. For instance a definition called authenticated\(.*) can be placed between these two definitions. After all if you couldn't do this and all pages had to be tiled the same we really wouldn't be using tiles!
Just so you know when using tiles3 (the struts2 tiles3 plugin) you can use all three types of view technologies (jsp, freemarker, velocity) to compose one tile. It works. You are probably going to use one view technology consistently but it's nice to know it is possible.
I am new to this Adobe flex development.Right now I am working on the application and in my mxml form I have to place 2 fields.
one is Radio button(option (yes ,no) )and another one is text box(name), and the requirement is:
when the user selects yes then the name field should enable otherwise it should be disable.
The validation rules are:
if the user selects yes and he would not enter any value(textbox is empty) then we should display error message that "value is required"
if the user selects no and the text filed has some value then first he has to delete the contents in the textfiled then only select 'no'.
someone please help me and give me some code sample.That would be great help for me.
put this code in your MXMLapplication and run..
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="567" height="206" minWidth="955" minHeight="600" initialize="application1_initializeHandler(event)">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.events.FlexEvent;
protected function application1_initializeHandler(event:FlexEvent):void
{
}
protected function rd1_clickHandler(event:MouseEvent):void
{
if(!t1.enabled)
{
t1.enabled=true;
}
else if(t1.text=="" && t1.enabled)
{
Alert.show("Value is required in text box");
}
else
t1.enabled=true;
}
protected function rd2_clickHandler(event:MouseEvent):void
{
t1.text=null
t1.enabled =false;
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:TextInput x="132" y="41" id="t1"/>
<s:RadioButton x="162" y="91" label="Yes" id="rd1" groupName="select" click="rd1_clickHandler(event)"/>
<s:RadioButton x="211" y="91" label="No" id="rd2" groupName="select" click="rd2_clickHandler(event)" />
<s:Label x="79" y="45" text="Name"/>
</s:Application>
Create a custom mxml control based on TitleWindow and us PopupManager to show it on display.