Archive for 2007

PythonCE-2.5-20071004 Released

Friday, October 5th, 2007

Yesterday I uploaded to the PythonCE SourceForge project downloads page a new PythonCE release.

This one is slightly better than my previous release, since it has been built from a known code base, which allows it to be rebuilt and expanded upon in the future.

If you are using a Pocket PC device you should also see that you have a new context sensitive popup menu on the main editor window. This provides cut/copy/paste options which you may find handy while developing short programs directly on the device.

This release also includes an optional patch which provides initial support for building with Visual Studio 2005 instead of Embedded Visual C v4. This patch needs refinement before it can be included in the main release, since it has essentially been hacked into the existing msevc.py file and lacks auto-detection of the location of your Visual Studio 2005 and Device SDK installations etc (what has happened to the batch files that used to be present in EVC???).

The main aim of this release is to start to increase the device compatibility of PythonCE. If anyone downloads this release I would appreciate feedback on whether it works on your device type or not. It should work on any Windows Mobile Pocket PC or Smartphone device running Windows Mobile 2003 or later.

Custom soft keys for Notifications

Thursday, October 4th, 2007

Screenshot of custom notification displayed on the screen with two custom soft key menu items.Since my previous posting about Notifications within the .NET Compact Framework I have seen the question of how to provide custom menu items at the bottom of a notification come up a number of times. Today I am going to provide you with one solution to this challenge.

The short answer is that the Microsoft.WindowsCE.Forms.Notification class provided by the .NET Compact Framework does not expose the required functionality. Although the native notification API allows custom menus to be associated with a notification, for some reason this hasn’t be exposed to managed developers. With a little bit of work however (i.e. some PInvoking) you can produce your own managed wrapper around the Notification APIs to expose the required functionality.

I have done this and produced a class called NotificationWithSoftKeys. You can download a copy of this class, along with a sample application which demonstrates it’s various features below.

[Download notificationsoftkeys.zip 32KB]

The rest of this blog entry will demonstrate how you can utilise the class within your own applications. It is designed to be a stand in replacement for the existing class, but adds some additional properties and events.

Adding Spinners
Screenshot of custom notification showing the use of spinnersSpinners are the first additional functionality the NotificationWithSoftKeys control exposes. A notification spinner is a little bit of text on the right hand side of a notification which sits in-between a pair of < and > buttons. The user can tap on these buttons to “spin” through a series of related notifications.

Here is a brief example of how to enable this feature within your notification.

private NotificationWithSoftKeys notification;
 
private void btnCreateNotification_Click(object sender, EventArgs e)
{
  notification = new NotificationWithSoftKeys();
  notification.Icon = Properties.Resources.Icon;
  notification.Caption = "This is my notification";
  notification.Text = "Spinner Test\t1 of 5";
  notification.Spinners = true;
  notification.SpinnerClicked +=
      new SpinnerClickEventHandler(notification_SpinnerClicked);
  notification.Visible = true;
}

The main thing you need to do is set the Spinners property to True in order to enable the Spinner functionality. After this any text within the Caption property after the first tab (\t) character will be displayed on the right hand side of the notification’s caption.

When the user presses the < or > buttons the SpinnerClicked event will fire. Within your event handler you should update your Notification’s Caption and Text properties to give the illusion that another notification has been displayed.

Display Current Time on Caption
Screenshot of custom notification showing the use of the TitleTimer propertyDisplaying the current system time on the caption is very straight forward. All you need to do is set the TitleTime property to true.

Custom Buttons
This is the feature which will probably generate the most interest. The standard notification has a “Hide” button on the left soft key. Some built in applications however display notifications with alternative softkeys and perform different actions depending upon which softkey was selected. Using the NotificationWithSoftKey class we can achieve the same effect as demonstrated with the following example.

private NotificationWithSoftKeys notification;
 
private void btnCreateNotification_Click(object sender, EventArgs e)
{
  notification = new NotificationWithSoftKeys();
  notification.Icon = Properties.Resources.Icon;
  notification.Caption = "This is my notification";
  notification.Text = "A soft key test";
  notification.LeftSoftKey =
      new NotificationSoftKey(SoftKeyType.Dismiss, "Close");
  notification.RightSoftKey =
      new NotificationSoftKey(SoftKeyType.StayOpen, "View");
  notification.RightSoftKeyClick +=
      new EventHandler(notification_rightSoftKeyClick);
  }
 
  notification.Visible = true;
}

This example will display a notification with two soft keys. The left soft key will be labeled “Close” and will remove the notification, while the right soft key will be labeled “View” and trigger the RightSoftKeyClick event when selected.

Within this event handler you could open up a second form, delete something from a database or perform any other task you would like to in response to the user pressing the soft key.

There are a number of soft key types to choose from as shown below:

Type Description
Dismiss Remove the notification when the soft key is pressed
Hide Hide the notification when the softkey is pressed (but do not dismiss)
StayOpen Do not dismiss or hide the notification when the softkey is pressed.
Submit Submit the HTML form in the notification instead of calling the click event handler
Disabled The softkey is disabled (grayed out)

Backwards Compatibility
The additional features discussed today are only available on devices running Windows Mobile 5.0 or higher. If your application is targeting multiple device types, and you need to run on Pocket PC 2003 devices the new functionality will not work.

The NotificationWithSoftKeys class utilises techniques outlined in my blog posting on device and platform detection to fail gracefully on older devices. When the class detects an older device type, it will simply ignore all the soft key related properties and revert back to the old notification style.

Sometimes you may want to check if this fallback has occurred. For instance you may want to utilise the soft key buttons for user input. In this case you would need to use an alternative approach (such as HTML based buttons) when running on Pocket PC 2003 or older devices which don’t have softkeys. You can do this by checking the PlatformSupportsCustomSoftKeyButtons property and altering the other properties of the NotificationWithSoftKeys class as required.

Summary
This is still only scratching the surface of what is possible with the native notification APIs. There are a number of notification features which are not exposed by this wrapper. For example there are the following flags which could easily be exposed as properties.

  • SHNF_DISPLAYON – the display is forced to turn on for the notification
  • SHNF_SILENT – the notification is forced to be silent and not vibrate, regardless of system settings

Please feel free to download the sample project and have a play around with it. I would be keen to hear from anyone who attempts to use it or modifies it to expose additional functionality etc.

Will the following C# compile?

Wednesday, October 3rd, 2007

While reviewing some code earlier his week, I came across a C# function which when reduced to its core essentials boiled down to the following sample:

public void SomeMethod()
{
     http://www.christec.co.nz  

     MessageBox.Show("hello world");
}

Off the top of your head do you think a C# compiler would happily accept this source code, or would you expect a syntax error?

The answer is that the above example function is completely valid C# source code. It is easier to see why once syntax highlighting has been applied to it.

The // characters after the http: prefix cause the rest of the URL to be interpreted as a comment, and the bit which is left (http:) is in the correct syntax to be interpreted as a label for use with the goto statement.

I had accidentally pasted a URL into my source code and checked it into our source control system. I didn’t notice for a few weeks since the code compiled and executed perfectly…

An amusing (but somewhat useless) application of this fact can be found in the following program

using System;
 
public class MyDumbApplication
{
  public static void Main() 
  { 
     http://www.christec.co.nz  
 
     Console.WriteLine("Hello World!");  
 
     goto http; 
  }
}

I wonder if this fact could be used to advantage in a International Obfuscated C Code Contest style C# program?

Add Cut/Copy/Paste functionality to a Textbox

Tuesday, October 2nd, 2007

Earlier I discussed how you could manually get the software keyboard (SIP) to display whenever a TextBox control gained focus. There was potentially a lot of event handlers to write, two for every control on a form. Today I will show you an alternative approach that utilises less code but also has some additional benefits.

A common thing I do while testing new Windows Mobile applications, is to tap-and-hold on a text field. Very well behaved applications should popup a context sensitive menu containing cut/copy/paste style options for the current control. Surprisingly, very few applications actually pass this test, even though it is a feature that has been built into the operating system for a while.

Within Windows CE the SIPPREF control can be used to automatically implement default input panel behavior for a dialog. It provides the following features:

  • The Input Panel is automatically shown/hidden as controls gain and loose focus.
  • Edit controls have an automatic context menu with Cut, Copy, Paste type options.
  • The SIP state is remembered if the user switches to another application and later returns to this form.

In my mind this has three advantages over the process I previously discussed.

  1. You add the SIPPREF control once and it automatically hooks up event handlers for each of your controls. With the manual event handler approach it’s easy to add a new control and forget to hook up the events required to handle the SIP.
  2. You get free localisation. Although you could create a custom context menu for cut/copy/paste, you would need to localise the text into multiple languages yourself (if you are concerned with true internalisation that is) and it’s another thing thing to hook up for each control.
  3. You get standardised behavior. By using functionality provided by the operating system you are ensuring that your application has a natural and expected behavior to it. If the platform ever changes the conventions of SIP usage, your application will automatically be updated.

For the rest of this blog entry I will discuss how to go about utilising the SIPPREF control within your application. I have split my discussion into two sections. The first section will be of interest to native developers developing in C or C++, while the second section is intended for .NET Compact Framework developers.

Each section contains a small example application which demonstrates the behaviour of the SIPPREF control within the respective environment. When you run the sample applications you will notice that the SIP does not popup up when you click within a text box. And a tap-and-hold operation yields nothing. This behaviour changes when a SIPPREF control is added to the dialog, which can be achieved by clicking the sole button.

Native Developers

[Download sipprefcontrolnativeexample.zip 16KB]

In order to use the SIPPREF control we must first request the operating system to register the SIPPREF window class. We do this by calling the SHInitExtraControls function. This step only needs to be done once, so is typically done during your application’s start up code. It is very easy to call, as the following example demonstrates:

#include <aygshell.h>
 
SHInitExtraControls();

Since SHInitExtraControls lives within aygshell.dll, we also need to modify our project settings to link with aygshell.lib, otherwise the linker will complain that it can not find the SHInitExtraControls function.

Once we have registered the SIPPREF window class, we simply create a SIPPREF control as a child of our dialog. When the SIPPREF control is created it will enumerate all sibling controls and subclass them in order to provide the default SIP handling behaviour. The SIPPREF control must be the last control added to your dialog, as any controls added after the SIPPREF control will not be present when the SIPPREF control enumerates its siblings, and hence will not be subclassed to provide the proper SIP handling.

If dynamically creating the SIPPREF control, a good place to do this is within the WM_CREATE or WM_INITDIALOG message handler, as the following code sample demonstrates:

case WM_INITDIALOG:
  // Create a SIPPREF control to handle the SIP. This
  // assumes 'hDlg' is the HWND of the dialog.
  CreateWindow(WC_SIPPREF, L"", WS_CHILD,
       0,  0, 0, 0, hDlg, NULL, NULL, NULL);

As an alternative to dynamically creating the SIPPREF control, we can place the control within our dialog resource by adding the following control definition to the end of a dialog within the project’s *.rc file.

CONTROL  "",-1,WC_SIPPREF, NOT WS_VISIBLE,-10,-10,5,5

Depending upon your developer environment you may even be able to do this entirely from within the Resource Editor GUI. For example within Visual Studio 2005 you could drag the “State of Input Panel Control” from the Toolbox onto your form to cause a SIPPREF control to be added.

sip-toolbox.png

.NET Compact Framework Developers

[Download sipprefcontrolexample.zip 16KB]

The process of using the SIPPREF control for a .NET Compact Framework application is fairly similar to that of a Native application. Since the .NET Compact Framework does not natively support the use of dialog templates, we must use the CreateWindow approach to create a SIPPREF control dynamically at runtime.

The first step is to declare a number of PInvoke method declarations for the various operating system APIs we need to call.

using System.Runtime.InteropServices;
 
[DllImport("aygshell.dll")]
private static extern int SHInitExtraControls();
 
[DllImport("coredll.dll")]
private static extern IntPtr CreateWindowEx(
  uint dwExStyle,
  string lpClassName,
  string lpWindowName,
  uint dwStyle,
  int x,
  int y,
  int nWidth,
  int nHeight,
  IntPtr hWndParent,
  IntPtr hMenu,
  IntPtr hInstance,
  IntPtr lpParam);
 
  private static readonly string WC_SIPPREF = "SIPPREF";
  private static readonly uint WS_CHILD = 0x40000000;

One interesting fact is that we PInvoke a function called CreateWindowEx, while the native example above called CreateWindow. If you dig deeper into the Window Header files you will notice that CreateWindow is actually a macro which expands into a call to CreateWindowEx. At the operating system level the CreateWindow function doesn’t actually exist.

With this boiler plate code out of the way, the solution is very similar to the native one…

protected override void OnLoad()
{
     // Initialise the extra controls library
     SHInitExtraControls();
 
     // Create our SIPPREF control which will enumerate all existing
     // controls created by the InitializeControl() call.
     IntPtr hWnd = CreateWindowEx(0, WC_SIPPREF, "", WS_CHILD,
          0, 0, 0, 0, this.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
}

In the above example we simply create the SIPPREF control within the OnLoad method of our form. Within the downloadable sample project I have wrapped up this code into a static method called SIPPref.Enable(Form f) to enable to it easily be reused between forms.

Hopefully today I have shown you that having knowledge of the underlying operating system is still a useful skill to have for .NET Compact Framework developers. Knowing the features provided by the operating system can allow you to add some neat functionality to your applications with little additional effort on your behave.

Creating non full screen forms and custom MessageBoxes

Monday, October 1st, 2007

A screenshot of a custom message box form which is not full screen.Every now and then someone asks how to create a .NET Compact Framework form which does not cover the fullscreen. Here is my solution to this problem.

This solution is built upon details discussed by other bloggers, including:

The trick is to set the form’s FormBorderStyle property to None. This disables the .NET Compact Framework’s built in functionality which forces a form to become fullscreen (on Windows Mobile devices), but also has the side effect of removing the border around the edge of the form. We can add the border (and a caption) back via the use of some PInvoke calls into the native operating system.

Points of interest

I decided to create my solution as two classes which are designed to be reusable within your own applications. One class enables the non full screen functionality, while the other provides an implementation for a common reason why non full screen forms are requested. These classes are as follows:

  1. NonFullScreenForm – a base class which you can use instead of Form. This base class allows you to have a form which does not cover the entire screen, and will optionally automatically center the form in the middle of the screen.
  2. MessageBoxForm – this is a subclass of NonFullScreenForm which provides a static method called Show. This method behaves in a similiar way to the standard MessageBox.Show API, with a few extensions such as the ability to use custom buttons.

Example Application

[Download nonfullscreendemo.ZIP 32Kb]

The example application available for download demonstrates the use of the NonFullScreenForm and MessageBoxForm classes via a series of buttons.

The first two buttons compare the behaviour of the standard MessageBox.Show API against our custom MessageBoxForm implementation for a number of message box styles. You will notice that the look and feel of our custom message boxes are similiar, but often have a slightly different layout.

The button labeled “Auto Centered Form” demonstrates the effect of setting NonFullScreenForm’s CenterFormOnScreen property to True. Whenever you tap the “Expand” or “Collapse” buttons the form changes size, and the NonFullScreenForm base class automatically changes the location of the form to ensure it is centered at all times. It will even re-center the form when the device switches between landscape and portrait modes.

How to use the NonFullScreenForm class

To use the NonFullScreenForm class within your own applications follow these steps:

  1. Add NonFullScreenForm.cs to your project.
  2. Set the FormBorderStyle property of your form to None.
  3. Set the Size and Location properties of your form to the desired size and position on the screen.
  4. View your form’s source code and change the form to derive from NonFullScreenForm instead of Form.
  5. If you want your form automatically centered on the screen set the CenterFormOnScreen property to true.
  6. If you don’t want an [OK] button in the caption, set the ControlBox property to false.

There are a number of areas where these custom controls could be improved. MessageBoxForm’s compatability could be improved. It does not support displaying an icon within the MessageBox at present for instance. There is also a slight flickering on the navbar (the bar at the top of the device which contains the start menu etc) when a non full screen dialog is created which would be nice to eventually remove. This is caused by the .NET Compact Framework dispaying the form before we have had a chance to change it’s window style to include a caption.

These controls were quickly put together to demonstrate what could be possible with a little bit of work. If anyone is interested in collaborating to improve these controls, I would be keen to hear from them.

PythonCE for Windows Mobile Smartphone

Monday, October 1st, 2007

Screenshot of PythonCE About dialog box running on a Windows Mobile 6 powered SmartphonePythonCE is a port of the Python programming language to the Windows Mobile Pocket PC platform. I became interested in this project a couple of months ago and released a couple of patches to allow the application to run on Smartphone devices (such as Motorola Qs or Samsung Blackjacks).

Over the last month or two I have had numerious requests for a PythonCE CAB file which runs on a Windows Mobile smartphone.

The patches I submitted have not yet been included in the builds available via the main downloads page for PythonCE, so today I decided to host my unofficial PythonCE build on my blog. I hope this helps people who are looking at getting a working Python development environment up and running for their smartphone.

The cab file should run on any Windows Mobile 5.0 or above Pocket PC or Smartphone device.

[Download pythoncesp2005_armcab.zip 3.09 MB]

Edited: October 5th 2007 – An official build has been released by the PythonCE project. You can obtain it from their downloads page.

Please be aware that this is just a “random” build I had on my harddrive. I lost interest in PythonCE after finding development had been quiet for a while. Since I’ve had a number of requests for Smartphone compatible builds recently, I decided to take a look at what I had, and I found this CAB file. Taking a look at it, it seems this build has all the patches I submitted upstream, plus a couple I didn’t get around to submitting. It doesn’t however have some of the features I was working on just before I lost interest. It’s a case of “user beware”, I take no responsibility with respect to the usability of this particular build…

I would be interested to hear feedback from anyone using PythonCE, especially anyone interested in further development. There seems to be a keen community of PythonCE users, but this doesn’t seem to be matched by people keen on developing PythonCE further. Is this just a case of the existing builds satisfying most people’s needs?

Creating a Tab Control for the Smartphone

Saturday, September 29th, 2007

Screenshot of sample application demonstrating the tab control I developed for Windows Mobile Smartphone devicesThe .NET Compact Framework does not support a Tab Control when targeting a Windows Mobile Smartphone device. Today I present a sample custom control designed to provide a suitable replacement for System.Windows.Forms.TabControl when you are building a Smartphone application.

I am not sure using such a control is good from a usability perspective, but it has been asked for enough times within the developer community, that this time I decided to provide a working solution. This was especially true once I determined the custom tab control would also provide some benefits for Pocket PC developers.

My development background was originally in the native world writing applications in C and C++. As such I know the raw Win32 APIs provided by the Windows Mobile operating system pretty well. The smartphone OS actually provides the standard Commctrl.h Tab Control, however the .NET Compact Framework has decided not to expose this functionality. So this custom control simply uses quite a lot of Platform Invoke calls to wrap up the native tab control provided by the operating system in a .NET Compact Framework friendly manor.

Benefits of this control

  • Works on both Smartphone and Pocket PC devices.
  • Allows tabs to have an optional bitmap displayed beside the text.
  • Exposes a SelectedIndexChanging event which allows the programmer to cancel a tab change if the application logic desires.

Sample Application

A sample application which runs on both Pocket PC and Smartphone devices is available for download. This download also contains the file SmartphoneTabControl.cs which contains the source code for the custom control itself.

[Download smartphonetabcontrol.zip 48KB]

The sample application has three tabs and demonstrates how you can navigate the tabs via the left and right arrow keys. Once you have selected the third tab, you can use the up and down arrow keys to select the various textbox controls, and return to the tab control to select another tab.

Another neat feature demonstrated in the sample application is a way to have an animated icon displayed on a tab. This may be useful for a tabbed interface where a tab is displaying the results of an asynchronous or lengthy operation (such as a file download). The tab’s icon could be animated while the operation is still in progress, allowing the user to determine the state of the operation with a quick glance.

How to use the control

  1. Add SmartphoneTabControl.cs to your solution.
  2. Compile your project (needed to get the control into your toolbox).
  3. Drag a SmartphoneTabControl from your toolbox onto your form.
  4. Dock the tab control to the bottom and make it 26 pixels high.
  5. Add one or more Panel controls to your form (one for each tab page) and set the DockStyle property of each one to ‘Fill’.
  6. Populate the panels with the controls you want visible on each tab.
  7. Within your form’s constructor call the SmartphoneTabControl’s AddTab() method to create each tab, passing in the name of the tab, an optional image, and the panel which contains it’s controls.

There are a couple of outstanding bugs/missing features with this control. The biggest being the lack of design time support and device resolution independance. This control was quickly put together to demonstrate what could be possible with a little bit of work. If anyone is interested in collaborating to improve this control, I would be keen to hear from them.

Free games for your Windows Mobile PDA or Smartphone

Wednesday, September 26th, 2007

It has been a long time since my last blog posting. I am trying to get back into the habit of regular posting, so I thought I would start off again with a beta release of a project that I have been working upon recently.

Introduction

As I mentioned previously Simon Tatham’s Portable Puzzle Collection is a high addictive collection of 27 logic puzzles that can be used on Windows, Mac OS , Unix and PalmOS powered devices. I have picked up the existing Windows Mobile PDA support found within the SVN repository and improved the port, making it more functional and compatible with a wider range of Windows Mobile devices.

I am at the stage where I am getting ready to make an initial release. As such I have decided to make a beta release, in the hope that it generates some feedback which will allow me to further improve device compatibility and iron out any kinks.

Installation Process

[Download puzzles.zip - 741KB]

Download the attached ZIP file and extract the CAB file called Puzzles.ARMV4.CAB located within it. Once you transfer this CAB file to your device, you can install the games as you would with any other CAB file.

Once installed, you should see a Puzzles folder underneath the Games section of your device’s Start Menu which contains 27 new games to occupy your time with.

The single CAB file should be usable on any Windows Mobile 2003 or above device – Smartphone or Pocket PC, QVGA or VGA, Landscape, Portrait or Square screen. It would probably even work on the OFone if one actually existed! The user interface dynamically adapts at run time to its current situation. For instance it uses a slightly different menu layout on Pocket PC 2003 devices, and hides a toolbar if it finds itself running on a smartphone with a numeric keypad.

On the device compatibility front, if anyone has a Windows Mobile Standard (smartphone) device with a QWERTY keyboard such as a Moto Q or Samsung BlackJack which I could borrow for a short time, I would love to hear from them… especially something running WM6.

Further details

From anyone who downloads this beta release I would be interested in hearing feedback on:

  1. Any bugs and/or device compatibility issues
  2. If there is interest in a localised version (i.e. having the UI in French, German or Japanese etc)?
  3. If there is interest in support for “raw” Windows CE based devices?

As with all beta releases there are a couple of known issues which you will need to be aware of. Many will hopefully be resolved before I make a final release. These issues include:

  1. The lack of proper start menu icons.
  2. Usability issues with many of the games on non touch-screen enabled devices.
  3. Some Cosmetic issues within the custom game configuration dialog.
  4. The game “filling” has some corrupt pixels on the edges of the game board.
  5. The game “inertia” is currently unplayable via keyboard, due to not being able to move on diagonals.

My next steps

Within the next day or so I will clean up my patch to Simon Tatham’s original source code and post it to this blog along with compilation instructions (for Visual Studio 2005) etc. Then I will start producing a plan for the additional work required to make a proper v1.0 release.

In developing this port I have also came up with some ideas for future blog topics. These blog topics will be of interest to native (C and C++) developers, instead of Compact Framework developers, and will focus on topics such as SIP handling, Smartphone and Pocket PC UI differences, and cross platform development tips.

I also have a couple of blog postings about popup notification balloons to follow on from my previous entry on the subject. So hopefully I’ll get back into the swing of things over the next few days.

Code Camp Boot Camp 2007

Sunday, August 26th, 2007

New Zealand Code Camp logoAs part of the New Zealand .NET User Group I am helping organise a Code Camp event in Christchurch later this year.

The theme is “next generation, back to basics”, i.e the 2 day event will be filled with presentations looking at the upcomming releases of C#, VB.NET, .NET 3.5, ASP.NET, and SQL Server, with a focus on how to get up to speed on them quickly.

More details can be found on the NZ .NET User Group website at www.codecamp.net.nz. The exact date has not yet been determined, but will be a Saturday and Sunday during November (depends upon venue availability). In the mean time it would be helpful if people who are interested could pre-register on the website to allow us to guage the likely number of attendees.

Further details can be found on Peter’s blog.

Updated Windows Mobile 6 SDK Documentation

Tuesday, August 14th, 2007

The Windows Mobile 6 SDK documentation has recently been updated on MSDN. The new documentation includes a couple of new navigation features which hopefully will make the documentation more discoverable and easier to navigate.

I particularly liked the topic index, which seems to be the work of John Kennedy. His recent posting discusses what’s changed since the last release.

The other thing which has started in the last few months is Jim Wilson’sHow Do I?” webcast series. These appear to be worth keeping an eye on. I like the fact that each video is a bite size tip which covers one topic/problem in depth.