As you may know, I’m currently working on Mishra Reader which is intended to be a cool Google Reader for Windows.

I develop it using WPF with design and usability in mind.

I’m not a designer (at all) but following some guidance I achieved to provide a visually appealing application. So I will try to expose here some tricks to help developers producing more beautiful applications

Note: The screens here are from the beta 1 which will be available this Friday Clignement d'œil.

Colors choice

First of all, we have to keep the coherence of the application by selecting a few numbers of colors.

For Mishra Reader, I selected 4 colors:

  • Background color: this color is used to paint the background of controls
  • Foreground color: this color is used to draw text and border of unfocused controls
  • Darker foreground color: I use this color when I want to display some texts alongside a more descriptive version. The foreground color is for the main text and the darker one is for the description
  • Accent color : Like in Windows Phone, I have a special color to attract user attention

So with only 4 colors (and no more, we don’t want to draw a rainbow), we can produce something like that:

image

You have to be focused on keeping this coherence with colors and select the good one at the right place. For example, in the options screen, I designed a checkbox and use the accent color in it. Of course, the accent color must be displayed only when the checkbox is checked:

image

We can also note that help message use the darker foreground color to distinguish with the option text.

Finally, as I wanted to allow user to change the accent color, I cannot leave the application logo unchanged. So I developed a small bunch of code to apply the accent color to a grayscale logo:

image

The code used is the following:

  1. public static WriteableBitmap GetColoredLogo(OptionsManager optionsManager)
  2. {
  3.     WriteableBitmap bmp = new WriteableBitmap(new BitmapImage(new Uri("pack://application:,,,/Assets/MishraReader.png")));
  4.     int size = bmp.PixelHeight * bmp.PixelWidth * bmp.Format.BitsPerPixel / 8;
  5.     byte[] pixels = new byte[size];
  6.     Color accentColor = (Color) ColorConverter.ConvertFromString(optionsManager.AccentColorCode);
  7.  
  8.     bmp.CopyPixels(pixels, bmp.PixelWidth * bmp.Format.BitsPerPixel / 8, 0);
  9.  
  10.     for (int index = 0; index < size; index += 4)
  11.     {
  12.         byte b = pixels[index];
  13.         byte g = pixels[index + 1];
  14.         byte r = pixels[index + 2];
  15.  
  16.         if (r > 250 && g > 250 && b > 250)
  17.             continue;
  18.  
  19.         pixels[index + 2] = (byte)Range(0, 255, accentColor.R * Range(0, 1, r / 255.0f + 0.4f));
  20.         pixels[index + 1] = (byte)Range(0, 255, accentColor.G * Range(0, 1, b / 255.0f + 0.4f));
  21.         pixels[index] = (byte)Range(0, 255, accentColor.B * Range(0, 1, b / 255.0f + 0.4f));
  22.     }
  23.  
  24.     bmp.WritePixels(new Int32Rect(0, 0, bmp.PixelWidth, bmp.PixelHeight), pixels, bmp.PixelWidth * bmp.Format.BitsPerPixel / 8, 0);
  25.  
  26.     return bmp;
  27. }

Application in motion

The second point to take into account is the animations. You should never change something on your UI without an animation. And therefore you should never do something that can stuck the rendering and the animations.

In Mishra, I took a lot of time checking that my code is running in a background thread without interfering too much with the main thread. To do so, I always use the Dispatcher with a really low priority in order to not disturb animations:

  1. Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new Action(() =>

I also spent a lot of time writing animations for each state that I had to change.

About animations, it is better to use some easing functions in order to have non linear transitions. Easing functions such as BackEase or CircleEase give an impression of performance. For example here is the code for displaying a control:

  1. <Storyboard x:Key="showSubscription">
  2.     <DoubleAnimation Duration="0:0:0.300" To="1" Storyboard.TargetName="addSubscription" Storyboard.TargetProperty="Opacity"/>
  3.     <DoubleAnimation Duration="0:0:0.300" To="0" Storyboard.TargetName="addSubscription" Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.Y)">
  4.         <DoubleAnimation.EasingFunction>
  5.             <BackEase Amplitude="0.6" EasingMode="EaseOut"/>
  6.         </DoubleAnimation.EasingFunction>
  7.     </DoubleAnimation>
  8.     <ObjectAnimationUsingKeyFrames Storyboard.TargetName="addSubscription" Storyboard.TargetProperty="Visibility">
  9.         <DiscreteObjectKeyFrame KeyTime="0">
  10.             <DiscreteObjectKeyFrame.Value>
  11.                 <Visibility>
  12.                     Visible
  13.                 </Visibility>
  14.             </DiscreteObjectKeyFrame.Value>
  15.         </DiscreteObjectKeyFrame>
  16.     </ObjectAnimationUsingKeyFrames>
  17. </Storyboard>

As you can see, I animated the opacity with a linear DoubleAnimation but the TranslateTransform.Y is animated by a beautiful BackEase function.

With the entire user interface moving smoothly with non-linear animations, the application is seen as efficient even if you are performing a lot of computations in the background.

Help your users: give them only what they need

Finally the last trick I want to share is about simplicity. Simple is beautiful! So do not produce overloaded interface with a lot of buttons on it.

You should instead present a clean user interface and make appear controls when they are required. For example, here we can see new icons appear only when the mouse is over an item:

image

Conclusion

Of course there is a lot of others points to take in account when you want to produce a good user interface. But as a developer, following these simple tricks is a good start Sourire