Windows Store Apps – Implementing custom Trigger-Action

In the previous post we showed you how to invoke a trigger action in XAML. As any developer would now ask:”Is it possible to write our own action that will be invoked on a specific event?”
And the answer is: Yes, you can do this.

This post will cover this topic.
For example you want to navigate to a page and instantly inform their underlying view model that this site has been navigated. Ofcourse you can do this with a seperate controller but let’s imagine we only have a view model and don’t want any code-behind in our views.
The first step you have to do is to create a new class which inherits from DependencyObject and implements the IAction interface. Despite the default navigation functionallity we need to implement the informing-process. To provide the default navigation we help onself by reusing the existing NavigateToPageAction and delegating calls to this class. Altogether this looks like this:

public sealed class NavigateToPageActionWithNotification : DependencyObject, IAction
    {
        private NavigateToPageAction _navigateAction;

        public object Parameter
        {
            get
            {
                return (object)GetValue(ParameterProperty);
            }
            set
            {
                _navigateAction.Parameter = value;
                SetValue(ParameterProperty, value);
            }
        }

        // Using a DependencyProperty as the backing store for Parameter.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ParameterProperty =
            DependencyProperty.Register("Parameter", typeof(object), typeof(NavigateToPageActionWithNotification), new PropertyMetadata(null));

        public String TargetPage
        {
            get
            {
                return (String)GetValue(TargetPageProperty);
            }
            set
            {
                _navigateAction.TargetPage = value;
                SetValue(TargetPageProperty, value);
            }
        }

        // Using a DependencyProperty as the backing store for TargetPage.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TargetPageProperty =
            DependencyProperty.Register("TargetPage", typeof(String), typeof(NavigateToPageActionWithNotification), new PropertyMetadata(String.Empty));

        public NavigateToPageActionWithNotification()
        {
            _navigateAction = new NavigateToPageAction();
        }

        public object Execute(object sender, object parameter)
        {
           
            var o = _navigateAction.Execute(sender, parameter);
            Frame f = Window.Current.Content as Frame;
            var page = f.Content as Page;

            if(page != null)
            {
                var navigatable = page.DataContext as INavigatableViewModel;
                if (navigatable != null)
                {
                    if (Parameter != null)
                        navigatable.OnNavigated(sender, Parameter);
                    else
                        navigatable.OnNavigated(sender, parameter);
                }  
            }
            return o;
        }
    }

We just delegate the Parameter and TargetPage properties to the internal NavigateToPageAction object.
The Execute-Method then contains our special logic. First ofcourse we do the navigation via the internal navigation-object and then we expect the new page to provide a INavigatableViewModel as DataContext. After we retrieved a valid INavigationViewModel we can inform this viewmodel with the navigation information.

So this is our backend for the new action. But whats about the XAML?
No big point here, just add your namespace of your custom action to the page and then add the action as trigger action. For example you want to navigate to a different page on a ItemClick of an ListView:

<trigger:Interaction.Behaviors>
    <tcore:EventTriggerBehavior EventName="ItemClick" >
       <local:NavigateToPageActionWithNotification TargetPage="SpecialPage" Parameter="{Binding ClickedItem, ElementName=listView}" />
     </tcore:EventTriggerBehavior>
</trigger:Interaction.Behaviors>

Colored logcat – reloaded

After stumbling across another useful github repository from the famous Jake Wharton called pidcat, we want to share how we work with the logcat while developing our apps.

Like pidcat, we use the great coloredlogcat python script from Jeff Sharkey as a basis but with some minor improvements.

  • colors for the debug level Warning and Error are extended to the message part to make them easier to spot
  • added a timestamp (from your PC, not the device!) including ms to enable rough time measurements between log messages

The result looks like that

coloredlogcat

The source of the script can be found on pastebin (direct download link). Just create an executable .py file with the given source code.

For Linux:
To ease the usage for the script, you can add the following to your .bash_alias file. Don’t forget to modify the SDK_DIR variable and the path to the coloredlogcat.py script

# Android related
SDK_DIR="/opt/android-studio/sdk"

alias clearlogcat=”$SDK_DIR/platform-tools/adb logcat -c”

# get dynamic logcat based on the parameter
dynamicLogcat()
{
if [ $# -eq 1 ]
then
$SDK_DIR/platform-tools/adb logcat *:$1 | /path/to/coloredlogcat.py
else
$SDK_DIR/platform-tools/adb logcat *:V | /path/to/coloredlogcat.py
fi
}
alias logcat=dynamicLogcat
Save the file and reopen your terminal (so that the .bash_alias will be loaded again)

To start the logcat output (with log level VERBOSE as default) just type in your console:

logcat

If you want to modify the log level, just add the letter of the lowest level you want to the command (here WARNING and ERROR):

logcat W

Two final notes:

  • There is a “bug” that the output of the script does not resize with changed size of the terminal window. If you need to change the size, just stop the script with CTRL + C and start the command again.
  • As the output can be quite long and fast, we recommend to have at least a scrollback in your terminal window of 3.000 lines or more. Over the time we consider 5.000 lines as a good value which will give you some history of the log, too.

Windows Store Apps – Invoke Triggeractions in XAML

In Windows Phone its a common approach to trigger Commands, Actions or Methods via XAML-Trigger.
This behavior can also be applied in Windows Store Apps.
The following steps requiere a Windows 8.1 Store App, which means you need to run Windows 8.1 and Visual Studio 2013.
The first thing to do is to add the Behaviors SDK(XAML) to your solution. You do this the following way:

  • Open the ‘Add Reference’ Dialog in your solution by right-clicking on the ‘References’ and ‘Add References’
  • On the left side choose ‘Windows’ and than the ‘Extension’ tab
  • Now check the ‘Behaviors SDK(XAML)’ and you are done with this steps

After you included the Behaviors SDK you can now apply trigger to your Page and Controls.
Imagine you have the following XAML-Page:

<Page
    x:Class="SampleTriggerActionPage"
    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"
    mc:Ignorable="d"
    xmlns:trigger="using:Microsoft.Xaml.Interactivity"
    xmlns:tcore="using:Microsoft.Xaml.Interactions.Core" >
   
   
    <Grid>
        <Button x:Name="nextPageButton" Content="Click to navigate to the next Page" >
            <trigger:Interaction.Behaviors>
                <tcore:EventTriggerBehavior EventName="Click">
                    <tcore:NavigateToPageAction TargetPage="SampleTrigger.NextPage" />
                </tcore:EventTriggerBehavior>
            </trigger:Interaction.Behaviors>
        </Button>
    </Grid>
   
</Page>

You need to add the needed xml-namespaces to get access to the trigger.

Afterwards, you add the specific trigger as behavior to the button. In this case its a event-trigger listening to the Click-Event of the button. For each behavior you add, you need to specify a action that performs if the trigger is executed. For this example its a NavigateToPageAction where you specify the page to navigate to.
There are also some other actions, e.g.:

  • CallMethodAction
  • ChangePropertyAction
  • GoToStateAction
  • InvokeCommandAction

In the next post we will show you how to write a custom action!

So keep on track!

Moving localization from arrays.xml to strings.xml

Android provides a quite comfortable way of defining localized String arrays via providing one or more arrays.xml in the res/values folder (values, values-de, values-fr, …) e.g.

<string-array name="status>    
    <item>Unknown</item>
    <item>None</item>
    <item>New</item>
    <item>In Progress</item>
    <item>Solved</item>
    <item>Closed</item>
    <item>Invalid</item>
    <item>Reopen</item>
    <item>Zombie</item>
</string-array>

We experienced some disadvantages doing so:

  • you have to maintain two or more arrays.xml, if you have a lot of languages that can be quite an effort (and memory overhead)
  • when adding, removing or changing the order you need to that in each arrays.xml which is quite error-prone
  • you have to maintain two files per language in your favoured localization tool

With one arrays.xml containing only links to the localized strings.xml you can

  • easily add or remove or change the order within an array in a single arrays.xml without the need to do that for all language arrays.xml
  • maintain only the strings.xml in your favoured localization tool (online or custom offline)

For moving the localization content from the arrays.xml to the strings.xml we wrote a little Python SCRIPT creating String keys (with optional prefix, e.g. list_) for all items within an array, replacing the string with the key and providing the keys in a new strings.xml, e.g.

<string-array name="status">;
    <item>@string/status_unknown</item>
    <item>@string/status_none</item>
    <item>@string/status_new</item>
    <item>@string/status_in_progress</item>
    <item>@string/status_solved</item>
    <item>@string/status_closed</item>
    <item>@string/status_invalid</item>
    <item>@string/status_reopen</item>
    <item>@string/status_zombie</item>
</string-array>

Conent of new strings.xml

    ...
    <string name="status_unknown">Unknown</string>
    <string name="status_none">None</string>
    ...

Usage example:

py replace-array-strings.py -i arrays.xml -p array_

Notes:

  • you have to handle special characters yourself if it is included in one of your strings since Android does not allow all characters within String keys (e.g. “-”, “(“, …)
  • it currently works only on one language, we try to that for all languages at once

 

Setting Text Selection and Cursor Position in UITextFields

Would’t it be wonderful to set the cursor position of a UITextField just like we are used to from UITextViews?

[textField setSelectedRange:NSMakeRange(0, 0)];

This actually works, unfortunately it is an undocumented API. However, UITextField conforms to the UITextInput protocol, offering a comprehensive API for selecting text. The following method can be used with a NSRange. If range.length==0, only the cursor is set to  range.location without selecting text.

- (void)selectTextInTextField:(UITextField *)textField range:(NSRange)range {
UITextPosition *from = [textField positionFromPosition:[textField beginningOfDocument] offset:range.location];
UITextPosition *to = [textField positionFromPosition:from offset:range.length];
[textField setSelectedTextRange:[textField textRangeFromPosition:from toPosition:to]];
}

Android WebView in Scrollview

Putting a scrolling WebView inside a ScrollView in Android won’t work out of the box.
The ScrollView will intercept the WebView’s swipe events, preventing it from scrolling.

Answer 2 to this stackoverflow question describes the solution:
http://stackoverflow.com/questions/13257990/android-webview-inside-scrollview-scrolls-only-scrollview

You have to subclass WebView with something like this:

package com.mypackage.common.custom.android.widgets

public class TouchyWebView extends WebView {

    public TouchyWebView(Context context) {
        super(context);
    }

    public TouchyWebView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public TouchyWebView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event){
        requestDisallowInterceptTouchEvent(true);
        return super.onTouchEvent(event);
    }
}

Conditional scroll in a UITableView

We recently had the problem, we needed to call some custom code, only when scrollToRowAtIndexPath:atScrollPosition: would actually scroll our tableView. Here is the solution:

UITableViewCell* cell = self.tableDataView.tableView.visibleCells[0];
NSIndexPath* path = [self.tableDataView.tableView indexPathForCell:cell];
if (path.section != i) {
[self.tableDataView.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:i]
atScrollPosition:UITableViewScrollPositionTop
animated:YES];
//your own done
}

Initialize a private class

If you ever what to instanciate a class on iOS that you know exists, let´s say it exists on MacOSX and you´ve seen in in a log. Or if you want to play with other private API here is how you do [[NSTextTab alloc] initWithType:0 location:224] without even knowing the NSTextTab class.

Class NSTextTabClass = NSClassFromString(@"NSTextTab");
id tab = [NSTextTabClass alloc];
id realTap;
SEL selector = @selector(initWithType:location:);
NSMethodSignature* signature = [tab methodSignatureForSelector:selector];
NSInvocation* invoke = [NSInvocation invocationWithMethodSignature:signature];
[invoke setSelector:selector];
NSInteger mode = 0;
[invoke setArgument:&mode atIndex:2];
CGFloat position = 224.0;
[invoke setArgument:&position atIndex:3];
[invoke setTarget:tab];
[invoke invokeWithTarget:tab];
[invoke getReturnValue:&realTap];

This should show you how to do these kinds of tasks with NSInvocation;

By the way, class-dump is a great tool to find out about private classes if you have the binaries.