Category Archives: .NET

Smoothing out the rough edges

Once a software solution is mature enough to have the core functionality complete, developers can begin to focus on the use cases that are uncommon, but very annoying. We had the chance to tackle on of those cases today.

ParaPlan will allow a user to enter just a month and day when typing dates. We assume that they meant this year and we automatically fill it in. This works out really well for the first 50 weeks of the year and saves our users many keystrokes and spares many typos. The last two weeks of the year become much more challenging as clients start requesting rides for the following year. So if Betty calls today and is requesting a ride for January 14, the user will enter 1/14 in the trip date and once they tab out, it will autocomplete to — 01/14/2012. Not the ideal end result.

To fix this situation, we added a check to see if today was in December and the user was entering a date in January and both of the dates were this year. If so, we change the inputted date to the following year and provide a message at the top of the window that we did so and provide an opportunity for the user to click the message and undo the change.

Note that we automatically executed the action that 95% of the use cases would end up choosing if we had displayed the options to the user in a message prompt. We find that message prompts (especially Yes/No prompts) confuse and irritate users. Often they will just ignore the text and choose the button that they think will get them back to using the software. So we almost always automatically execute the action that is most likely, then give the user the option to undo the automatic action taken. Much cleaner and less intrusive than a Yes/No message box demanding the users attention.

Here is a screenshot of a user entering a date that is meant for next year, but will be autocompleted to this year:

Before

 

Here is the resulting screenshot of the year automatically changed and the ability for the user to rollback the change:

After

ParaPlan universal logon and transition to REST

Over the next several months, we will be rolling out several new features that will significantly change how users will be able to interact with ParaPlan. This post is targeted at IT managers for agencies that use ParaPlan. In this post we will talk about universal logons and how we are moving from transferring data via SQL over port 1433 to using HTTPS and port 443.

Note that all these changes will be optional. We have clients that run ParaPlan on a single computer and host the database on that computer and they do not even have internet access. Our software is a perfect fit for really small agencies and we will continue to support local network only installations. These changes are being made to pave the path for the future. They allow us to deliver ParaPlan to the iPad and iPhone, they allow agencies to operate in disaster situations and they allow access to ParaPlan from outside of the office.

Universal Logons

We have always used local username and passwords to authenticate users to their local SQL database. This has never been an issue because all our clients have had separate databases. With the introduction of our iOS apps, we needed a single gateway to authenticate ParaPlan users, so we moved to an email-based username system.

A user will submit their email address and password. We hash that password and authenticate it against our global ParaPlan user database. If it authenticates, we hand back a single session token and a key. The token is a GUID that is active only for that user’s session and can be revoked server-side. The key will match an entry in a dictionary that is hardcoded and compiled on the client and on the server.

When the user makes data requests, we use that token and key to determine the local database that user’s requests will be run against.

REST over HTTPS

The data requests will be made over SSL on port 443 to a REST server. A sample request would look like this:

https://rest.engraph.com/Trips/Perform/{tripId}/{actionType}?Token={token}&Database={databaseid}&DriverID={driverid}&Lat={lat}&Lng={lng}&Timestamp={timestamp}&Odometer={odometer}

Since the request is made over HTTPS, we don’t have to make special firewall rules. And since the request is submitted securely over SSL (the same standard used by financial websites), we know the connection between the client and server is safe.

Our REST server looks up the database credentials and construct the data request to the SQL server, then returns the results in JSON format. For our existing ParaPlan Cloud clients, this will not create any delay due to network data transfer as the REST server sits in the same network boundary as our SQL server. For self-hosted clients, there will be a short delay due to the data transfer between our REST server and the client’s database. Self-hosted clients will also need a firewall rule to allow external requests on port 1433 from the IP of our REST server.

Points of action

As an IT manager, there are several steps that can be taken to help ease this transition:

  1. Ensure that all ParaPlan users have an email address that they use frequently.
  2. Ensure that all workstations and firewalls allow HTTPS traffic on 443 to *.engraph.com.

For self-hosted clients, please check the following items:

  1. Make sure that the SQL ‘sa’ account is disabled and there is only one account with dbo access to the ParaPlan database. Here is how to disable sa.
  2. Check existing network policies that will hinder external traffic on 1433. Keep in mind that we will be able to provide a very narrow scope of IPs that will be communicating across 1433.

This is a very exciting time in technology. We are moving into a post-PC world and soon a post-local network world. We want our users to be able to use ParaPlan from any computer or internet-connected device. We also want to make sure our data is secure and kept safe from prying eyes and would-be hackers. By using industry standard technologies, we are able to deliver secure solutions that can grow and move as our industry changes.

Podcast with AJI about iOS development coming from a .NET background

TheAJIReportLogo

I talked with Jeff and John from AJI Software the other day about developing for the iOS platform. We chatted about learning Xcode and Objective-C, provisioning devices and the app publishing process. We all have a .NET background and made lots of comparisons between the two platforms/ecosystems/fanbois. They even let me throw in a plug for Christian Radio Locator.

Jeff was my first contact with the Kansas City .NET community. It was probably about 10 years ago. He pushed me to talk more (and rescued me from my first talk that bombed) and blog more. One time a group of us took a 16 hour car trip to South Carolina for a code camp and live podcasted the whole thing. Good times.

Listen to the show

Click here to subscribe to more AJI Reports in the future.

Extension Method to return dates in range

While setting up some unit tests, I needed to get a list of all the dates between two dates. The best way to implement helper functions like these are to put them in an Extension Method.

public static List<DateTime> DistinctDates(this DateTime dt, DateTime endDate)
{
    var start = dt;
    var end = endDate;
    if (start.Date == end.Date) return new List<DateTime>() { start };
    if (start > end)
    {
        start = endDate;
        end = dt;
    }

    var rv = new List<DateTime>();
    rv.Add(start);
    var diff = (end - start).TotalDays;
    for (int i = 1; i < diff; i++)
    {
        rv.Add(start.AddDays(i));
    }
    rv.Add(end);
    return rv;
}

Display item count in WPF ListBox and reorder using ButtonSpinner

I ran into a situation the other day where I needed to be able to display Trips to a user in the order that they were going to be executed and have them be able to move them up and down the list. I liked how Netflix tackled the issue, but wanted to give the user a little more control.

As it were, I could not find a decent solution to the problem. Google and StackOverflow mostly turned up hacks that were sure to fall apart. After much trial and error, I was able to use an IMultiValueConverter that accepted a ListBox and an item in the ListBox. From there, I could do an IndexOf and return that value back to a TextBlock for display.

The second issue of reordering I solved using the ButtonSpinner in the WPF Extended Toolkit. I had to trick it because I wanted the “down” spinner to move the item to the next higher index and the “up” spin to actually move the item down the list (from an index’s point of view). This functionality requires that the binding list is an ObservableCollection that exposes a Move event and also fires all the INotifyPropertyChanged events that WPF uses for UI refreshing.

Note that the ButtonSpinner is a content control and looks a little ugly when used by itself. The trick is to set the Padding to zero.

So now we have a ListBox that displays the current order of the items and allows the user to move the items up and down the list (which changes the order label) using the ButtonSpinners.

WPF ListBox

In my production code, I actually hide the ButtonSpinners until the user mouses over them, but I didn’t want to clutter the code. I’ll save that for a later post.

The key parts of the code are listed below. The entire project can be downloaded here. Remember to unblock after downloading.

The Converter that takes a ListBox and an item in the ListBox:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Data;
using System.Windows.Controls;

namespace ReorderingListBox
{
    public class ListBoxIndexConverter : IMultiValueConverter
    {
        #region IMultiValueConverter Members

        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            var item = values[0];

            if (item == null)
            {
                return null;
            }

            var lb = values[1] as ListBox;
            if (lb == null)
            {
                return null;
            }

            //make it 1 based
            var rv = lb.Items.IndexOf(item) + 1;

            //very important because control
            //we are binding to is expecting a string
            return rv.ToString();
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}

The XAML with the ListBox and ButtonSpinner:

<Window x:Class="ReorderingListBox.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:converters="clr-namespace:ReorderingListBox"
        xmlns:wpf="http://schemas.xceed.com/wpf/xaml/toolkit"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.Resources>
            <!--reference to our multi converter-->
            <converters:ListBoxIndexConverter x:Key="listBoxIndexConverter" />
            <!--this defines how each item in the listbox will layout-->
            <DataTemplate x:Key="placesTemplate">
                <StackPanel Orientation="Horizontal">
                    <!--shows the current order in the listbox-->
                    <TextBlock Width="15" Name="textBlockOrder">
                        <TextBlock.Text>
                            <!--this is a multibinding, we are into the converter-->
                            <!--the current object that we are bound to (a place)-->
                            <!--and the listbox that it lives in-->
                            <MultiBinding Converter="{StaticResource listBoxIndexConverter}">
                                <Binding />
                                <Binding ElementName="listBox" />
                            </MultiBinding>
                        </TextBlock.Text>
                    </TextBlock>
                    <!--control from http://wpftoolkit.codeplex.com/-->
                    <!--note the padding has to be explicitly set-->
                    <!--otherwise it looks horrible more info about that-->
                    <!--http://timhibbard.com/blog/2012/05/08/default-padding-issue-with-buttonspinner/-->
                    <wpf:ButtonSpinner Padding="0"
                                       Margin="3"
                                       Spin="spinner_Spin" />
                    <TextBlock Text="{Binding}" />
                </StackPanel>
            </DataTemplate>
        </Grid.Resources>
        <ListBox Name="listBox"
                 ItemTemplate="{StaticResource placesTemplate}" />

    </Grid>
</Window>

The code behind that constructs the list that binds to the ListBox and the event that listens to the ButtonSpinner.Spin event:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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 System.Collections.ObjectModel;
using Xceed.Wpf.Toolkit;

namespace ReorderingListBox
{
    ///
<summary> /// Interaction logic for MainWindow.xaml
 /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            //construct our backing list to bind to
            var placesIWantToLive = new ObservableCollection
            {
                "Minneapolis",
                "San Diego",
                "Charleston",
                "Philadelphia"
            };

            this.listBox.ItemsSource = placesIWantToLive;
        }

        private void spinner_Spin(object sender, SpinEventArgs e)
        {
            ButtonSpinner spinner = sender as ButtonSpinner;
            //this will find the textblock in the same listbox item
            TextBlock textOrder = spinner.FindName("textBlockOrder") as TextBlock;
            ObservableCollection places = this.listBox.ItemsSource as ObservableCollection;
            //back to zero based - remember we store the current index in this textblock
            int current = int.Parse(textOrder.Text) - 1;
            int destination = 0;
            switch (e.Direction)
            {
                case SpinDirection.Decrease:
                    //because we want it to go "down" the list
                    destination = current + 1;
                    break;
                case SpinDirection.Increase:
                    //because we want it to go "up" the list
                    destination = current - 1;
                    break;
                default:
                    //we'll never hit here, but else statements are ugly
                    //and not as obvious as switch statements
                    break;
            }
            if (destination < 0)             
            {                 
                 //can't more the first item any higher so exit                 
                 return;             
            }             
            if (destination > places.Count + 1)
            {
                //can't move it any more down so exit
                return;
            }

            //this is where the magic happens
            places.Move(current, destination);
            //this refreshes the textblocks holding the index
            this.listBox.Items.Refresh();
            //highlight the item that was moved so it is obvious to the user
            this.listBox.SelectedIndex = destination;
        }
    }
}

Default Padding issue with ButtonSpinner

The Extended WPF Toolkit plugs a large hole in the WPF framework and we use it extensively at EnGraph. Every now and then, you find a little odd thing. I ran into an issue this morning where the ButtonSpinner looked very odd if you didn’t specify any child content to be inside the spinner.

It’s not really a bug because the control was designed to have content, but I needed it to be by itself.

<wpf:ButtonSpinner />

It produced that ugly border to the left of the control where the content would be. Digging through the properties, I found that the default value for Padding was set to 2. So I changed that to be 0 and now we get the excepted result:

<wpf:ButtonSpinner Padding="0" />


Perfect.

Using ItemContainerStyle to manage individual items of a bound CheckListBox

This post uses the CheckListBox in the excellent Extended WPF Toolkit library (originally created by Brian Lagunas)

Our software supports trips imported by LogistiCare. The user selects a CSV file, we parse it, then they select from a list which trips they wish to import. Sometimes the data in the download just isn’t right and I needed a way to prevent the user from importing bad data into the system.

I could have done this validation in the manager that parses the CSV, but this could have lead to confusion. What if the original download listed 300 trips and we only displayed 280 available to import?

It could also have been done on the Data Access Layer side, but if we displayed 300 trips to import, but only 280 showed up in Scheduling Canvas, the user would also be confused.

So it seemed that the UI would be the logical place to do this. That way the user is aware of what is going on. Now, we don’t actually want the logic to be done in the UI layer, so we’ll do the heavy lifting in the object and just expose a public readonly property called LogisticareIDIsValid that returns a boolean.

We are able to hook into the ItemContainerStyle of the CheckListBox to be able to set properties on each individual item listed. In this case, we want IsEnabled to be bound to LogisticareIDIsValid.

With ItemContainerStyle, you can also cool things like subscribe to events. You can see we subscribed to the MouseDoubleClick event.

This code assumes you know how to declare a namespace in your XAML.

Here is the XAML:

<wpf:CheckListBox>
    <wpf:CheckListBox.ItemContainerStyle>
        <Style TargetType="{x:Type wpf:CheckListBoxItem}">
            <Setter Property="IsEnabled"
                    Value="{Binding Path=LogisticareIDIsValid}" />
            <EventSetter Event="MouseDoubleClick"
                    Handler="listResultsDoubleClick" />
        </Style>
    </wpf:CheckListBox.ItemContainerStyle>
</wpf:CheckListBox>

I had originally accomplished this using a method and a converter. I didn’t really see how much easier it was to use a property until I was getting ready to publish this post and the mistake was glaring. Funny how having to explain your thought process exaggerates the error of your ways.