I have created ContenPage with WebView inside xaml (code below). I'am passing webpageURL in constructor. My target is to add item to webview localstorage using javascirpt, before even starting loading passed URL. I tried several methods like
Browser.EvaluateJavaScriptAsync(javascirpt);
or
Browser.Evaluate(javascirpt);
before executing in Constructor
Browser.Source = URL;
or wrapping this methods inside
Device.BeginInvokeOnMainThread(async () =>{});
but still they didn't work as supposed. Do you know any solutions to my problem ?
public partial class WebView : ContentPage
{
public WebView(string URL)
{
string item = "test";
string javascirpt = String.Format("localStorage.setItem('ls.item', '{0}')", item);
Browser.Eval(javascirpt);
Browser.Source = URL;
}
}
Xaml:
<WebView x:Name="Browser" Grid.Column="0" Grid.Row="0" Grid.RowSpan="2"
HeightRequest="1000"
WidthRequest="1000"
Navigating="WebOnNavigating"
Navigated="WebOnEndNavigating" />
We don't recommend that put these code in the constructor .Because the class "WebView" hasn't initialized yet when you call these code.Try to move them to method OnAppearing.
protected override void OnAppearing()
{
base.OnAppearing();
string item = "test";
string javascirpt = String.Format("localStorage.setItem('ls.item', '{0}')", item);
Browser.Eval(javascirpt);
Browser.Source = URL;
}
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’ve gotten stuck on an “Object reference not set to an instance of an object” error message.
We’re trying to use a PagePart field that is attached to the Page type to dynamically link a CSS file in the HEAD of a layout file. See below code.
<!-- DYNAMIC CSS-->
var contentItem = Model.ContentItem;
var pagePart = (PagePart)contentItem.PagePart;
if (!String.IsNullOrWhiteSpace(pagePart.FestivalProgramName))
{
<link ref="#Url.Content("/Themes/MyTheme/Styles/festival-programs/" + pagePart.FestivalProgramName + ".css")" rel="stylesheet" type="text/css">
}
This is in a file called:
Layout.cshtml
Something is wrong about this (obviously) since pagePart is “null” when I Attach to Debugger and look. I get that the Layout file doesn’t know that it’s associated with a “Page” Content Type but this layout is only used with Pages. Anyway, this is very similar to code that works elsewhere in our Orchard site. Any help or advice is hugely appreciated!
Thanks, T
In the Layout, the Model is the Layout object. It has nothing to do whatsoever with whatever content is going to get rendered into the Content zone.
I think what you are trying to do should be done by overriding the Page template (Content-Page.Detail.cshtml). (Note the Detail part, you probably don't want to import every css when displaying multiple pages in summary)
In there you can do:
#{
var contentItem = Model.ContentItem; // The Page content item
var pagePart = contentItem.Page; // Note that casting to PagePart won't work, because it does not exist
if (!String.IsNullOrWhiteSpace(pagePart.FestivalProgramName.Value))
{
// "Orchard's" way to include styles
Style.Include("festival-programs/" + pagePart.FestivalProgramName.Value + ".css");
}
}
EDIT:
What you probably should do (I assume not every page is a festival page) is create a new Content Type: FestivalPage. Then attach the following parts to this content type (same as the Page content type):
Common
Publish later
Title
Autoroute
Body (Orchard 1.8.1 and lower)
Layout (Orchard 1.9.x)
Tags
Localization
Menu
And your field:
FestivalProgramName
Then create an alternate Content-FestivalPage.Detail.cshtml with the following content:
#using Orchard.Utility.Extensions;
#{
if (Model.Title != null) {
Layout.Title = Model.Title;
}
Model.Classes.Add("content-item");
var contentTypeClassName = ((string)Model.ContentItem.ContentType).HtmlClassify();
Model.Classes.Add(contentTypeClassName);
var tag = Tag(Model, "article");
var contentItem = Model.ContentItem; // The FestivalPage content item
var pagePart = contentItem.FestivalPage; // The FestivalPage part with the FestivalProgramName field
if (!String.IsNullOrWhiteSpace(pagePart.FestivalProgramName.Value))
{
// "Orchard's" way to include styles
Style.Include("festival-programs/" + pagePart.FestivalProgramName.Value + ".css");
}
}
// -- Default Orchard content --
#tag.StartElement
<header>
#Display(Model.Header)
#if (Model.Meta != null) {
<div class="metadata">
#Display(Model.Meta)
</div>
}
</header>
#Display(Model.Content)
#if(Model.Footer != null) {
<footer>
#Display(Model.Footer)
</footer>
}
#tag.EndElement
// ----
This way you won't get in the way with the normal pages of the application.
Thank you so much for responding. Your solution works but not in my case since I use the Layout Selector module and can't override at the Page.Detail level since that would imply one layout - or at least from my perspective it seems that way. I found another option though that does the trick.
We already take advantage of the Handlers class to insert META stuff into the HEAD of the page and thanks to your feedback and this thread Adding an element to page <head> in Orchard CMS, it dawned on me to use the same Handler to insert the CSS link.
PagePartHandler.cs.
using System;
using MyModuleName.Models;
using Orchard.ContentManagement.Handlers;
using Orchard.Core.Title.Models;
using Orchard.Data;
using Orchard.UI.Resources;
using Orchard.Utility.Extensions;
namespace MyModuleName.Handlers {
public class PagePartHandler : ContentHandler {
private readonly IResourceManager _resourceManager;
public PagePartHandler(
IRepository<PagePartRecord> repository,
IResourceManager resourceManager) {
_resourceManager = resourceManager;
Filters.Add(StorageFilter.For(repository));
OnGetDisplayShape<PagePart>(RegisterFestivalProgramStyle);
}
private void RegisterFestivalProgramStyle(BuildDisplayContext context, PagePart part) {
if (context.DisplayType != "Detail")
return;
if (String.IsNullOrWhiteSpace(part.FestivalProgramName))
return;
_resourceManager.RegisterLink(new LinkEntry
{
Rel = "stylesheet",
Type = "text/css",
Href = "/Themes/Bootstrap/Styles/festival-programs/" + part.FestivalProgramName + ".css"
});
}
}
}
This uses the tradition link style, not ResourceManifest.cs, but WORKS!
I'm wondering if it's possible to add a stylesheet or styling rules to the iframe on a RichTextArea field?
I need to make a couple of CSS tweaks to the default styling but I can't target the RichTextArea through my application stylesheet because it's loaded within an iframe.
The "problem" with the Vaadin RichTextArea component is not only in the fact that the editor field is inside an iframe element, but as with all the other Vaadin components, you also have to keep in mind that your components will not be available when the DOM ready callback (i.e. for example $(document).ready(function() {}) if using jQuery or the callback bound to a DOMContentLoaded event) will execute.
This is because, as you know, when the Vaadin application starts, you actually don't have your components inside the DOM yet, but a vaadin bootstrap process will request and take care of the rendering of your UI for you. This is actually the principle with whom GWT works also (see How does GWT provide the correct Javascript code to every browser e.g. to carry out i18n and browser compatibility?) (after all Vaadin is based on GWT).
So e.g. if you use jQuery and you have a script like this loaded at the very beginning right after the vaadinBootstrap.js script loads and executes:
$(function() {
// this code will execute, but no components are available yet.
var rTa = $(".v-richtextarea"); // this won't select your Rich text area
var len = rTa.length // len will be 0 here, as no element matches the previous selector because as stated before, there is not an element with such a class in the DOM yet.
});
After this code executes, the very "heavy" process of creating the UI components and your layout begins, your widgetsets and/or the default one get loaded, and after that you have your beautiful UI set up and ready to interact with the user.
In order to customise an existent component such a RichTextArea and e.g. add a style element to the body of its iframe element, you can certainly venture into the depths of GWT and use JSNI as you did in your answer, but there's also another way to do it, in my opinion, more compact, simple, and does not require the usage of JSNI.
All you need to do is to implement a JavaScriptExtension with a connector on the client side for your component (you can just extend Vaadin's RichTextArea), check out this simple code example:
#!java
package com.package.example;
#JavaScript({"vaadin://js/src/rich_text_area_connector.js"})
public class RichTextAreaExtension extends AbstractJavaScriptExtension {
#Override
public void extend(AbstractClientConnector connector) {
super.extend(connector);
}
}
This is the extension, then you would need to create the client side connector, which is basically a JavaScript file with a function which name is based on the package name of the extension and, of course, the extension's class name:
#!javascript
com_package_example_RichTextAreaExtension = function() {
var connectorParentId = this.getParentId();
var element = this.getElement(parentId); // this is the rich text area element, which at this point is
// If you are using jQuery, then you can just select your element like so:
var jQueryElement = $(element);
// and do whatever you would normally do with the element like
// when you are inside $(document).ready(function() {});
// or you can add a style element to the head element inside the iframe, doing something like the following:
$(element).find("iframe").contents().find('head')
.append('<link rel="stylesheet" href="./VAADIN/themes/your_theme/style_for_richtextarea_body.css" type="text/css" />');
}
And you are done. Another benefit, as you can see is that you don't have to write different code for different browsers (Mozilla, Chrome, IE), you can just use jQuery and the library will handle the compatibility for you. The last part is the extended component itself. As I said before, you can just extend Vaadin's RichTextArea:
public class RichTextAreaWithStyleOnBody extends RichTextArea {
public RichTextAreaWithStyleOnBody(String caption, Property<?> dataSource) {
super(caption);
if (dataSource != null)
setPropertyDataSource(dataSource);
new RichTextAreaExtension().extend(this);
}
public RichTextAreaWithStyleOnBody(String caption, Property<?> dataSource) {
this(caption, dataSource);
}
public RichTextAreaWithStyleOnBody(String caption) {
this(caption, null);
}
public RichTextAreaWithStyleOnBody(Property<?> dataSource) {
this(null, dataSource);
}
public RichTextAreaWithStyleOnBody() {
this(null, null);
}
}
Note the usage of the JavaScript extension inside the main constructor. And finally you can use it in your layout just as you would with any other component:
// Inside your UI's class
#Override
protected void init(VaadinRequest request) {
VerticalLayout layout = new VerticalLayout();
layout.setMargin(true);
layout.setSpacing(true);
setContent(layout);
RichTextArea rTa = new RichTextAreaWithStyleOnBody("A rich text area with a styled body");
rTa.setStyleName("myRichTextArea"); // you can do whatever you'll like on the server side just because your rich text area extends a Vaadin server side component.
rTa.setSizeFull();
layout.addComponent(rTa);
}
As far as I know it is not possible. I had the same problem and wrote a little add-on based on a copy of the vaadin code from the RichTextArea. I added some additional methods to set font-family and font-size. Unfortunately I didn't have enough time recently to clean up my code publish a new version of the add-on.
The main functionality of the add-on is to decouple the toolbar from the area.
You can find the code in the v7 branch here: https://gitorious.org/richtexttoolbar-vaadin-addon/richtexttoolbar-vaadin-addon
After continued searching I found this discussion, which led me to a solution:
https://groups.google.com/forum/#!msg/Google-Web-Toolkit/9kwAJNhnamY/1MVfFFRq8tUJ
I ended up wrapping the RichTextImpl* classes, cloning the initElement() method from the parent class, and inserting these lines...
...for Mozilla/Safari:
_this.#com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document.designMode = 'On';
var doc = _this.#com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document;
head=doc.getElementsByTagName('head')[0];
link=document.createElement('link');
link.setAttribute('rel',"stylesheet");
link.setAttribute('href',"/path/to/richtext.css" );
link.setAttribute('type',"text/css");
head.appendChild(link);
...for IE:
var ct = "<html><head><style>#import url('/path/to/richtext.css');</style></head><body CONTENTEDITABLE='true'></body></html>" ;
doc.write( ct );
... to get a style sheet loading in my RichTextArea fields.
Hy guys, i don't know if ist still important but i did something different.
I had a token function made for my richtextarea, and for the token that a was saving in the db just save it with a style class that i already have in my "styles.css" declared.
Step by step is something like this, i am taking the stylesheet and looking for my token classes, and then i am adding the styles in my iframes header, so that my token class in the iframe is styled.
function styleRichtextareaIframe(id, themeClass, tokenClass, tokenSelectedClass) {
setTimeout(function() {
// iframe by id selected
var $iframe = $("#" + id).find(".gwt-RichTextArea");
var $head = $iframe.contents().find("head");
var $body = $iframe.contents().find("body");
var classNameToken = "." + themeClass + " " + "." + tokenClass;
var classNameToken_selected = "." + themeClass + " " + "." + tokenSelectedClass;
var fontClass = "." + themeClass + ".v-app, " + "." + themeClass + " " + ".v-window";
var styleSheets = window.document.styleSheets;
var styleSheetsLength = styleSheets.length;
var style = document.createElement('style');
style.type = 'text/css';
for (var i = 0; i < styleSheetsLength; i++) {
var classes = styleSheets[i].rules || styleSheets[i].cssRules;
for (var x = 0; x < classes.length; x++) {
if (classes[x].selectorText == classNameToken) {
style.appendChild(document.createTextNode(getClassFor(classes[x])));
}
if (classes[x].selectorText == classNameToken_selected) {
style.appendChild(document.createTextNode(getClassFor(classes[x])));
}
if (classes[x].selectorText == fontClass) {
style.appendChild(document.createTextNode(getClassFor(classes[x])));
}
}
}
function getClassFor(classObj) {
if (classObj.cssText) {
return classObj.cssText;
} else {
return classObj.style.cssText;
}
}
//Adding the classes also to the body
//of dourse you can change the output string to just give you the style class that you need.
$body.addClass("v-app " + themeClass);
$head.append(style);
}, 200);
}
I had to inset the timeout function because of the "problem" with the Vaadin RichTextArea component is not only in the fact that the editor field is inside an iframe element, but as with all the other Vaadin components, you also have to keep in mind that your components will not be available when the DOM is ready, (THE COMPONENT IS NOT IN THE DOM).
Hope this help someone, and sorry for my bad english.
Cheers.
Simplest way I came up with was extending the RichTextArea component and setting a custom style with JavaScript:
public class MyRichTextArea extends RichTextArea {
public MyRichTextArea(String className) {
setStyleName(className);
String js = "var iframeContainer = document.getElementsByClassName('" + className + "')[0];" +
"var iframe = iframeContainer.getElementsByTagName('iframe')[0];" +
"var iframeBody = iframe.contentDocument.getElementsByTagName('body')[0];" +
"iframeBody.style.fontFamily='Arial';";
JavaScript.getCurrent().execute(js);
}
}
Usage:
MyRichTextArea field = new MyRichTextArea("fieldClassName");
Please note that iframe.contentDocument should be supported with all major browsers, but for better support additional tweaks should be added - see Getting the document object of an iframe
I'm developing a plugin to display additional information related to a project.
So I'm developing a Project Tab Panel module but my page does not display the paramenters I put in the velocity context.
Here is the a part of plugin xml:
<project-tabpanel key="stats-tab-panel" name="Stats Tab Panel" i18n-name-key="stats-tab-panel.name" class="it.pride.jira.plugins.StatsTabPanel">
<description key="stats-tab-panel.description">The Stats Tab Panel Plugin</description>
<label key="stats-tab-panel.label"></label>
<order>10</order>
<resource type="velocity" name="view" location="templates/tabpanels/stats-tab-panel.vm"/>
Here instead the useful part of my class:
public class StatsTabPanel extends GenericProjectTabPanel {
public StatsTabPanel(JiraAuthenticationContext jiraAuthenticationContext,
FieldVisibilityManager fieldVisibilityManager) {
super(jiraAuthenticationContext, fieldVisibilityManager);
// TODO Auto-generated constructor stub
}
public String testvalue="112002";
#Override
public boolean showPanel(BrowseContext context){
return true;
}
#Override
public Map<String, Object> createVelocityParams (BrowseContext context) {
Map<String, Object> contextMap = createVelocityParams(context);
contextMap.put("testvalue", testvalue);
return contextMap;
}
}
So, as in this case, when i write `$testvalue in my template the number doesn't show up.
What am I doing wrong?
The problem is that the getHtml method that is used to return the HTML for the tab has a Map that is the Velocity context but only contains the params in the BrowseContext object. The way to fix this is to override createVelocityParams in your class, e.g.
protected Map createVelocityParams(final BrowseContext ctx) {
Map params = super.createVelocityParams(ctx);
params.put("myparam", "some value for it");
return params;
}
The problem is that method is protected so I ended up also overriding getHtml which calls createVelocityParams in a parent class.
Some Project tabs extend GenericProjectTabPanel but some such as SummaryProjectTabPanel.java seem to use UI Fragments. I suspect that Fragments is what things are moving towards.