Element in the VisualElementRenderer is not my generic type - ios

I followed the example - https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/custom-renderer/hybridwebview/ to create a hybrid web view. It works fine in Andriod project but I have issues with the iOS project. In the OnElementChanged override, the Element property is always Xamarin.Forms.Element type which is the base type and not my generic view type (TElement). I have rechecked my code multiple times but cannot find any fixes. If I download the original code and run it, it works fine. The issue is only in my project.
Can anybody give me pointers as to why I might not be getting the generic TElement type. In the original project , the TElement type is HybridWebView. In mine I have a different name but with almost all the same properties and methods.
Below are some of my code. It is really easy to replicate the issue. Just create a new Xamarin.Forms solution and add my code. You will see the issue on this lie - var ele = Element; //ELEMENT HERE IS NOt SBMHybridWebView
The custom control in PCL
public class SBMHybridWebView : Xamarin.Forms.View
{
}
The iOS Renderer (off-course I have added the ExportRenderer)
public class SBMHybridWebViewRenderer : ViewRenderer<SBMHybridWebView, WKWebView>, IWKScriptMessageHandler
{
const string JavaScriptFunction = "function invokeCSharpAction(data){window.webkit.messageHandlers.invokeAction.postMessage(data);}";
WKUserContentController userController;
protected override void OnElementChanged(ElementChangedEventArgs<SBMHybridWebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
userController = new WKUserContentController();
var script = new WKUserScript(new NSString(JavaScriptFunction), WKUserScriptInjectionTime.AtDocumentEnd, false);
userController.AddUserScript(script);
userController.AddScriptMessageHandler(this, "invokeAction");
var config = new WKWebViewConfiguration { UserContentController = userController };
var webView = new WKWebView(Frame, config);
SetNativeControl(webView);
}
if (e.OldElement != null)
{
//Clean up code
}
if (e.NewElement != null)
{
var ele = Element; //ELEMENT HERE IS NOt SBMHybridWebView
}
}
public void DidReceiveScriptMessage(WKUserContentController userContentController, WKScriptMessage message)
{
//Element.InvokeAction(message.Body.ToString());
}
}
The XAML
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:localcontrol="clr-namespace:App2.Control;assembly=App2"
x:Class="App2.View.Page1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="60" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<localcontrol:SBMHybridWebView Grid.Row="0" />
</Grid>
</ContentPage>

I found the cause which I cannot explain. In my iOS project, I had my SBMHybridWebViewRenderer in Renderer folder. Somehowm iOS does not like the renderer to be in a folder. Moving it to the root of the project (but keeping the same namespace) solved the issue. WHY? I dont know. I would have expected it to work regardless of where I place the file.
I thought I will post this just incase someone else has the same issue.

Related

LibVLCSharp Playlist implementation

I'm trying to use LibVLCSharp with a playlist ( or that's the plan) and am testing it out with a basic loop between two different videos. I'm using this in WPF. The UI has a button to start the first video playing. If I don't loop and click the button each time, the next video will play as expected. Let it loop and threading error occurs on second video. I have checked out some of the other posts here on SO - How to achieve looping playback with Libvlcsharp and the various links within there, but I'm missing something. I'm hoping someone has a suggestion - or two! Thanks.
XAML
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:wpf="clr-namespace:LibVLCSharp.WPF;assembly=LibVLCSharp.WPF"
xmlns:local="clr-namespace:testVLC"
xmlns:Properties="clr-namespace:testVLC.Properties" x:Class="testVLC.MainWindow"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".2*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width=".2*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height=".25*"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height=".25*"/>
<RowDefinition Height=".25*"/>
</Grid.RowDefinitions>
<wpf:VideoView x:Name="VLC_player" Grid.Column="1" Grid.ColumnSpan="4" Grid.Row="0" Grid.RowSpan="3" Background="Black" Loaded="VLC_player_Loaded"/>
<Button x:Name="go_Btn" Content="Press to view video" Grid.Column="2" HorizontalAlignment="Right" Grid.Row="3" Padding="4,1" Margin="0,0,1,0" Click="Button_click"/>
<TextBox x:Name="countTBox" Grid.Column="2" HorizontalAlignment="Left" Grid.Row="3" Width="30" VerticalContentAlignment="Stretch"/>
</Grid>
</Window>
CS
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using LibVLCSharp.Shared;
using MediaPlayer = LibVLCSharp.Shared.MediaPlayer;
namespace testVLC
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
LibVLC _libVLC;
MediaPlayer media_player;
bool init_flag = false;
bool Web_flag = false;
int count = 0;
public MainWindow()
{
InitializeComponent();
}
private async void Media_play (string URI)
{
if (init_flag)
{
if(Web_flag)
{
_libVLC = new LibVLC("--verbose=2");
Media media = new Media(_libVLC, (URI), FromType.FromLocation);
await media.Parse(MediaParseOptions.ParseNetwork);
media_player = new MediaPlayer(media.SubItems.First());
VLC_player.MediaPlayer = media_player;
media_player.EndReached += Video_Ended;
media_player.Play();
//MessageBox.Show(Thread.CurrentThread.Name);
}
else
{
_libVLC = new LibVLC();
media_player = new MediaPlayer(_libVLC);
VLC_player.MediaPlayer = media_player;
media_player.EndReached += Video_Ended;
media_player.Play(new Media(_libVLC, URI));
}
}
}
private void Button_click(object sender, RoutedEventArgs e)
{
count++;
this.Dispatcher.Invoke((Action)(() =>
{
countTBox.Text = count.ToString();
}));
Main_List();
}
private async void Video_Ended(object sender, EventArgs e)
{
//ThreadPool.QueueUserWorkItem(_ => media_player.Stop());//doesn't work
await Task.Run(() => media_player.Stop());
Web_flag = false;
//Button_click(this, null);//Works fine when commented out and the "Press to view video" button is clicked each time. Not so much when looping.
}
private void Main_List()
{
if (count%2 == 0)//even
{
Media_play("C:\\Users\\echo_\\Downloads\\videoplayback (1).mp4");
}
else//odd
{
Web_flag = true;
Media_play("https://youtu.be/UK4t59mhIhs");
}
}
private void VLC_player_Loaded(object sender, RoutedEventArgs e)
{
init_flag = true;
Core.Initialize();
}
}
}
Don't try to call Stop at the VideoEnded event, I think there is a known issue in this area in libvlc 3 that hangs the program.
Call .Play with the new media instead.
Also, I wouldn't recommend to use two different libvlc instances if you can avoid it. 1 LibVlc, and 1 Media Player, but multiple Play() on the same MediaPlayer would be the norm. You can pass different options as media options if you need to have different options (though some options are not available at the media level, like the verbosity option (in which case, I'd register a log callback and filter there))

Key press handling in JSF component, especially <p:tree>

Goal: I want to enrich a predefined component with my own behavior. This is typically the case with list, tables and trees, implementing my actions like "delete", "add before", "add after", "move up",... (with text field this seems to be simple...)
I thought there must be a way to attach key listeners at the component itself (assumed that there's something like a "focus"), e.g. if i have two trees on a page pressing "Ctrl+" will add one time an A to treeA via listenerA and the other a B to treeB via listenerB.
Adding an ajax listener at a tree node or the tree itself does not work. So it seems to be necessary (see two answers below) to catch key globally and "dispatch" them myself properly. At least with one tree this should work without hassle.
According to the answers below this can only be done using JavaScript or using a non standard JSF tag.
As i am concerned with JSF question at most 2 times a year, i think someone more involved can give insight in best practice on this twilight zone between JSF and JavaScript.
In this snippet i want to create a new child item when "+" is pressed.
<h:form>
<p:tree id="document" value="#{demo.root}" var="node"
selectionMode="single" selection="#{demo.selection}">
<p:treeNode>
<h:outputText value="#{node.label}" />
</p:treeNode>
</p:tree>
</h:form>
The tag
<f:ajax event="keypress" listener="#{demo.doTest}" />
is not accepted in "treeNode" and "tree" and has no function in "form".
= EDIT
As can be seen in the answers, this concrete scenario is supported by simply using <p:hotkey>. This solution has 2 drawbacks, its Primefaces bound and it fails if we add input components like this
<h:form>
<p:tree id="document" value="#{demo.root}" var="node"
selectionMode="single" selection="#{demo.selection}">
<p:treeNode>
<p:inputText value="#{node.label}" />
</p:treeNode>
</p:tree>
</h:form>
What is the best practice to implement such things? At least, is it possible in plain JSF at all? If i only use plain JSF, what would be the least ugly idiom.
= EDIT
I want to point to a short history of findings, given as an answer below, to give more detail on the problem behind this question
This implementation enables navigation and add/remove as well.
IMHO it has the best functionality/effort ratio.
I don't know what you mean with standard JSF tag or plain JSF, but in this example there isn't a single line of JavaScript.
Note that p:hotkey component behavior is global. Non-input components like p:tree cannot have owned key listeners since they can't be "focused" (or at least by default behavior), just like you pointed.
However, here it is:
<h:form>
<p:hotkey bind="left" actionListener="#{testBean.onLeft}" process="#form" update="target" />
<p:hotkey bind="right" actionListener="#{testBean.onRight}" process="#form" update="target" />
<p:hotkey bind="up" actionListener="#{testBean.onUp}" process="#form" update="target" />
<p:hotkey bind="down" actionListener="#{testBean.onDown}" process="#form" update="target" />
<p:hotkey bind="ctrl+a" actionListener="#{testBean.onAdd}" process="#form" update="target" />
<p:hotkey bind="ctrl+d" actionListener="#{testBean.onDelete}" process="#form" update="target" />
<h:panelGroup id="target">
<p:tree value="#{testBean.root}" var="data" selectionMode="single"
selection="#{testBean.selection}" dynamic="true">
<p:treeNode expandedIcon="ui-icon-folder-open" collapsedIcon="ui-icon-folder-collapsed">
<h:outputText value="#{data}" />
</p:treeNode>
</p:tree>
<br />
<h3>current selection: #{testBean.selection.data}</h3>
</h:panelGroup>
</h:form>
and this is the managed bean:
#ManagedBean
#ViewScoped
public class TestBean implements Serializable
{
private static final long serialVersionUID = 1L;
private DefaultTreeNode root;
private TreeNode selection;
#PostConstruct
public void init()
{
root = new DefaultTreeNode("node");
root.setSelectable(false);
DefaultTreeNode node_0 = new DefaultTreeNode("node_0");
DefaultTreeNode node_1 = new DefaultTreeNode("node_1");
DefaultTreeNode node_0_0 = new DefaultTreeNode("node_0_0");
DefaultTreeNode node_0_1 = new DefaultTreeNode("node_0_1");
DefaultTreeNode node_1_0 = new DefaultTreeNode("node_1_0");
DefaultTreeNode node_1_1 = new DefaultTreeNode("node_1_1");
node_0.setParent(root);
root.getChildren().add(node_0);
node_1.setParent(root);
root.getChildren().add(node_1);
node_0_0.setParent(node_0);
node_0.getChildren().add(node_0_0);
node_0_1.setParent(node_0);
node_0.getChildren().add(node_0_1);
node_1_0.setParent(node_1);
node_1.getChildren().add(node_1_0);
node_1_1.setParent(node_1);
node_1.getChildren().add(node_1_1);
selection = node_0;
node_0.setSelected(true);
}
private void initSelection()
{
List<TreeNode> children = root.getChildren();
if(!children.isEmpty())
{
selection = children.get(0);
selection.setSelected(true);
}
}
public void onLeft()
{
if(selection == null)
{
initSelection();
return;
}
if(selection.isExpanded())
{
selection.setExpanded(false);
return;
}
TreeNode parent = selection.getParent();
if(parent != null && !parent.equals(root))
{
selection.setSelected(false);
selection = parent;
selection.setSelected(true);
}
}
public void onRight()
{
if(selection == null)
{
initSelection();
return;
}
if(selection.isLeaf())
{
return;
}
if(!selection.isExpanded())
{
selection.setExpanded(true);
return;
}
List<TreeNode> children = selection.getChildren();
if(!children.isEmpty())
{
selection.setSelected(false);
selection = children.get(0);
selection.setSelected(true);
}
}
public void onUp()
{
if(selection == null)
{
initSelection();
return;
}
TreeNode prev = findPrev(selection);
if(prev != null)
{
selection.setSelected(false);
selection = prev;
selection.setSelected(true);
}
}
public void onDown()
{
if(selection == null)
{
initSelection();
return;
}
if(selection.isExpanded())
{
List<TreeNode> children = selection.getChildren();
if(!children.isEmpty())
{
selection.setSelected(false);
selection = children.get(0);
selection.setSelected(true);
return;
}
}
TreeNode next = findNext(selection);
if(next != null)
{
selection.setSelected(false);
selection = next;
selection.setSelected(true);
}
}
public void onAdd()
{
if(selection == null)
{
selection = root;
}
TreeNode node = createNode();
node.setParent(selection);
selection.getChildren().add(node);
selection.setExpanded(true);
selection.setSelected(false);
selection = node;
selection.setSelected(true);
}
public void onDelete()
{
if(selection == null)
{
return;
}
TreeNode parent = selection.getParent();
parent.getChildren().remove(selection);
if(!parent.equals(root))
{
selection = parent;
selection.setSelected(true);
if(selection.isLeaf())
{
selection.setExpanded(false);
}
}
else
{
selection = null;
}
}
// create the new node the way you like, this is an example
private TreeNode createNode()
{
int prog = 0;
TreeNode lastNode = Iterables.getLast(selection.getChildren(), null);
if(lastNode != null)
{
prog = NumberUtils.toInt(StringUtils.substringAfterLast(String.valueOf(lastNode.getData()), "_"), -1) + 1;
}
return new DefaultTreeNode(selection.getData() + "_" + prog);
}
private TreeNode findNext(TreeNode node)
{
TreeNode parent = node.getParent();
if(parent == null)
{
return null;
}
List<TreeNode> brothers = parent.getChildren();
int index = brothers.indexOf(node);
if(index < brothers.size() - 1)
{
return brothers.get(index + 1);
}
return findNext(parent);
}
private TreeNode findPrev(TreeNode node)
{
TreeNode parent = node.getParent();
if(parent == null)
{
return null;
}
List<TreeNode> brothers = parent.getChildren();
int index = brothers.indexOf(node);
if(index > 0)
{
return findLastUnexpanded(brothers.get(index - 1));
}
if(!parent.equals(root))
{
return parent;
}
return null;
}
private TreeNode findLastUnexpanded(TreeNode node)
{
if(!node.isExpanded())
{
return node;
}
List<TreeNode> children = node.getChildren();
if(children.isEmpty())
{
return node;
}
return findLastUnexpanded(Iterables.getLast(children));
}
public TreeNode getRoot()
{
return root;
}
public TreeNode getSelection()
{
return selection;
}
public void setSelection(TreeNode selection)
{
this.selection = selection;
}
}
UPDATE
Maybe I found an interesting solution to attach key bindings to single DOM elements:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:cc="http://xmlns.jcp.org/jsf/composite" xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:fn="http://xmlns.jcp.org/jsp/jstl/functions" xmlns:p="http://primefaces.org/ui"
xmlns:o="http://omnifaces.org/ui" xmlns:of="http://omnifaces.org/functions"
xmlns:s="http://shapeitalia.com/jsf2" xmlns:sc="http://xmlns.jcp.org/jsf/composite/shape"
xmlns:e="http://java.sun.com/jsf/composite/cc" xmlns:pt="http://xmlns.jcp.org/jsf/passthrough">
<h:head>
<title>test hotkey</title>
</h:head>
<h:body>
<h:form>
<h:panelGroup id="container1">
<s:hotkey bind="left" actionListener="#{testBean.onLeft}" update="container1" />
<s:hotkey bind="right" actionListener="#{testBean.onRight}" update="container1" />
<s:hotkey bind="up" actionListener="#{testBean.onUp}" update="container1" />
<s:hotkey bind="down" actionListener="#{testBean.onDown}" update="container1" />
<s:hotkey bind="ctrl+a" actionListener="#{testBean.onAdd}" update="container1" />
<s:hotkey bind="ctrl+d" actionListener="#{testBean.onDelete}" update="container1" />
<p:tree value="#{testBean.root}" var="data" selectionMode="single"
selection="#{testBean.selection}" dynamic="true" pt:tabindex="1">
<p:treeNode expandedIcon="ui-icon-folder-open"
collapsedIcon="ui-icon-folder-collapsed">
<h:outputText value="#{data}" />
</p:treeNode>
</p:tree>
<br />
<h3>current selection: #{testBean.selection.data}</h3>
</h:panelGroup>
</h:form>
</h:body>
</html>
three important things:
h:panelGroup attribute id is required, otherwise it is not rendered as DOM element. style, styleClass, and other render-enable attributes can be used with or instead.
Note that pt:tabindex=1 on p:tree: it is required to enable "focus". pt is the namespace used for "passthrough" attributes and only works in JSF 2.2.
I had to customize HotkeyRenderer in order to attach the DOM event listener to a specific DOM element instead of the entire document: now it's s:hotkey instead of p:hotkey. My implementation attachs it to DOM element associated to parent component, continue read for implementation.
the modified renderer:
#FacesRenderer(componentFamily = Hotkey.COMPONENT_FAMILY, rendererType = "it.shape.HotkeyRenderer")
public class HotkeyRenderer extends org.primefaces.component.hotkey.HotkeyRenderer
{
#SuppressWarnings("resource")
#Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException
{
ResponseWriter writer = context.getResponseWriter();
Hotkey hotkey = (Hotkey) component;
String clientId = hotkey.getClientId(context);
String targetClientId = hotkey.getParent().getClientId();
writer.startElement("script", null);
writer.writeAttribute("type", "text/javascript", null);
writer.write("$(function() {");
writer.write("$(PrimeFaces.escapeClientId('" + targetClientId + "')).bind('keydown', '" + hotkey.getBind() + "', function(){");
if(hotkey.isAjaxified())
{
UIComponent form = ComponentUtils.findParentForm(context, hotkey);
if(form == null)
{
throw new FacesException("Hotkey '" + clientId + "' needs to be enclosed in a form when ajax mode is enabled");
}
AjaxRequestBuilder builder = RequestContext.getCurrentInstance().getAjaxRequestBuilder();
String request = builder.init()
.source(clientId)
.form(form.getClientId(context))
.process(component, hotkey.getProcess())
.update(component, hotkey.getUpdate())
.async(hotkey.isAsync())
.global(hotkey.isGlobal())
.delay(hotkey.getDelay())
.timeout(hotkey.getTimeout())
.partialSubmit(hotkey.isPartialSubmit(), hotkey.isPartialSubmitSet())
.resetValues(hotkey.isResetValues(), hotkey.isResetValuesSet())
.ignoreAutoUpdate(hotkey.isIgnoreAutoUpdate())
.onstart(hotkey.getOnstart())
.onerror(hotkey.getOnerror())
.onsuccess(hotkey.getOnsuccess())
.oncomplete(hotkey.getOncomplete())
.params(hotkey)
.build();
writer.write(request);
}
else
{
writer.write(hotkey.getHandler());
}
writer.write(";return false;});});");
writer.endElement("script");
}
}
and finally this is the taglib definition for the new s:hotkey (it's a copy/paste of the original with the only difference of <renderer-type>it.shape.HotkeyRenderer</renderer-type>):
<?xml version="1.0" encoding="UTF-8"?>
<facelet-taglib version="2.2" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facelettaglibrary_2_2.xsd">
<namespace>http://shapeitalia.com/jsf2</namespace>
<tag>
<description><![CDATA[HotKey is a generic key binding component that can bind any formation of keys to javascript event handlers or ajax calls.]]></description>
<tag-name>hotkey</tag-name>
<component>
<component-type>org.primefaces.component.Hotkey</component-type>
<renderer-type>it.shape.HotkeyRenderer</renderer-type>
</component>
<attribute>
<description><![CDATA[Unique identifier of the component in a namingContainer.]]></description>
<name>id</name>
<required>false</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<description><![CDATA[Boolean value to specify the rendering of the component, when set to false component will not be rendered.]]></description>
<name>rendered</name>
<required>false</required>
<type>java.lang.Boolean</type>
</attribute>
<attribute>
<description><![CDATA[An el expression referring to a server side UIComponent instance in a backing bean.]]></description>
<name>binding</name>
<required>false</required>
<type>javax.faces.component.UIComponent</type>
</attribute>
<attribute>
<description><![CDATA[An actionlistener that'd be processed in the partial request caused by uiajax.]]></description>
<name>actionListener</name>
<required>false</required>
<type>javax.faces.event.ActionListener</type>
</attribute>
<attribute>
<description><![CDATA[A method expression that'd be processed in the partial request caused by uiajax.]]></description>
<name>action</name>
<required>false</required>
<type>javax.el.MethodExpression</type>
</attribute>
<attribute>
<description><![CDATA[Boolean value that determines the phaseId, when true actions are processed at apply_request_values, when false at invoke_application phase.]]></description>
<name>immediate</name>
<required>false</required>
<type>java.lang.Boolean</type>
</attribute>
<attribute>
<description><![CDATA[The Key binding. Required.]]></description>
<name>bind</name>
<required>true</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<description><![CDATA[Client side id of the component(s) to be updated after async partial submit request.]]></description>
<name>update</name>
<required>false</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<description><![CDATA[Component id(s) to process partially instead of whole view.]]></description>
<name>process</name>
<required>false</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<description><![CDATA[Javascript event handler to be executed when the key binding is pressed.]]></description>
<name>handler</name>
<required>false</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<description><![CDATA[Javascript handler to execute before ajax request is begins.]]></description>
<name>onstart</name>
<required>false</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<description><![CDATA[Javascript handler to execute when ajax request is completed.]]></description>
<name>oncomplete</name>
<required>false</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<description><![CDATA[Javascript handler to execute when ajax request fails.]]></description>
<name>onerror</name>
<required>false</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<description><![CDATA[Javascript handler to execute when ajax request succeeds.]]></description>
<name>onsuccess</name>
<required>false</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<description><![CDATA[Global ajax requests are listened by ajaxStatus component, setting global to false will not trigger ajaxStatus. Default is true.]]></description>
<name>global</name>
<required>false</required>
<type>java.lang.Boolean</type>
</attribute>
<attribute>
<description><![CDATA[If less than delay milliseconds elapses between calls to request() only the most recent one is sent and all other requests are discarded. The default value of this option is null. If the value of delay is the literal string 'none' without the quotes or the default, no delay is used.]]></description>
<name>delay</name>
<required>false</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<description><![CDATA[Defines the timeout for the ajax request.]]></description>
<name>timeout</name>
<required>false</required>
<type>java.lang.Integer</type>
</attribute>
<attribute>
<description><![CDATA[When set to true, ajax requests are not queued. Default is false.]]></description>
<name>async</name>
<required>false</required>
<type>java.lang.Boolean</type>
</attribute>
<attribute>
<description><![CDATA[When enabled, only values related to partially processed components would be serialized for ajax
instead of whole form.]]></description>
<name>partialSubmit</name>
<required>false</required>
<type>java.lang.Boolean</type>
</attribute>
<attribute>
<description><![CDATA[If true, indicate that this particular Ajax transaction is a value reset transaction. This will cause resetValue() to be called on any EditableValueHolder instances encountered as a result of this ajax transaction. If not specified, or the value is false, no such indication is made.]]></description>
<name>resetValues</name>
<required>false</required>
<type>java.lang.Boolean</type>
</attribute>
<attribute>
<description><![CDATA[If true, components which autoUpdate="true" will not be updated for this request. If not specified, or the value is false, no such indication is made.]]></description>
<name>ignoreAutoUpdate</name>
<required>false</required>
<type>java.lang.Boolean</type>
</attribute>
</tag>
</facelet-taglib>
whew, it was hard ;)
Until now, there's no really satisfying answer so far. I summarize my findings:
Some JSF components have "inner logic" that bind some keys to component specific features. Too bad that "intelligent components" like <p:tree /> don't even bind arrow key navigation.
So you try to emulate and find <p:hotkey/>. No you can (as shown in the very extensive answer by #michele-mariotti) feel a little comfortable wih your component.
Then you add input features to the tree... And hotkeys are breaking down. You do not know for what reasons (and, really, i think you should not have to...).
So you start digging around and suddenly find yourself in JavaScript and DOM wonderland.
The "hotkey" library for the ubiquitous jQuery seems to bring help. Or one of the 1000 others you bring up when searching for this stuff. Better take the right one from the start (which one is it?).
So you start adding ugly jQuery expressions for each and every accelerator, first on the document, then down on every input component (as shown e.g. here). Your page starts beeing a mess.
But you're happy - at least after two days you have brought up a simple tree..
Now you add sugar. You add <p:inplace /> or simply add new tree nodes. Your hotkeys break down.
Oh, yes, you should have known: The dynamic inputs are not bound to hotkeys. Add some more JavaScript hacks to the page...
But hey, what's this: Testing all your hotkey stuff, you forgot to enter values in the tree input fields. Now you realize: it's not working!! Again some searching: Seems to be a well known bug/missing feature for years. Primefaces removes focus immediately after activating the tree input. Well, who on earth makes input in a tree...
So, here's where you could debug some some sophisticated Primefaces JavaScript or add some other equally sophisticated JavaScript to force the focus back to this field. You could realize you use the wrong component library and restart with Richfaces tree, Omnifaces tree or whatever. You could resign to use web technology, sleep another 2 years and come back to see if basic technology has evolved to be usable. Is Java web simply a playground for tinkerers?
After this piece of rant, is there anybody that can help with some advice?
I found a workaround that does not exactly what was asked, but can handle my scenario.
Adding a "hotkey" component to the form calls the server as requested:
<p:hotkey bind="ctrl+shift+a" update="messages" actionListener="#{demo.doTest}"/>
Similiar component exists in RichFaces, don't know about plain JSF.
What i can't believe is that there's no other way then reverting down to JavaScript (like http://winechess.blogspot.ru/2014/02/datatable-keyboard-navigation.html or http://www.openjs.com/scripts/events/keyboard_shortcuts/) to write usable JSF apps?
And that standard components like tree or table do not have standard keyboard navigation (its 2015, i even do not remember when Web 2.0 was invented).
Any hint to best practice?
And some more investigation before a more enlightened brain can lift the secret...
A somewhat similiar q/a solves the problem on how to get to call a backend method from JS if a key was handled in JS - use the
<p:remoteCommand>
see Catch key pressed ajax event without input fields for the ugly details.
Again, this is a global key catch, not component sensitive. But nice to know. Does this exist in plain JSF, too?

Redirection takes place , but url is not updated accordingly

i have used navigation rules , to navigate from one page to another in jsf . But the problem is the url is not getting updated though i used in faces-config page .
code snippet is below:
faces-config.xml :
<navigation-rule>
<display-name>faces/layout.xhtml</display-name>
<from-view-id>/faces/layout.xhtml</from-view-id>
<navigation-case>
<from-outcome>logout</from-outcome>
<to-view-id>/faces/logout.xhtml</to-view-id>
<redirect />
</navigation-case>
</navigation-rule>
source.java
public String Logout() {
FacesContext facesContext = FacesContext.getCurrentInstance();
isLogged = false;
session = (HttpSession)facesContext.getExternalContext().getSession(false);
if(session != null)
{
session.setAttribute("username", "");
session.invalidate();
}
return "logout";
}
sample.xhtml :
<p:splitButton value="#{loginBean.getUsername()}" icon="ui-icon-person">
<p:menuitem value="Logout" icon="ui-icon-close" action="#{source.Logout()}" ajax="false"/>
</p:splitButton>
any help is always appreciated
Thanks in advance

composite component button action issue in jsf2 mojarra

Trying to develop a composite component using jsf2.0 (Mojarra) which should render command buttons dynamically based on the list from the bean. I was able to render the buttons but action is not getting triggered.Could any one please help me to resolve the issue?
Here follows the code
<composite:interface>
<composite:attribute name="buttonList" required="true"
type="java.util.List" />
<composite:attribute name="beanName" required="true"
type="java.lang.Object" />
</composite:interface>
<composite:implementation>
<ui:repeat var="listItem" value="#{cc.attrs.buttonList}">
<h:commandButton value="#{listItem.buttonName}"
action="#{cc.attrs.beanName.listItem.buttonAction}">
</h:commandButton>
</ui:repeat>
</composite:implementation>
This is used as
<utils:buttonGroup buttonList="#{testButtonBean.buttonList}"
beanName="#{testButtonBean}" />
The bean looks like
public class TestButtonBean {
public List<ButtonPOJO> buttonList = new ArrayList<ButtonPOJO>();
public List<ButtonPOJO> getButtonList() {
return buttonList;
}
public void setButtonList(List<ButtonPOJO> buttonList) {
this.buttonList = buttonList;
}
public void preProcess() {
if (null != buttonList && buttonList.size() == 0) {
ButtonPOJO ob1 = new ButtonPOJO("Continue", "next");
ButtonPOJO ob2 = new ButtonPOJO("Back", "prev");
buttonList.add(ob1);
buttonList.add(ob2);
}
}
public String next() {
return "page1";
}
public String prev() {
return "page2";
}
}
action="#{cc.attrs.beanName.listItem.buttonAction}"
This is not right. This syntax is basically looking for a property listItem on beanName and then trying to invoke the literal action buttonAction() on it.
You need the brace notation action="#{bean[methodName]}" if you want to specify the action method name as string coming from another bean property.
action="#{cc.attrs.beanName[listItem.buttonAction]}"
Unrelated to the concrete problem, if the above solution still fails, then that can only mean that the value="#{cc.attrs.buttonList}" has incompatibly changed during the form submit request. You need to make sure that exactly the same list is prepared during the postback as it was during the initial request. See also point 4 of commandButton/commandLink/ajax action/listener method not invoked or input value not updated.

Rendering ListBox takes too long on Windows Phone

I am working on a Windows Phone 7 Application using Local SQLite Database and I'm having an issue with the rendering time of pages that use DataBinding.
Currently it takes 60-70ms to retrieve the data from the database. Then it takes about 3100ms to render the data retrieved using a ListBox with DataBinding.
Here you can see the DataTemplate of the ListBox:
<DataTemplate x:Key="ListBoxItemTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="68" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock x:Name="TimeColumn"
Text="{Binding TimeSpan}" Grid.Column="0" Grid.Row="0"
Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" />
<TextBlock Text="{Binding Stop.StopName}" Grid.Column="1" Grid.Row="0"
Margin="15,0,0,0" TextWrapping="NoWrap" Foreground="Black"
HorizontalAlignment="Left" VerticalAlignment="Center" />
</Grid>
</DataTemplate>
Comment: I have tried it using Canvas instead of Grid too, same result.
Then, the database loads data into a CSList (using ViciCoolStorage) and that gets Binded to the ListBox:
StationList.ItemsSource = App.RouteViewModel.RouteStops;
Comment: I have tried to add the elements of the CSList to an ObservableCollection and bind that to the interface but didn't seem to change anything.
Question:
Am I doing something wrong that results in a huge load time - even if just loading 10 elements -, or this is normal? Do you have any recommendations to get a better performance with DataBinding?
Thank you for your answers in advance!
Corresponding Code Parts:
RouteViewModel.cs
private Route rRoute;
public Route Route
{
get
{
if (rRoute != null)
{
return rRoute;
}
else
{
return new Route();
}
}
}
public void LoadRoute(string index)
{
try
{
if (rRoute.RouteId != index)
{
RouteLoaded = false;
StationsLoaded = false;
TimetableLoaded = false;
}
}
catch (Exception) { }
this.index = index;
if (!RouteLoaded)
{
NotifyPropertyChanging("Route");
rRoute = Route.ReadSafe(index);
RouteLoaded = true;
NotifyPropertyChanged("Route");
}
}
private CSList<RouteTime> rtLine;
public CSList<RouteTime> RouteStops
{
get
{
if (rtLine != null)
{
return rtLine;
}
else
{
return new CSList<RouteTime>();
}
}
}
public void LoadRouteStops()
{
LoadRoute(index);
if (!this.StationsLoaded)
{
NotifyPropertyChanging("RouteStops");
rtLine = rRoute.RouteTimes.FilteredBy("DirectionId = #DirectionId", "#DirectionId", this.direction).OrderedBy("TimeSpan");
NotifyPropertyChanged("RouteStops");
StationsLoaded = true;
}
}
RouteView.xaml.cs
private string index;
private bool visszaut = false;
public RouteView()
{
InitializeComponent();
Loaded += new System.Windows.RoutedEventHandler(RouteView_Loaded);
}
void RouteView_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
DataContext = App.RouteViewModel;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
NavigationContext.QueryString.TryGetValue("index", out index);
App.RouteViewModel.LoadRoute(index);
App.RouteViewModel.Direction = Convert.ToInt32(visszaut);
App.RouteViewModel.LoadRouteStops();
StationList.ItemsSource = App.RouteViewModel.RouteStops;
}
RouteTime.cs - Class Implementation
[MapTo("RouteTimes")]
public class RouteTime : CSObject<RouteTime, int>
{
public int RouteTimeId
{
get
{
return (int)GetField("RouteTimeId");
}
set
{
SetField("RouteTimeId", value);
}
}
public int RouteId
{
get
{
return (int)GetField("RouteId");
}
set
{
SetField("RouteId", value);
}
}
public int StopId
{
get
{
return (int)GetField("StopId");
}
set
{
SetField("StopId", value);
}
}
public int TimeSpan
{
get
{
return (int)GetField("TimeSpan");
}
set
{
SetField("TimeSpan", value);
}
}
public Direction DirectionId
{
get
{
return (Direction)GetField("DirectionId");
}
set
{
SetField("DirectionId", value);
}
}
[OneToOne(LocalKey = "StopId", ForeignKey = "StopId")]
public Stop Stop
{
get
{
return (Stop)GetField("Stop");
}
set
{
SetField("Stop", value);
}
}
[ManyToOne(LocalKey = "RouteId", ForeignKey = "RouteId")]
public Route Route
{
get
{
return (Route)GetField("Route");
}
set
{
SetField("Route", value);
}
}
}
Okay so, in this scenario it seems the source of the slow rendering was the [OneToOne] connection between the RouteTime and Stop classes. If I bind to any of the variables that is stored in the linked class, the rendering takes a long time.
Fixed by
I have added a new partial class in the code where I need to show the results.
public partial class StopTimes
{
public int TimeSpan
{
get;
set;
}
public string StopName
{
get;
set;
}
}
Using an ad-hoc query in Vici CoolStorage, I've made my own query to request the data needed and viola, everything is there and rendering didn't take more then 1 second. Perhaps, during the rendering it requests the StopName field with an SQLQuery one by one?
Don't know, but thank you for your help anyway :)
DataBinding should not be the problem in this scenario - I never had any problems with it. It has something to do with your SQL DB for sure. I had lists of around 200 items, and it rendered fine under a reasonable time-slot of 100ms.
It either is your SQL implementation or you are using ViciCoolStorage wrong. Post your code.
Have you tried doing the filtering of your list outside of the LoadRouteStops() method? Maybe in the code behind instead of the viewModel? It seems like a lot of work to be done in between propertyChanging and propertyChanged notifications.
I'm confused: How do you do local SQLite on Windows Phone 7? Did you mean WP8 where this is supported?
In any case, consider using LongListSelector (built into WP8 - Toolkit in WP7), since it does a better job with virtualization when you have a lot of items to render.
The problem is mostly because of setting a lot of binding. Try to keep bindings minimal and try to do the hard work on the code behind. Such as;
"binding a list of string to xaml one by one"
versus
"by string.join method with just one binding to a text block"
I had
<TextBlock Text="{Binding Times[0]}"/>
<TextBlock Text="{Binding Times[1]}"/>
...
<TextBlock Text="{Binding Times[9]}"/>
And changed to
<TextBlock Text="{Binding TimesToLongString}"/>
This fixed the delay.
Used to load in 9 sec with 3 items.
Now it loads within 110ms with 10 items.
To check the time it takes to load the list, you can check out this post

Resources