NPMethod called on non-NPObject wrapped JSObject

I’m working on a “hybrid” android appilcation. In the app, part of the UI was written in HTML and hosted in a WebView. And I exposed several Java objects to JavaScript as Java Script Interface.

Setup WebView
1
2
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(irBlaster, "irBlaster");

The irBlaster object contains several methods, and js will invoke the specific method according to the data-attribute bound to the element.

Script that handles the button click in HTML
1
2
3
4
5
6
7
8
9
10
onIrButtonClicked: (e) =>
$button = $(e.currentTarget)
type = $button.data('irType')
sendFunc = irBlaster[type]
code = @parseCode($button.data('irCode'))
length = $button.data('irLength')
sendFunc(length, code)

The previous coffee-script works fine in my Jasmine tests with javascript mock version of irBlaster. When the button clicked, the proper method was invoked with proper arguments.
But when I run this code with real android app, WebView yields error says “NPMethod called on non-NPObject wrapped JSObject”.

The error message looks quite hard to understand the meaning, so I spent quite time to diagnose the code.

After several try, I found the following code works fine, but original one doesn’t:

Code works
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
onIrButtonClicked: (e) =>
$button = $(e.currentTarget)
type = $button.data('irType')
switch irBlaster[type]
when 'NEC'
code = @parseCode($button.data('irCode'))
length = $button.data('irLength')
irBlaster.NEC(length, code)
.
.
.

So I realize the issue was occurd in the javascript contextual binding.

Javascript is function-first-citizen language, and method call on object was simulate by invoking the function with specific context, and the code behavior won’t change if there is no reference to this pointer in the method. So it is quite normal that fetch a method from a object than invoke it without context.

That is why the Jasmine tests passed successfully. But the irBlaster isn’t real or simple javascript, but a native java object provided by js binding interface, so there is a limitation that the method on it cannot be invoked without context. Or it causes error.

So the issue can be resolved as following code by invoking the method in a “reflection” flavor:

Invoking native binding object with context provided in HTML
1
2
3
4
5
6
7
8
9
10
onIrButtonClicked: (e) =>
$button = $(e.currentTarget)
type = $button.data('irType')
sendFunc = irBlaster[type]
code = @parseCode($button.data('irCode'))
length = $button.data('irLength')
sendFunc.call(irBlaster, length, code)

In previous code, I invoke the sendFunc with call, and provids irBlaster as the context as this. So the problem solved, the code runs smoothly without issue.

Sync Notify Pattern for WPF Cross Thread View Model

WPF has a powerful data binding mechanism, by which it is easy to implement MVVM and MVC pattern for UI application.
But thing gets not so beautiful once you introduced multi-thread into the app. Somehow, it means you have to manually call dispatcher method to synchronize the thread context, or else INotifyPropertyChanged contract causes cross thread violation, which is likely to drive UI component throw exception. But tons of calls to Dispatcher.Invoke or Dispatcher.BeginInvoke make you code ugly and hard to maintain. And it is boring to call dispatcher every time you try to write view model properties.

Is there any solution to this embarrassed situation?
The answer is Sync Notify pattern.

By analyzing the WPF data binding data flow, you can find the best point to behave the thread synchronization is at the boundary of your code and WPF implementation, which is the property changed event raiser. To sync the thread context here makes your code clean and effective.

Typically, you might implement the INotifyPropertyChanged in following way:

INofityPropertyChanged Implementation
1
2
3
4
5
6
7
8
9
10
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void Notify(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion

And you might implement the property of view model as following:

Notify Property Decalration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#region Notify Property ViewModelProperty
private string viewModelPropertyBackField;
public string ViewModelProperty
{
get { return viewModelPropertyBackField; }
set
{
if (viewModelPropertyBackField == value)
return;
viewModelPropertyBackField = value;
Notify("ViewModelProperty");
}
}
#endregion

This implementation works perfect in single thread context , but fails in multi-thread context. So we introduce a new event raiser implementation, which synchronize the thread context before raising the event:

SyncNotify
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#region Sync INotifyPropertyChanged Members
protected void SyncNotify(string propertyName, bool wait = false, Dispatcher dispatcher = null)
{
if (PropertyChanged == null)
return;
dispatcher = dispatcher ?? System.Windows.Threading.Dispatcher.CurrentDispatcher;
if (dispatcher.Thread == Thread.CurrentThread)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
else
{
if (wait)
{
dispatcher.Invoke(PropertyChanged, this, new PropertyChangedEventArgs(propertyName));
}
else
{
dispatcher.BeginInvoke(PropertyChanged, this, new PropertyChangedEventArgs(propertyName));
}
}
}
#endregion

This method check the whether it is running on the same thread as dispatcher, it not it synchronize the thread. It works perfect in most common cases.
But this implementation requires you to add some new code to the view model, and requires the event works as a delegate. This restriction sometime hurt, such as when you try to derive your view model from ObservableCollection<T>.
ObservableCollection<T> declared PropertyChanged event, but implemented it explicitly, which means you won’t be able to raise the event by calling PropertyChanged directly. Instead, you need to call protected method OnPropertyChanged.

In such cases, you need the following implementation powered by extension method:

SyncNotifyExtension
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
public static class SyncNotifyExtension
{
public static void SyncNotify(this INotifyPropertyChanged host, string propertyName, Action<PropertyChangedEventArgs> eventRaiser, bool wait = false, Dispatcher dispatcher = null)
{
Contract.Requires<ArgumentNullException>(host != null);
Contract.Requires<ArgumentException>(string.IsNullOrWhiteSpace(propertyName));
Contract.Requires<ArgumentNullException>(eventRaiser != null);
dispatcher = dispatcher ?? System.Windows.Threading.Dispatcher.CurrentDispatcher;
if (dispatcher.Thread == Thread.CurrentThread)
{
eventRaiser(new PropertyChangedEventArgs(propertyName));
}
else
{
if (wait)
{
dispatcher.Invoke(eventRaiser, new PropertyChangedEventArgs(propertyName));
}
else
{
dispatcher.BeginInvoke(eventRaiser, new PropertyChangedEventArgs(propertyName));
}
}
}
public static void SyncNotify(this INotifyPropertyChanged host, string propertyName, Action<string> eventRaiser, bool wait = false, Dispatcher dispatcher = null)
{
Contract.Requires<ArgumentNullException>(host != null);
Contract.Requires<ArgumentException>(string.IsNullOrWhiteSpace(propertyName));
Contract.Requires<ArgumentNullException>(eventRaiser != null);
dispatcher = dispatcher ?? System.Windows.Threading.Dispatcher.CurrentDispatcher;
if (dispatcher.Thread == Thread.CurrentThread)
{
eventRaiser(propertyName);
}
else
{
if (wait)
{
dispatcher.Invoke(eventRaiser, propertyName);
}
else
{
dispatcher.BeginInvoke(eventRaiser, propertyName);
}
}
}
public static void SyncNotify(this INotifyPropertyChanged host, string propertyName, Action<object, PropertyChangedEventArgs> eventRaiser, bool wait = false, Dispatcher dispatcher = null)
{
Contract.Requires<ArgumentNullException>(host != null);
Contract.Requires<ArgumentException>(string.IsNullOrWhiteSpace(propertyName));
Contract.Requires<ArgumentNullException>(eventRaiser != null);
dispatcher = dispatcher ?? System.Windows.Threading.Dispatcher.CurrentDispatcher;
if (dispatcher.Thread == Thread.CurrentThread)
{
eventRaiser(host, new PropertyChangedEventArgs(propertyName));
}
else
{
if (wait)
{
dispatcher.Invoke(eventRaiser, host, new PropertyChangedEventArgs(propertyName));
}
else
{
dispatcher.BeginInvoke(eventRaiser, host, new PropertyChangedEventArgs(propertyName));
}
}
}
public static void SyncNotify(this INotifyPropertyChanged host, string propertyName, Action<object, string> eventRaiser, bool wait = false, Dispatcher dispatcher = null)
{
Contract.Requires<ArgumentNullException>(host != null);
Contract.Requires<ArgumentException>(string.IsNullOrWhiteSpace(propertyName));
Contract.Requires<ArgumentNullException>(eventRaiser != null);
dispatcher = dispatcher ?? System.Windows.Threading.Dispatcher.CurrentDispatcher;
if (dispatcher.Thread == Thread.CurrentThread)
{
eventRaiser(host, propertyName);
}
else
{
if (wait)
{
dispatcher.Invoke(eventRaiser, host, propertyName);
}
else
{
dispatcher.BeginInvoke(eventRaiser, host, propertyName);
}
}
}
}

These extension methods can be called inside the view model, and it ensures that the event is raised on the dispatcher thread.
Since these extension methods uses delegate to invoke the event raiser, so it is unnecessary to worry about the accessibility of the event raiser, event it is declared as protected or private. And 4 overloads can handle most common event raisers.

Binding Visual Element Visibility to Mouse Over Event in WPF 4.0

I’m working on an user interface demo application in WPF.
I have a group of expander on the ui, and I want the expander show some descriptive text besides its caption while the mouse over it.
I used a TextBlock element to display the descriptive text, which is a child element in the control’s visual tree.

At first, I tried to use Style and Trigger to achieve this effect, but failed. Since Style Trigger apply its modification to the exactly same element that fires the trigger, which means if I would like to modify the property TextBlock, I can only fire the trigger by moving mouse over the TextBlock it self but the control that contains the TextBlock.

I tried some other ways, but all failed. Finally, I found I can achieve the effect by Data Binding!
Since TextBlock’s Visibility Property is a Dependency Property, and the TextBlock is contained by the Expander Control!
So we can Bind TextBlock’s Visibility Property to Expander Control’s IsMouseOver Property!
To achieve this, we need Relative Binding Source to locate TextBlock’s ancestor element, Expander Control.
Then we need BooleanToVisibilityConverter to convert the bool type value to Visibility value.

Here is the Xaml Code:

Data Template
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<HierarchicalDataTemplate DataType="{x:Type m:CategoryItem}"
ItemsSource="{Binding Properties}">
<Border>
<Expander ToolTip="{Binding Tooltip}" IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal" >
<TextBlock Text="{Binding Name}"/>
<TextBlock Name="HeaderDescription"
Margin="10,0,0,0"
Text="{Binding Description}"
Foreground="Green"
Visibility="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Expander}}, Path=IsMouseOver, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</StackPanel>
</Expander.Header>
<ListBox
DataContext="{Binding Properties}"
ItemsSource="{Binding}"
IsSynchronizedWithCurrentItem="True"/>
</Expander>
</Border>
</HierarchicalDataTemplate>