Tuesday, December 23, 2014

Javascript Client Side File Size Validation

When you attempt to upload a file that is larger than the web server will accept, it just stops processing. It might be OK, but you don't have a way to be notified. It would be nice to be able to do have something like the following code snippet.
$("#editDialog form").submit(function( event ) {
    return validateFileSize('profile_image', 1024*1024*2, 'Profile Image', event);
});
The solution is very simple with the HTML5 file reader functionality. We can add some JavaScript to run before we attempt to send the bad files. The following achieves the validation needed.
function validateFileSize(id, limit, label, event) {
    if (typeof FileReader !== "undefined" && document.getElementById(id).files.length > 0) {
        var size = document.getElementById(id).files[0].size;
        if (size > limit) {
            alert(label + ' is too large.  The file must be less than ' + formatSize(limit) + '.');
            event.preventDefault();
            return false;
        }
    }
    return true;
}
I am utilizing my JavaScript function from my previous post JavaScript Format File Size Truncate Decimals. This allows me to format the file size very nicely.
function formatSize(bytes, decimals) {
    if (!!!bytes) return 'n/a';
    var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'],
        i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))),
        decMult = Math.pow(10, decimals || 2);
    return (Math.round((bytes / Math.pow(1024, i)) * decMult)) / decMult + ' ' + sizes[i];
}

Friday, December 19, 2014

Checking For Network Reachabilty In Xamarin

Just ran into this little gem. Everything that I found on how to detect network reachability in says that the following code should be used.
await Network.IsReachable(url, new TimeSpan(NetworkTimeout))
This worked on every iOS device I could get, except the most important device - the client's. It works on iPhone 4s, 5, and 5s, also it worked on my iPad, but not the client's iPhone 5. After much research and frustration, I started diving into the Network.InternetConnectionStatus() function to see if that would be fruitful. I tried the following:
return !Network.InternetConnectionStatus().HasFlag(NetworkStatus.NotReachable);
which naturally (for Xamarin) it is not reliable. I ended up trying the following setup, which seemed to work for on my devices.
var networkstatus = Network.InternetConnectionStatus();
return 
    networkstatus.HasFlag(NetworkStatus.ReachableViaWiFiNetwork) || 
    networkstatus.HasFlag(NetworkStatus.ReachableViaCarrierDataNetwork);
After pushing the build out to the client, he was able to log in. This way has some downsides, as it doesn't check to see if you can actually reach the site you want, but at least you can detect if there is network connectivity.

I would love to submit a bug, but I am not sure how to reliably recreate the issue. It is little things like this that do not build my confidence in Xamarin.

Wednesday, December 17, 2014

Xamarin Forms Non-Native CheckBox

Having experience developing iOS applications, I know that there is no "check box" per say. When I searched for a Xamarin Forms built in check box and was left with only an native implementation, this would have been acceptable, except that they were causing some significant performance issues/lag when a view containing the controls would be added to the navigation stack.

In my last couple posts, I dove into creating non-native controls in Xamarin Forms. I can build on that knowledge to build a Xamarin Forms checkbox hopefully improve performance. This definitely renders a lot faster.
<localcontrol:CheckBoxView Checked="{Binding SomeBooleanProperty}" DefaultText="Check Box Text" ReadOnly="true" HorizontalOptions="FillAndExpand" TextColor="#000000" FontSize="12" />
The source code...
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
       x:Class="IntPonApp.Controls.CheckBoxView">

 <StackLayout x:Name="CheckBoxStack" Orientation="Horizontal">
  <StackLayout.GestureRecognizers>
   <TapGestureRecognizer 
      Command="{Binding CheckCommand}"
      CommandParameter="#" />
  </StackLayout.GestureRecognizers>
  <Image x:Name="boxImage" 
      Source="{Binding BoxImageSource}" />
  <Label x:Name="textLabel" 
      Text="{Binding Text}" 
      LineBreakMode="WordWrap" 
      XAlign="Center" 
      HorizontalOptions="StartAndExpand" 
      VerticalOptions="Center" 
      TextColor="{Binding TextColor}" 
      Font="{Binding Font}" />
 </StackLayout>
 
</ContentView>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Labs;
using CustomControls;
using System.Diagnostics;
using System.Windows.Input;

namespace Check123Mobile.Controls
{
    public partial class CheckBoxView
    {
        #region Properties

        /// <summary>
        /// The width request in inches property.
        /// </summary>
        public static readonly BindableProperty CheckedProperty =
            BindableProperty.Create<CheckBoxView, bool>(
                p => p.Checked, false
                , propertyChanged: new BindableProperty.BindingPropertyChangedDelegate<bool>(
                    (BindableObject obj, bool oldPlaceHolderValue, bool newPlaceHolderValue) =>
                    {
                        var cbv = (CheckBoxView)obj;
                        cbv.BoxImageSource = cbv.GetCheckBoxImageSource();
                    })
                );

        protected static readonly BindableProperty BoxImageSourceProperty =
            BindableProperty.Create<CheckBoxView, ImageSource>(
                p => p.BoxImageSource, null);

        /// <summary>
        /// The read only property.
        /// </summary>
        public static readonly BindableProperty ReadOnlyProperty =
            BindableProperty.Create<CheckBoxView, bool>(
                p => p.ReadOnly, false);

        /// <summary>
        /// The checked text property.
        /// </summary>
        public static readonly BindableProperty CheckedTextProperty =
            BindableProperty.Create<CheckBoxView, string>(
                p => p.CheckedText, string.Empty);

        /// <summary>
        /// The unchecked text property.
        /// </summary>
        public static readonly BindableProperty UncheckedTextProperty =
            BindableProperty.Create<CheckBoxView, string>(
                p => p.UncheckedText, string.Empty);

        /// <summary>
        /// The checked image property.
        /// </summary>
        public static readonly BindableProperty CheckedImageProperty =
            BindableProperty.Create<CheckBoxView, string>(
                p => p.CheckedImage, string.Empty);

        /// <summary>
        /// The unchecked image property.
        /// </summary>
        public static readonly BindableProperty UncheckedImageProperty =
            BindableProperty.Create<CheckBoxView, string>(
                p => p.UncheckedImage, string.Empty);

        /// <summary>
        /// The default text property.
        /// </summary>
        public static readonly BindableProperty DefaultTextProperty =
            BindableProperty.Create<CheckBoxView, string>(
                p => p.Text, string.Empty);

        /// <summary>
        /// Identifies the TextColor bindable property.
        /// </summary>
        /// 
        /// <remarks/>
        public static readonly BindableProperty TextColorProperty =
            BindableProperty.Create<CheckBoxView, Color>(
                p => p.TextColor, Color.Black);

        /// <summary>
        /// The font size property
        /// </summary>
        public static readonly BindableProperty FontSizeProperty =
            BindableProperty.Create<CheckBoxView, double>(
                p => p.FontSize, -1);

        /// <summary>
        /// The font name property.
        /// </summary>
        public static readonly BindableProperty FontNameProperty =
            BindableProperty.Create<CheckBoxView, string>(
                p => p.FontName, string.Empty);

        public static ImageSource CheckedImageSource { get; protected set; }

        public static ImageSource UncheckedImageSource { get; protected set; }

        /// <summary>
        /// The checked changed event.
        /// </summary>
        public EventHandler<EventArgs<bool>> CheckedChanged;

        /// <summary>
        /// Gets or sets a value indicating whether the control is checked.
        /// </summary>
        /// <value>The checked state.</value>
        public bool Checked
        {
            get
            {
                return this.GetValue<bool>(CheckedProperty);
            }

            set
            {
                this.SetValue(CheckedProperty, value);
                var eventHandler = this.CheckedChanged;
                if (eventHandler != null)
                {
                    eventHandler.Invoke(this, value);
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the control is checked.
        /// </summary>
        /// <value>The checked state.</value>
        public bool ReadOnly
        {
            get
            {
                return this.GetValue<bool>(ReadOnlyProperty);
            }

            set
            {
                this.SetValue(ReadOnlyProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating the checked text.
        /// </summary>
        /// <value>The checked state.</value>
        /// <remarks>
        /// Overwrites the default text property if set when checkbox is checked.
        /// </remarks>
        public string CheckedText
        {
            get
            {
                return this.GetValue<string>(CheckedTextProperty);
            }

            set
            {
                this.SetValue(CheckedTextProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the control is checked.
        /// </summary>
        /// <value>The checked state.</value>
        /// <remarks>
        /// Overwrites the default text property if set when checkbox is checked.
        /// </remarks>
        public string UncheckedText
        {
            get
            {
                return this.GetValue<string>(UncheckedTextProperty);
            }

            set
            {
                this.SetValue(UncheckedTextProperty, value);
            }
        }

        public ImageSource BoxImageSource
        {
            get
            {

                return this.GetValue<ImageSource>(BoxImageSourceProperty) ?? GetCheckBoxImageSource();
            }

            set
            {
                this.SetValue(BoxImageSourceProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating the checked text.
        /// </summary>
        /// <value>The checked state.</value>
        /// <remarks>
        /// Overwrites the default text property if set when checkbox is checked.
        /// </remarks>
        public string CheckedImage
        {
            get
            {
                return this.GetValue<string>(CheckedImageProperty);
            }

            set
            {
                this.SetValue(CheckedImageProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the control is checked.
        /// </summary>
        /// <value>The checked state.</value>
        /// <remarks>
        /// Overwrites the default text property if set when checkbox is checked.
        /// </remarks>
        public string UncheckedImage
        {
            get
            {
                return this.GetValue<string>(UncheckedImageProperty);
            }

            set
            {
                this.SetValue(UncheckedImageProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the text.
        /// </summary>
        public string DefaultText
        {
            get
            {
                return this.GetValue<string>(DefaultTextProperty);
            }

            set
            {
                this.SetValue(DefaultTextProperty, value);
            }
        }

        public Color TextColor
        {
            get
            {
                return this.GetValue<Color>(TextColorProperty);
            }

            set
            {
                this.SetValue(TextColorProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the size of the font.
        /// </summary>
        /// <value>The size of the font.</value>
        public double FontSize
        {
            get
            {
                return (double)GetValue(FontSizeProperty);
            }
            set
            {
                SetValue(FontSizeProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the name of the font.
        /// </summary>
        /// <value>The name of the font.</value>
        public string FontName
        {
            get
            {
                return (string)GetValue(FontNameProperty);
            }
            set
            {
                SetValue(FontNameProperty, value);
            }
        }

        public Font Font
        {
            get
            {
                return Font.SystemFontOfSize(FontSize);
            }
        }

        public string Text
        {
            get
            {
                return this.Checked
                    ? (string.IsNullOrEmpty(this.CheckedText) ? this.DefaultText : this.CheckedText)
                        : (string.IsNullOrEmpty(this.UncheckedText) ? this.DefaultText : this.UncheckedText);
            }
        }

        public ICommand CheckCommand { get; protected set; }

        #endregion Properties

        #region Constructor

        public CheckBoxView()
        {
            CheckCommand = new Command((object s) =>
                {
                    if (!ReadOnly)
                    {
                        Checked = !Checked;
                    }
                });
            InitializeComponent();
            LoadImages();
            CheckBoxStack.BindingContext = this;
            boxImage.BindingContext = this;
            textLabel.BindingContext = this;
        }

        #endregion Constructor
         
        #region Image Functions

        protected void LoadImages()
        {
            if (CheckedImageSource == null)
            {
                CheckedImageSource = ImageSource.FromResource(GetCheckedImage());
            }
            if (UncheckedImageSource == null)
            {
                UncheckedImageSource = ImageSource.FromResource(GetUncheckedImage());
            }
        }

        private ImageSource GetCheckBoxImageSource()
        {
            return this.Checked ? CheckedImageSource : UncheckedImageSource;
        }

        private string GetCheckBoxImage()
        {
            return this.Checked
                        ? GetCheckedImage()
                        : GetUncheckedImage();
        }

        private string GetCheckedImage()
        {
            return (string.IsNullOrEmpty(this.CheckedImage) ?
                            "Check123Mobile.Resources.checked_checkbox.png" :
                            this.CheckedImage);
        }

        private string GetUncheckedImage()
        {
            return (string.IsNullOrEmpty(this.UncheckedImage) ?
                            "Check123Mobile.Resources.unchecked_checkbox.png" :
                            this.UncheckedImage);
        }

        #endregion Image Functions
    }
}

Friday, November 28, 2014

Windows 7 IKEv2 VPN "Verifying User and Password..."

I VPN quite often and recently something changed on my old laptop running Windows 7 which prevented me from connecting. I was able to connect using my laptop running Windows 8. After some hunting I came across this TechNet thread.

The fix ended up being very simple, but not intuitive.
  1. View your network adaptors by going to Control Panel > Network and Internet > Network Connection
  2. Right-click on the offending VPN adapter and click Properties
  3. Click on the Security tab
  4. Change the "Type of VPN" value from IKEv2 to Point to Point Tunneling Protocol (PPTP)
  5. Click OK
From the thread:
It seems that when this property is set to Automatic the WAN Miniport defaults to IKEv2 (and gets stuck if this is not the VPN type used).
Translated: it is a feature not a bug.

Now attempt to connect again. Short of other issues, it will connect successfully.

Thursday, November 20, 2014

Git Filter-Branch Saves The Day Again

Disclaimer

You can really mess things up, make sure you back up your files/repositories before you wield this axe.

Removing a large file from a Git repository

Recently I committed and pushed a commit to my remote repository. The push took an unusually long time. Looking at the commit, I discovered that I had committed/pushed a vagrant box into the repository. Doh! Now the repository was ~500MB. Here is what I did to clean up the repository and remove the large file.

I can't remember where I found this command (I may have assembled it from many locations). It is supposed to remove all references and the file from the repository.
git filter-branch --prune-empty -d /dev/shm/scratch --index-filter "git rm --cached -f --ignore-unmatch ubuntu-precise32-intpon.box" --tag-name-filter cat -- --all
This seemed to clean up the git tree, but didn't actually remove the file from the repository. So, on we go...

I ran across an Atlassian page which detailed several new steps to remove a large file. I skipped the first 3 steps because the above command seemed to do the same thing.

The next step was to prune all of the reflog references from now on back.
git reflog expire --expire=now --all

Then repack the repository by running the garbage collector and pruning old objects.
git gc --prune=now

Finally, push all your changes back to the remote repository.
git push origin master --force

Looking at my repository, it was back at ~45MB. I can pretend it never happened and life is good again. As long as I don't tell anyone about it.

Changing the author information

If you are committing to a public repository, you may not want your private email address exposed to the world. GitHub's change author info page has an excellent script that can fix that issue if you accidentally commit with the wrong email address. In case the page changes or disappears, here is the script:
#!/bin/sh
 
git filter-branch --env-filter '
 
OLD_EMAIL="your-old-email@example.com"
CORRECT_NAME="Your Correct Name"
CORRECT_EMAIL="your-correct-email@example.com"
 
if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
then
export GIT_COMMITTER_NAME="$CORRECT_NAME"
export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
then
export GIT_AUTHOR_NAME="$CORRECT_NAME"
export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL"
fi
' --tag-name-filter cat -- --branches --tags
Once it completes, you need to push the changes to the remote repository.
git push --force --tags origin 'refs/heads/*'
Your email is now changed.

Tuesday, October 21, 2014

A JUnit XSL Transform For PHPUnit Logging

I recently worked on a PHP application with a PHPUnit test suite. Not finding a good way to view the test run results, I settled on logging the output into JUnit format and then applied the style sheet to the XML file so that I can through the XML file into a browser.
time phpunit --testsuite "Application Test Suite" --log-junit public/testresults.xml & sed -i 's//\n/g' public/testresults.xml
Not sure why, but finding a decent transform for the JUnit output was more diffcult than expected. I decided to extended an XSL transform in this StackOverflow answer. It is a pretty good start, plain text, but I can work with it.

After applying Bootstrap styling, the end result is a pleasantly styled JUnit view in a browser. For some reason, FireFox was not applying the bootstrap CSS file, so I just brought in the styles that I needed, thus the style block.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" encoding="UTF-8" indent="yes"/>

    <xsl:template match="/testsuites">
        <!-- <xsl:text disable-output-escaping='yes'>&lt;!DOCTYPE html></xsl:text> -->
        <html class="no-js" lang="en">
        <head>
            <meta charset="utf-8" />
            <meta http-equiv="X-UA-Compatible" content="IE=edge" />
            <title>PHPUnit Test Results</title>
            <meta name="description" content="" />
            <meta name="viewport" content="width=device-width, initial-scale=1" />
            <!-- <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" /> -->
            <style>
                html {
                  font-family: sans-serif;
                  -webkit-text-size-adjust: 100%;
                      -ms-text-size-adjust: 100%;
                  font-size: 10px;
                  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
                }
                body {
                  margin: 0;
                  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
                  font-size: 14px;
                  line-height: 1.42857143;
                  color: #333;
                  background-color: #fff;
                }
                article,
                aside,
                details,
                figcaption,
                figure,
                footer,
                header,
                hgroup,
                main,
                nav,
                section,
                summary {
                  display: block;
                }
                hr {
                  height: 0;
                  -webkit-box-sizing: content-box;
                     -moz-box-sizing: content-box;
                          box-sizing: content-box;
                }
                pre {
                  overflow: auto;
                  font-family: monospace, monospace;
                  font-size: 1em;
                }

                .container {
                  padding-right: 15px;
                  padding-left: 15px;
                  margin-right: auto;
                  margin-left: auto;
                }
                .page-header {
                    padding-bottom: 9px;
                    margin: 40px 0 20px;
                    border-bottom: 1px solid #eee;
                }
                .failure, .error {
                    padding: 0px 20px;
                }
                .failure pre, .error pre {
                    border: 1px solid #DDD;
                    padding: 10px;
                }
            </style>
        </head>
        <body>
            <div class="container">
                <xsl:apply-templates select="testsuite" />
            </div>
        </body>
        </html>
    </xsl:template>

    <xsl:template match="testsuite">
        <header class="page-header">
        <h1>Testsuite: <xsl:value-of select="@name" /></h1>
        <div>
            <xsl:text>
            Tests run: </xsl:text>
            <xsl:value-of select="@tests" />
            <xsl:text>, Failures: </xsl:text>
            <xsl:value-of select="@failures" />
            <xsl:text>, Errors: </xsl:text>
            <xsl:value-of select="@errors" />
            <xsl:text>, Time elapsed: </xsl:text>
            <xsl:value-of select="@time" />
            <xsl:text> sec</xsl:text>
        </div>
        </header>
        <xsl:apply-templates select="system-out" />
        <xsl:apply-templates select="system-err" />
        <div>
            <xsl:apply-templates select="//testcase" />
        </div>
    </xsl:template>

    <xsl:template match="testcase">
        <p>
            <xsl:text>
            Testcase: </xsl:text>
            <xsl:value-of select="@name" />
            <xsl:text> took </xsl:text>
            <xsl:value-of select="@time" />
        </p>
        <xsl:apply-templates select="failure" />
        <xsl:apply-templates select="error" />
    </xsl:template>

    <xsl:template match="failure">
        <div class="failure">
            <span style="color: #ff4136;">
                <xsl:text>
                    Failure:
                </xsl:text>
                <xsl:value-of select="@type" />
            </span>
            <pre>
                <xsl:value-of select="." />
            </pre>
        </div>
    </xsl:template>

    <xsl:template match="error">
        <div class="error">
            <span style="color: #F00;">
                <xsl:text>
                    Error:
                </xsl:text>
                <xsl:value-of select="@type" />
            </span>
            <pre>
                <xsl:value-of select="." />
            </pre>
        </div>
    </xsl:template>

    <xsl:template match="system-out">
        <div>
            <xsl:text>
            ------ Standard output ------
            </xsl:text>
            <pre>
                <xsl:value-of select="." />
            </pre>
        </div>
    </xsl:template>

    <xsl:template match="system-err">
        <div>
            <xsl:text>
            ------ Error output ------
            </xsl:text>
            <pre>
                <xsl:value-of select="." />
            </pre>
        </div>
    </xsl:template>

</xsl:stylesheet>

Tuesday, October 7, 2014

Xamarin Forms Custom Controls Without Native Renderers

My last blog post discussed how to create reusable custom content views. It involved leveraging the ContentPage element's ViewModel. This is great when you want to easily pull common code out and make it reusable. This will be a great place to start building.

As with my last blog post, I was not able to find examples of people doing this pattern. It seems like such a common thing to want to do. I digress.

Let's say that there is a repeated layout structure which only differs by the ViewModel properties. What about making a custom control which do not depend on a ViewModel?

A custom control which doesn't depend on a ViewModel internally, will need to have properties which we can bind to a ContentPage ViewModel's property. We want to end up with something like:
<localcontrol:CountLabelView
    CountText="{Binding path=MessageCount}"
    Text="{Binding path=MessageText}" />
The first task is to add a new Forms Xaml Page to the project. I'll call it "CountLabelView".

Then, my experience with building native controls comes in handy. We can add BindableProperty fields to our custom control's code behind. One for each property we want to have available to our control.
public static readonly BindableProperty TextProperty =
    BindableProperty.Create<CountLabelView, string>(
        p => p.Text, 
        "", 
        BindingMode.TwoWay, 
        null,
        new BindableProperty.BindingPropertyChangedDelegate<string>(TextChanged), 
        null, 
        null);

public string Text
{
    get { return (string)GetValue(TextProperty); }
    set { SetValue(TextProperty, value); }
}

static void TextChanged(
    BindableObject obj, 
    string oldPlaceHolderValue, 
    string newPlaceHolderValue)
{
    
}
Now we have a way for a ContentPage to bind properties to custom ContentView controls.

Next we can move our layout into the ContentView.
<?xml version="1.0" encoding="utf-8" ?>
<ContentView 
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:IntPonApp;assembly=IntPonApp"
    xmlns:custom="clr-namespace:CustomControls.Controls;assembly=CustomControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
    x:Class="IntPonApp.Controls.CountLabelView">
 
 <StackLayout 
        Orientation="Horizontal" 
        HorizontalOptions="FillAndExpand" 
        VerticalOptions="FillAndExpand" 
        Padding="0">
  <custom:RoundFrame 
            HorizontalOptions="CenterAndExpand" 
            VerticalOptions="CenterAndExpand" 
            Padding="7,1" 
            BorderRadius="40" 
            FillColor="#333333" 
            HasShadow="false">

   <Label    x:Name="CountTextLabel"
                Text="{Binding CountText}" 
                VerticalOptions="Center" 
                XAlign="Center" 
                TextColor="#FFFFFF" />

  </custom:RoundFrame>

  <StackLayout 
            Spacing="0" 
            HorizontalOptions="StartAndExpand" 
            VerticalOptions="CenterAndExpand" 
            Padding="0">

   <Label    x:Name="TextLabel"
                Text="{Binding Text}" 
                LineBreakMode="WordWrap" 
                XAlign="Center" 
                HorizontalOptions="StartAndExpand" 
                VerticalOptions="Center" 
                TextColor="#DEDEDE" />

  </StackLayout>
 </StackLayout>
 
</ContentView>
This is great except, if you run this, the application will fail (unless CountText and Text happen to exist in your ContentPage's ViewModel. This is problematic, but not an insurmountable obstacle. We may have a couple options:
  1. We can remove the binding from the elements, assign names to the elements which need binding, hook up onchange events (not useful in this case because we are using labels and not Entry elements) which would assign the value back to the BindableProperty, and then find the named elements and assign the value from the BindableProperty.
  2. We can assign names to the elements which need binding and then find the named elements and assign the BindingContext to the custom control object.
  3. We can assign the custom control's BindingContext to the custom control itself.
Option 1 would be a mess to implement and maintain. It has to be replicated for every control which needs binding and there is a good chance that the bindings won't work as one should expect.

Option 2: We are able to bind in a much more expected fashion and changes will cascade as we expect. However this will still result in extra 1 line of code per control needing to be bound.

Option 3: It gets rid of the inherited BindingContext and forces the control to be self sufficient and thus reusable regardless of the ViewModel. Plus, the bindings work as one expects and it is only ONE line of code.

In testing Option 3, I found that the BindingContext was not being inherited correctly to the child controls. I suspect this is a bug in Xamarin. So, at the moment Option 2 is the best option that works.

Below is the code behind for the custom control.
public partial class CountLabelView
{
    #region Properties

    public static readonly BindableProperty TextProperty =
        BindableProperty.Create<CountLabelView, string>(
            p => p.Text, 
            "", 
            BindingMode.TwoWay, 
            null,
            new BindableProperty.BindingPropertyChangedDelegate<string>(TextChanged), 
            null, 
            null);

    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }

    static void TextChanged(
        BindableObject obj, 
        string oldPlaceHolderValue, 
        string newPlaceHolderValue)
    {
        
    }


    public static readonly BindableProperty CountTextProperty =
        BindableProperty.Create<CountLabelView, string>(
            p => p.CountText, 
            "", 
            BindingMode.TwoWay, 
            null,
            new BindableProperty.BindingPropertyChangedDelegate<string>(CountTextChanged), 
            null, 
            null);

    public string CountText
    {
        get { return (string)GetValue(CountTextProperty); }
        set { SetValue(CountTextProperty, value); }
    }

    static void CountTextChanged(
        BindableObject obj, 
        string oldPlaceHolderValue, 
        string newPlaceHolderValue)
    {

    }

    #endregion Properties

    #region Constructor

    public CountLabelView()
    {
        InitializeComponent();
        CountText = "1";
        //this.BindingContext = this;
        CountTextLabel.BindingContext = this;
        Text1Label.BindingContext = this;
        Text2Label.BindingContext = this;
    }

    #endregion Constructor
}
A point of note, the constructor contains this line CountText = "1";. This is required because there is a custom native control surrounding the label with a binding. For some reason the rendering process executes initially before the binding finishes and not having an initial value on the control will result in the custom native control's height to be ~1px. I suspect that this is another bug in Xamarin.

Then you add the XML namespace to the ContentPage declaration and add the custom control.
xmlns:localcontrol="clr-namespace:IntPonApp.Controls;assembly=IntPonApp"
<localcontrol:CountLabelView
    CountText="{Binding path=MessageCount}"
    Text="{Binding path=MessageText}" />

Wednesday, October 1, 2014

Xamarin Forms Reusable Custom Content Views

There is a lot (relatively) of information on how to make reusable native controls with renderers and native views. However there is next to nothing about how to make reusable controls which don't require renderers or native views.

[Edit 2014-12-05] The project I developed required the same header on each page, so I figured I would use that as a way to describe how to create resuable controls. Performance-wise, it shouldn't be any worse than loading the information on every page. I needed the NavigationPage functionality, but without the navigation bar. It is probably not ideal, but it is what the design imposed.

Let's assume that we have the following XAML which needs to be replicated on several pages.
<Grid x:Name="SharedHeaderBar" HorizontalOptions="FillAndExpand" Padding="5,10,5,-5" BackgroundColor="#3FB5C1" >
    <Grid.RowDefinitions>
      <RowDefinition Height="45" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>
    <Image x:Name="homeImageiOS" Source="{local:ImageResource IntPonApp.Resources.IntPon-Logo.png}" Grid.Row="0" Grid.Column="0" />
    <Button x:Name="logoffButtoniOS" Text="{Binding Path=FullName}" TextColor="#FFFFFF" Grid.Row="0" Grid.Column="2" />
  </Grid>
My first foray into this was unsuccessful because I tried using a View. I stumbled upon the ContentView. I couldn't find any examples of people using it. Having had a lot of experience with WPF and knowing what I wanted to achieve, this seemed to be the most promising path.

I created a new Forms Xaml Page and changed the ContentPage tags to ContentView. Then I moved the reused XAML into the ContentView.
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:IntPonApp;assembly=IntPonApp"
        x:Class="IntPonApp.Controls.SharedHeaderView">
  
  <Grid x:Name="SharedHeaderBar" HorizontalOptions="FillAndExpand" Padding="5,10,5,-5" BackgroundColor="#3FB5C1" >
    <Grid.RowDefinitions>
      <RowDefinition Height="45" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>
    <Image x:Name="homeImageiOS" Source="{local:ImageResource IntPonApp.Resources.IntPon-Logo.png}" Grid.Row="0" Grid.Column="0" />
    <Button x:Name="logoffButtoniOS" Text="{Binding Path=FullName}" TextColor="#FFFFFF" Grid.Row="0" Grid.Column="2" />
  </Grid>

</ContentView>
It is important to understand the state of the binding context. The binding context will be inherited from the parent control where it is used. This is very convenient because I wanted to pull a value from the ContentPage's ViewModel.

I pulled over the event bindings and the needed navigation functions. Part of the functionality that I wanted to move into the ContentView needed to raise a DisplayAlert which, as it turns out, isn't available on the ContentView element. So I flexed my recursive skills and created the following to get the ContentPage element.
public ContentPage FindParentPage(Element el = null)
{
    if (el == null)
        el = this;
    return 
          (el is ContentPage) ? (ContentPage)el
        : (el.Parent != null) ? FindParentPage(el.Parent) 
        : null;
}
Then I was able to display alert messages.

Below is the resulting code behind.
public partial class SharedHeaderView
{
    public SharedHeaderView()
    {
        InitializeComponent();

        string platformName = Device.OS.ToString();

        this.FindByName<Button>("logoffButton" + platformName)
            .Clicked += OnLogoffClicked;
        this.FindByName<Image>("homeImage" + platformName)
            .GestureRecognizers.Add(new TapGestureRecognizer((view, args) =>
        {
            this.Navigation.PopToRootAsync();
        }));
    }

    protected async void OnLogoffClicked(object sender, EventArgs e)
    {
        if (!string.IsNullOrEmpty(App.ApiKey))
        {
            string errorMessage = "";
            try
            {
                if (await FindParentPage()
                    .DisplayAlert("Sign Off", "Are you sure?", "Yes", "No"))
                {
                    App.Logout();
                }
            }
            catch (Exception ex)
            {
                errorMessage = ex.Message;
            }

            if (!string.IsNullOrEmpty(errorMessage))
            {
                await FindParentPage()
                    .DisplayAlert("Help", errorMessage, "OK");
            }
        }
    }

    public ContentPage FindParentPage(Element el = null)
    {
        if (el == null)
            el = this;
        return 
              (el is ContentPage) ? (ContentPage)el
            : (el.Parent != null) ? FindParentPage(el.Parent) 
            : null;
    }
}
Then you add the XML namespace to the ContentPage declaration and add the XML node.
xmlns:localcontrol="clr-namespace:IntPonApp.Controls;assembly=IntPonApp"
<localcontrol:SharedHeaderView />
Getting this to work has really saved me a lot of unneeded duplication.

Edit 2014-12-05: Edited to add comment/response/excuse to a question on StackOverflow: Is it possible to create a custom page layout with Xamarin.Forms?

Thursday, September 25, 2014

Synergy Desktop Path Error

I recently started working on a mobile project which afforded me the opportunity to flex my iOS development skills again. I installed the latest version of Synergy on my MacBook and my main laptop. I used the setup I had before which loaded correctly, but I started receiving this error which prevented me from connecting my Mac to my PC host.

ERROR: failed to get desktop path, no drop target available, error=2

Googling wasn't fruitful so I looked at the settings which looked as I expected. I finally tried unchecking the "Elevate" checkbox and clicked "Apply". The error went away and I was able to connect. "Elevate" should mean that the service will run in elevated/administrator mode. I found no events being raised in the event log and I believe I had the Synergy application successfully using the Elevate option in the past. Whatever the root cause was, not using the "Elevate" option fixed the issue.

For those that do not know about Synergy, it is an
application which allows you to share your keyboard and mouse with other computers over a network. I have been using Synergy for years and find is especially useful when I need to switch between multiple computers frequently.

Wednesday, September 3, 2014

IP Subnet Mask Expander The Web Client

In a previous post, I created a Node.Js project, IPSubnetMaskToIPRange, that takes a file with a list of IP subnet masks and then outputs 2 CSV files (one of the IP ranges and one of the expanded IP ranges).

Sometimes when I create a cool project, I end up having flashes of changes and improvements that I can make to it. This is one of those cases.

This project uses the same base functionality as the Node.Js project. This has the same functionality, except it is completely run from the browser leveraging AngularJS and Bootstrap 3. It outputs the ranges to the screen and provides buttons to download the IP ranges and the expanded IP ranges.

The working site can be found at IP Subnet Mask Expander and the source can be found on GitHub.

Monday, August 18, 2014

Angular.JS Service to Download Data Generated in Browser

I ran into a situation where I wanted to be able to download data that was generated in the browser. There are several libraries that handle this scenario, but I thought it should have been simpler than those large libraries.

Below is the result. It supports IE 10+ (potentially 9 haven't tested), FireFox, and Chrome.
angular.module('FileDownload')
.service('SaveFileService', [function () {
        this.Save = function(data, filename, mimeType) {
            var blob = new Blob([data.join('\n')], {type: mimeType});
            if (/\bMSIE\b|\bTrident\b|\bEdge\b/.test(navigator.userAgent)) {
                window.navigator.msSaveOrOpenBlob(blob, filename);
            } else {
                var url  = window.URL || window.webkitURL,
                    link = document.createElementNS("http://www.w3.org/1999/xhtml", "a"),
                    event = document.createEvent("MouseEvents");
                
                link.href = url.createObjectURL(blob);
                link.download = filename;

                event.initEvent("click", true, false);
                link.dispatchEvent(event);
            }
        };
    }]);
Basically, the Save function does a couple things. For IE browsers, it creates the blob and calls the msSaveOrOpenBlob (the msSaveBlob function would make IE only display a save dialog and not give the user the option to open it). For all other browsers, it creates the blob, generates a link element, creates a mouse click event, and has the link dispatch the created event.

To use the service, just inject the service into the controller and call the Save function w/ the needed parameters.

UPDATE 2015-09-28: Per Kevin's feedback, I have updated the regular expression to catch the Edge browser (so much for Microsoft making a standards evergreen browser). I have verified that the above change works as advertised.

Wednesday, August 13, 2014

First Data Global Gateway Integration and the HTTP 401 Unauthorized Error

A project recently required integrating with First Data for payment processing. This isn't my first rodeo integrating with a payment processor, but it was the first time I interfaced with First Data. Sounds easy enough, right? Following the Global Gateway version 6 documentation shouldn't be very difficult. Except when it isn't.

The documentation is terribly antiquated. The setup assumes you are on Windows XP and using IIS5/6. Thankfully have developed application in that environment and felt confident that I could translate the instructions to Windows 8.1 and IIS Express.

The first obstacle was getting the certificates. The original email I had said that I would receive the certificates in my email. When it didn't arrive, I was slightly confused. I called support and they instructed me to jump into the Virtual Terminal and download them there. I was back on track with the documentation.

I was able to use the Certificate MMC snap-in to import the P12 certificate into the Computer Account/Personal store. I then used the WinHttpCertCfg tool to grant the needed users access to the Certificate. The documentation specifies the IWAM_MachineName user, but knowing that doesn't exist, I opted to grant IIS_IUSRS, IUSR, NETWORK SERVICE, and my own account (because IIS Express runs under the users permissions). You may need to grant permission to "DefaultAppPool" if you are using IIS 7.5. Essentially, whatever user the application is going to run as, needs access to the certificate.

The next obstacle was getting the WSDL. Accessing the WSDL link from the documentation, https://ws.merchanttest.firstdataglobalgateway.com/fdggwsapi/services/order.wsdl, yielded SSL/TLS authentication handshake connection errors. It didn't matter which browser I was using.

After verifying that my setup followed the documentation and Google not finding anything that I hadn't already tried, I called support again. I was told that I was pointing to the wrong address and instead should point to the following addresses:

https://www.staging.linkpointcentral.com/fdggwsapi/services/order.wsdl
https://www.staging.linkpointcentral.com/fdggwsapi/schemas_us/v1.xsd
https://www.staging.linkpointcentral.com/fdggwsapi/schemas_us/a1.xsd
https://www.staging.linkpointcentral.com/fdggwsapi/schemas_us/fdggwsapi.xsd

At this point my confidence in the documentation was extremely low. These URLs were accessible so I felt good and was back on track with the documentation. I was able to generate the web reference and reworked the example to fit my environment. It compiled and I pointed my unit test to my shiny new payment service. What could possibly go wrong?

Well, I received a WebException HTTP 401: Unauthorized error. I probably should have expected another issue, but I try to be optimistic. After a LOT of googling and trying any suggestion I could find related to First Data and this exception. There were a lot of results; obviously I wasn't the only one experiencing this. The last option that I found was requesting the certificates to be regenerated. After exhausting all the other fruitful paths forward, I called support again.

I got a support person named Derek and requested to have the certificates regenerated. He ran the existing certificates through their test process and found that they worked. I told him about my environment and the other issues that I was having. He said that the WSDL URL I was told to use was not correct and that I should use the ones in the documentation (now my confidence was pretty thoroughly gone). After some testing, further information gathering, and research, he found that Windows 8 requires extra steps to get the WSDL. The end result was that he was going to send me the needed WSDL and XSDs and an unpublished document (hopefully they publish it soon) detailing the steps needed to setup the environment. Once I received the files I created a new web reference using the new WSDL/XSDs and used the documentation URLs.

https://ws.merchanttest.firstdataglobalgateway.com/fdggwsapi/services/order.wsdl
https://ws.merchanttest.firstdataglobalgateway.com/fdggwsapi/schemas_us/fdggwsapi.xsd
https://ws.merchanttest.firstdataglobalgateway.com/fdggwsapi/schemas_us/v1.xsd
https://ws.merchanttest.firstdataglobalgateway.com/fdggwsapi/schemas_us/a1.xsd
https://ws.merchanttest.firstdataglobalgateway.com/fdggwsapi/services

I updated my code, compiled and ran my unit tests. Then, I received a failed transaction because the card information I submitted was expired. It meant that it worked! I changed the expiration to a point in the future, tried again, and received an APPROVED response. What a great feeling! Derek stayed on the line through the whole process (which took quite a while). Thank you Derek.

Later I went through the unpublished document and specifically the part about getting the WSDL file. Basically, the P12 file needs to be imported into the browser. To do that do the following:
  1. Open Internet Explorer
  2. In the menu, click Tools->Internet Options
  3. Click the Content tab
  4. Click Certificates
  5. Click Import
  6. Select the P12 file
  7. Enter the password
  8. Click Next, then Finish
You may need to restart the browser. Now I could access the WSDL from all browsers.

I can't believe that I have had to call support so many times. Perhaps I was making up for not needing to call support before. I would include the documentation that I received, but it will get stale over time. The steps in this post are pretty thorough and should be sufficient. Good luck.

Wednesday, July 2, 2014

Expand IP Ranges From IP Subnet Masks

I ran into a situation where I had a third party authentication system which required a list of IP addresses. This probably isn't an issue until you start dealing w/ the environments which only list IP subnet masks. I found a great site (Network and IP address calculator) by Guido Socher that took a subnet mask and displayed a IP range (first and last address). It is not very efficient if you have more than a couple. Thankfully, the calculator was implemented in javascript and I could easily create a Node.js script to handle the automation.

An interesting bit from my code was the the function that expands the IP ranges that were calculated from Guido's calculator code. Googling did not yield any algorithms, so I took a stab at it. This really feels like it should be a recursive function, but I could not think of an elegant way to implement it.
function expandIPRange(ipform) {
    var expanded = [];
    var o1 = ipform.firstadr_1;
    while (o1 <= ipform.lastadr_1) {
        var o2 = ipform.firstadr_2;
        while ((o1 < ipform.lastadr_1 && o2 <= 255) || (o1 == ipform.lastadr_1 && o2 <= ipform.lastadr_2)) {
            var o3 = ipform.firstadr_3;
            while ((o2 < ipform.lastadr_2 && o3 <= 255) || (o2 == ipform.lastadr_2 && o3 <= ipform.lastadr_3)) {
                var o4 = ipform.firstadr_4;
                while ((o3 < ipform.lastadr_3 && o4 <= 255) || (o3 == ipform.lastadr_3 && o4 <= ipform.lastadr_4)) {
                    expanded.push(util.format("%s.%s.%s.%s", o1, o2, o3, o4));
                    o4++;
                    if (o3 <= ipform.lastadr_3 && o4 > 255) {
                        o3++;
                        o4 = 0;
                    }
                }
                o3++;
                if (o2 <= ipform.lastadr_2 && o3 > 255) {
                    o2++;
                    o3 = 0;
                }
            }
            o2++
            if (o1 <= ipform.lastadr_1 && o2 > 255) {
                o1++;
                o2 = 0;
            }
        }
        o1++;
    }

    return expanded;
}
The Node.js application can be found at my GitHub project, IPSubnetMaskToIPRange. The application takes a file of IP subnet masks and outputs 2 CSV files. A CSV file containing the IP ranges calculated by the calculator and a file containing the expanded IP addresses from the calculated ranges.

Thursday, April 24, 2014

Hide Done Column On TFS Web Portal Task Boards

Working with TFS's kanbon board layout can be difficult as the number of tasks grow and the number of tasks in your done column ever increase. If the done column is hidden, it becomes easier to scroll to find things that are still in play. Below is a script that I use to accomplish that.

This script handles custom setups where the Done column is not in it's default location.
// ==UserScript==
// @name        TFS Boards Hide Done Column
// @namespace   net.intellectualponderings.TFSBoardsHideDoneColumn
// @include     http://*:8080/tfs/*/_boards*
// @require     http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js
// @version     1
// @grant       GM_addStyle
// @grant       unsafeWindow
// ==/UserScript==

// Default easy method, doesn't work on customized layouts.
//GM_addStyle('#taskboard-table_s3, td[axis="taskboard-table_s3"] { display: none ! important; }');

var ob = (function () {
    var MutationObserver = window.MutationObserver || window.WebKitMutationObserver,
    eventListenerSupported = window.addEventListener;
    return function (obj, callback) {
        if (MutationObserver) {
            var obs = new MutationObserver(function (mutations, observer) {
                if (mutations[0].addedNodes.length || mutations[0].removedNodes.length)
                callback();
            });
            obs.observe(obj, {
                childList: true,
                subtree: true
            });
        } else if (eventListenerSupported) {
            obj.addEventListener('DOMNodeInserted', callback, false);
            obj.addEventListener('DOMNodeRemoved', callback, false);
        }
    }
}) ();
ob(document.getElementById('taskboard'), function () {
    var dth = $('.taskboard-row th:contains("Done")');
    if (dth) {
        var col = dth[0].id;
        // Hide the column
        GM_addStyle('#' + col + ', td[axis="' + col + '"] { display: none ! important; }');
        var numColumns = $('th.taskboard-cell:not(.taskboard-parent):visible').length
        if (numColumns > 0) {
            var newColumnWidth = 100 / numColumns;
            GM_addStyle('th.taskboard-cell:not(.taskboard-parent) { width: ' + newColumnWidth + '% !important; }');
        }
    }
});

Tuesday, February 11, 2014

Enable Remote Requests To IIS Express

I ran into a situation where I had an issue with a web application that was isolated to the iPad running the latest OS. Using Safari's WebInspector functionality yielded nothing fruitful, so I looked to ways that I could debug the remote requests coming to my web application in visual studio.

I found a SharpProxy and IISExpressProxy, both ran, but neither worked. I stumbled upon a Hanselman blog post titled Working with SSL at Development Time is easier with IISExpress. The title wasn't very useful to my situation, however lack of other options pushed me on to reading further.

About a third of the way down there is a section called "Getting IIS Express to serve externally...". This peeked my interest and ended up being the key to what I was attempting to achieve.

The solution

Initially this did not work. I was able to run all of the commands, however I ended up having to restart Visual Studio to get the config to update.
The blog post has a bit of out dated information regarding the firewall command. Below is the command that worked for me. Run the following commands from the Developer Console in Administrator mode. Obviously you'll have to change the ComputerName and the port to your situation.
netsh http add urlacl url=http://ComputerName:58144/ user=everyone

netsh advfirewall firewall add rule name="IISExpressWeb" dir=in action=allow program="IISExpressWeb" enable=yes

"c:\Program Files (x86)\IIS Express\appcmd.exe" set site /site.name:Web1 /+bindings.[protocol='http',bindingInformation='*:58144:ComputerName']

"c:\Program Files (x86)\IIS Express\appcmd.exe" set site /site.name:Web1 /+bindings.[protocol='http',bindingInformation='*:58144:ComputerName.domain']
If the firewall line doesn't work, you may need to change the program="IISExpressWeb" to program="c:\Program Files (x86)\IIS Express\iisexpress.exe".

If you have Visual Studio open, restart it and make sure all instances of IIS Express are closed. If you don't want to run the application from Visual Studio, you can start IIS Express from the command line with (change Web1 to whatever the site name is for your web application):
"c:\Program Files (x86)\IIS Express\iisexpress.exe" /site:Web1
You should be able to access your website from your remote web browser.

Friday, January 31, 2014

Duplicate TFS Query in Visual Studio's Team Explorer

Just a quick post on how to duplicate TFS Queries. In my environment, I don't have permission to add columns to the Shared Queries, but I can edit my own queries. Instead of creating my own version of the Product Back Log from scratch, I wanted to duplicate the query as my own. It turns out that it is as easy as copying the query and pasting the query to the "My Queries" folder.


If you paste to the same directory or there is a name collision, you will be prompted to enter a new name.


Incredibly easy, but not obvious. I am surprised how many things I can do from the Team Explorer panel. It makes life much easier.