TouchUI - Resolution/Orientation Aware (part 13)

by Chris 1. January 2010 10:06

As I have shown in several places in the previous parts of this series, the painting of things is always related to the bound of the current item (dialog, control, etc). This approach is essential to handle the quite extensive number of resolutions on WinPhone. That's especially true if you want to support all phones since WinMo 5, and even the non-touch devices. Another important reason is the fact that many devices with non-square screens allow the screen to be rotated between portrait and landscape mode.

The magical event that tells our app about the resolution and any change in orientation is the Resize event, and in the TouchUI sample, the code looks like this:

private void MainForm_Resize(object sender, EventArgs e)
{
   
Common.Instance.ClientRectangle = Screen.PrimaryScreen.WorkingArea;
   
if(loaded)
       
foreach(Dialog dialog in dialogStack)
            dialog.Resize(
Common.Instance.ClientRectangle);
   
this.Refresh();
}

The working area of the screen (excluding the title and menu bars) can be retrieved from a static method (PrimaryScreen) on the Screen class (in the System.Windows.Forms namespace). We save that in our app singleton (Common.Instance) and use it to inform all dialogs about the new dimensions.

Each dialog can adapt to the new resolution or orientation, and this is the code in the main dialog:

public override void Resize(Rectangle r)
{
   
this.Rectangle = r;
    scrollList.Rectangle =
new Rectangle(0, headerHeight,
        r.Width, r.Height - headerHeight);
}

It simply update the bounds of its ScrollList control, that in turn will adapt by using the new dimensions in its Paint method. With these simple measures, the app can run on any device with the same code and even the same executable. Here are some different resolutions and orientation examples...

image image

image

...and they all have their original resolution in the following order (width x height): 176 x 220 (non-touch), 400 x 240, and 480 x 800 (both touch). The beautiful strawberry graphics is from a photo by Tina Phillips / FreeDigitalPhotos.net.

Currently rated 5.0 by 3 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Chris | Compact Framework | User Interface | Windows Mobile

TouchUI - Animated Navigation (part 12)

by Chris 25. December 2009 10:02

nontouchanim Now we come to one of the nicest additions that the TouchUI bring to the table - the animated navigation. This is, as so many other things in modern mobile user experience design, inspired by the way things work on the iPhone.

When the user select an item in a list (in a dialog), the following code is run in the main form:

private void navigateForward()
{
    Common.Instance.PaintCopyGraphics.DrawImage
        (Common.Instance.PaintBitmap, 0, 0);

   
if(dialogStack.Last() is MainDialog)
    {
       
MainDialog mainDialog = (MainDialog)dialogStack.Last();
       
DetailDialog detailDialog = new DetailDialog(Common.Instance.ScreenFactor,
            Common.Instance.ClientRectangle, mainDialog.SelectedLine + 1);
        dialogStack.Add(detailDialog);
    }
    animationOffset =
Common.Instance.ClientRectangle.Width / 4;
    animationLeft =
Common.Instance.ClientRectangle.Width;
}

As you may remember from the painting part of this series, the PaintBitmap (in the app's singleton class Common.Instance) is used to do all the painting in the app. Then, it is used in the OnPaint method to do the actual painting to the screen. The first thing that happen in the code above, is that a copy of the current screen is made (into the PaintCopyBitmap that is pointed to by PaintCopyGraphics). The navigation is done (the details form is added to the dialog stack), and then the animations horizontal start position (animationLeft) is set along with the offset to move the new screen into place for each frame (animationOffset, in this case it's in four steps making the animation take about a third of a second). The navigation backward (up the dialog stack) is done in a similar fashion, but then the animationLeft and animationOffset are both negative. Btw, the beautiful strawberry graphics is from a photo by Tina Phillips / FreeDigitalPhotos.net.

With this in place, the actual painting is done in the OnPaint method, and it looks like this:

protected override void OnPaint(PaintEventArgs e)
{
   
base.OnPaint(e);

    dialogStack.Last().Paint(
Common.Instance.PaintGraphics);
   
Rectangle r = Common.Instance.ClientRectangle;
   
if(animationLeft != 0)
    {
        e.Graphics.DrawImage(
Common.Instance.PaintCopyBitmap,
           
new Rectangle(animationLeft - r.Width * animationLeft.CompareTo(0),
            0, r.Width, r.Height), r, GraphicsUnit.Pixel);
        e.Graphics.DrawImage(
Common.Instance.PaintBitmap,
            new Rectangle(animationLeft, 0, r.Width, r.Height), r,
            GraphicsUnit.Pixel);
        animationLeft -= animationOffset;
       
if(Math.Abs(animationLeft) < Math.Abs(animationOffset))
            animationLeft = 0;
    }
   
else
        e.Graphics.DrawImage(Common.Instance.PaintBitmap, r, r,
            GraphicsUnit.Pixel);
}

First, the dialog is painted (updating the PaintBitmap with the new content), and then both the old and new bitmaps are painted according to the step in the animation (controlled by animationLeft). When the animation is complete (animationLeft is zero), only the new dialog is painted.

A similar approach is used for the switching of images in the detail dialog in the sample, so be sure to check that out as an example of responding to sweep gestures.

Currently rated 5.0 by 2 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Chris | Compact Framework | User Interface | Windows Mobile

TouchUI - ScrollPanel (part 11)

by Chris 18. December 2009 10:01

Another controls that inherit from the ScrollControl (described in a previous part of this series) is the ScrollPanel control. As the name implies, this is a panel that implement modern features like soft (kinetics) scrolling, and the design and functionality was inspired by the scroll view control on iPhone.

The ScrollPanel itself does not implement much logic, it mainly functions as a way for the app to implement a scrolling panel. In the TouchUI sample app, that is done in the DetailScrollPanel that inherits from ScrollPanel (and in turn from ScrollControl). The painting of the panel is done like this:

public override void Paint(Graphics g)
{
   
base.Paint(g);

    imageHeight = Rectangle.Width * image.Size.Height / image.Size.Width;
    g.DrawImage(imagenew Rectangle(Rectangle.Left,
        Rectangle.Top + ScrollTop, Rectangle.Width, imageHeight),
        new Rectangle(0, 0, image.Width, image.Height),
        GraphicsUnit.Pixel);

   
string s = "Lorem ipsum dolor sit amet, consectetur adipisicing...";

    g.DrawString(s,
Common.Instance.Font, Common.Instance.FontBrush,
        new RectangleF(Rectangle.Left + 8 * ScreenFactor,
        Rectangle.Top + ScrollTop + imageHeight + 8,
        Rectangle.Width - 16 * ScreenFactor,
        ScrollHeight - imageHeight - 16 * ScreenFactor),
       
new StringFormat());
}

It draws the image at the top, and some Latin text as the bottom, and note that the use of the control's placement and size (Rectangle) will make both the image and the test resize with the screen. Dynamically that useful when switching between portrait and landscape, but it also means that different resolutions on different devices are handled.

This simple setup provides for full controls while still allowing for any use of the panel area. It could be of any height, and it could include any number of items. Although not included in the sample, it could also include traditional WinForms controls to allow input (text and combo boxes).

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Chris | Compact Framework | User Interface | Windows Mobile

TouchUI - ScrollList (part 10)

by Chris 11. December 2009 10:00

One of the controls that inherit from the ScrollControl (described in the previous part of this series) is the ScrollList control. As the name implies, this is a list that implement modern features like soft (kinetics) scrolling, and the design and functionality was inspired by the table view control on iPhone.

The control holds a generic list of scroll list items (suitably name ScrollListItem), and during the paining of the control, it instructs all the list items to paint themselves. It also takes care of the painting of the background of each item, to enable every other line to have a different background color, and also to highlight the selected item. The painting is efficient in the way that only the items that are (at least partly) visible is painted, and the code looks like this:

public override void Paint(Graphics g)
{
    DoAutomaticMotion();

   
int firstListItem = Math.Max(-(ScrollTop / ListItemHeight), 0);
   
int lastListItem = Math.Min(firstListItem +
        (Rectangle.Height / ListItemHeight) + 2, ListItems.Count);
   
for(int i = firstListItem; i < lastListItem; i++)
    {
       
if(i == SelectedLine)
        {
            ListItems[i].Selected =
true;
            g.FillRectangle(HighBrush,
new Rectangle(0,
                Rectangle.Top + ListItemHeight * i + ScrollTop,
                Rectangle.Width, ListItemHeight));
        }
       
else
        {
            ListItems[i].Selected =
false;
           
if(i % 2 == 1)
                g.FillRectangle(GrayBrush,
new Rectangle(0,
                    Rectangle.Top + ListItemHeight * i + ScrollTop,
                    Rectangle.Width, ListItemHeight));
        }

        ListItems[i].Rectangle =
new Rectangle(0,
            Rectangle.Top + ListItemHeight * i + ScrollTop,
            Rectangle.Width, ListItemHeight);
        ListItems[i].Paint(g);
    }
}

The control also takes care of translating a generic selection in the base class ScrollControl into an selection of an item in the list. The owner of this control, normally a dialog, is notified of each selection through an event (Selected). This is handled for both the finger (mouse) and if a hardware (physical) key on the phone is pressed. This hardware key support means that the control is also functional on a non-touch device.

The implementation of each list item is done by inheriting from ScrollListItem and mainly implement the Paint method. See the sample code for more details.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Chris | Compact Framework | User Interface | Windows Mobile

TouchUI - ScrollControls (part 9)

by Chris 4. December 2009 13:26

Now we have come to the heart of this series, as this is where the most important part of the TouchUI framework is implemented. It's the base control class for the controls that allow scrolling, and the implementation was derived in some parts from the excellent SmoothListBox by Fredrik Bornander.

As you will see when you look at the implementation, there are a lot of settings that you can use to modify the exact behavior, and here are some examples:

  • snapBackFactor: how fast should the control snap back when passed the top and bottom borders
  • dragDistanceFactor: factor to increase the actual speed of dragging
  • maxVelocity: maximum scrolling velocity
  • deaccelerationFactor: how fast should the scrolling decelerate when we let go
  • borderDeaccelerationFactor: same as before, but on the top and bottom borders

The action starts when the finger (mouse) is pushed down...

public override void MouseDown(Point p)
{
    mouseDownPoint = p;
    previousPoint = mouseDownPoint;
}

...and position is simply saved. This becomes useful as the finger is dragged...

public override void MouseMove(Point p)
{
    int delta = p.Y - previousPoint.Y;
    draggedDistance = delta;
    scroll(delta);
    previousPoint = p;
}

...the distance dragged is recorded for each movement, and also the control is scrolled accordingly:

private void scroll(int offset)
{
    ScrollTop += offset;
}

When the finger is finally released...

public override void MouseUp(Point p)
{
    velocity = Math.Min(Math.Max(dragDistanceFactor * draggedDistance,
        -maxVelocity), maxVelocity);
    draggedDistance = 0;
    DoAutomaticMotion();
}

...the speed is recorded and the main scrolling logic is called...

public virtual void DoAutomaticMotion()
{
    velocity *= deaccelerationFactor;
   
if(ScrollTop > 0 || ScrollTop + ScrollHeight < Rectangle.Height)
        velocity *= borderDeaccelerationFactor;
   
float deltaDistance = elapsedTime * velocity;
   
if(Math.Abs(deltaDistance) >= 1.0f)
        scroll((
int)deltaDistance);
   
else
    {
       
if(ScrollTop != 0)
        {
           
if(ScrollTop > 0)
                scroll(-
Math.Max(1, (int)(snapBackFactor * (float)(ScrollTop))));
           
else
            {
               
if(ScrollHeight > Rectangle.Height)
                {
                   
int bottomPosition = ScrollTop + ScrollHeight + Rectangle.Top;
                   
if(bottomPosition < Rectangle.Height)
                        scroll(
Math.Max(1, (int)(snapBackFactor *
                            (float)(Rectangle.Height - bottomPosition))));
                }
               
else
                    scroll(Math.Max(1, -((int)(snapBackFactor *
                        (float)ScrollTop))));
            }
        }
    }
}

...and it handles the automatic motion of the control when the finger is released. It also takes care of the handling of the borders, and the automatic negative scroll that occurs then the borders are passed.

Another important thing that happens in the MouseUp method is...

PointSelected = (Math.Abs(p.Y - mouseDownPoint.Y) < 5);
if(Math.Abs(mouseDownPoint.X - p.X) > 50)
    HorizontalGesture = mouseDownPoint.X - p.X;
else
    HorizontalGesture = 0;

...that indicate if something was selected in the control (difference between pressed and released vertical position is less that five pixels), and whether sweep gesture was made (difference between pressed and released horizontal position greater than fifty pixels).

Currently rated 5.0 by 4 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Chris | Compact Framework | User Interface | Windows Mobile

TouchUI - Controls (part 8)

by Chris 28. November 2009 14:02

As I showed in a previous part of this series, I don't use any controls for the simpler dialog painting (i.e. for heading, line, etc). For more advanced painting and logic, the notion of controls is welcome, and is obviously a part of the TouchUI framework.

I started off in the traditional way, building WinForm controls, but soon realized that I really don't like all the overhead that comes with that. Also, I wanted the central control that is not natural when each control receive their own (mouse) events. So I ended up creating something new from scratch, and the root TouchUI control ended up being defined like this:

public class Control
{
   
public Rectangle Rectangle { get; set; }
   
public int ScreenFactor { get; set; }
   
public Color BackColor { get; set; }

    public virtual void Paint(Graphics g) { }
   
public virtual void MouseDown(Point p) { }
   
public virtual void MouseMove(Point p) { }
   
public virtual void MouseUp(Point p) { }
   
public virtual void Resize(Rectangle r) { }
   
public virtual void KeyDown(KeyEventArgs e) { }
   
public virtual void KeyUp(KeyEventArgs e) { }
}

Note that a rectangle is used for placement, and the important screen factor to handle painting on high-res devices. Also note how each event is actually a plain method reversing the "event bubbling" to push the "events" down the control hierarchy.

The pain is that each level explicitly need to forward the calls, but the gain is that none of the controls need to be aware of any events happening in the system. The end result is a few more calls and a minimum of overhead.

Currently rated 3.0 by 1 people

  • Currently 3/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Chris | Compact Framework | Windows Mobile | User Interface

TouchUI - Active UI (part 7)

by Chris 21. November 2009 14:01

When creating a highly active user interface, there is a main difference with a traditional WinForms app, and it is the frequency that the screen is updates. In a WinForms app, only the parts of the screen that has changed is updated, and that logic creates a lot of overhead. Also, with that model it's harder to do things like soft scrolling and animations.

No, I needed something different, and I started to think about games. I guess my recent implementation of the Animeter utility also made me think along those lines. In games you typical find an active user interface as things mostly change all the time, and the screen is updated at a certain frame rate (number of times per second). As you may know, the standard screen refresh rate in an ordinary movie is around 24 frames per second, but for animated moves a more common frequency is about half of that. For most cases in a mobile app, that is sufficient to allow for things to come alive.

In TouchUI, that frequency is handled by a timer set to fire at every 80 milliseconds creating a frame rate of 12.5:

timer = new Timer();
timer.Interval = 80;

timer.Tick += new EventHandler(timer_Tick);
timer.Enabled =
true;

Each time that happens, the screen is redrawn by triggering a simple form refresh:

private void timer_Tick(object sender, EventArgs e)
{
   
this.Refresh();
}

That, in turn, will trigger the OnPaint method to be called, that is responsible for all the painting (as you saw in the previous part of this series).

While this allows for a very responsive and flexible user interface, you need to realize that it really puts the processor to work. Therefore, you probably want to turn all these updates off as soon as the user switch to another app or when the device is put in sleep mode. It can be done by catching the form's activate and deactivate events, like this:

private void MainForm_Activated(object sender, EventArgs e)
{
    timer.Enabled = true;
}

private void MainForm_Deactivate(object sender, EventArgs e)
{
   
timer.Enabled = false;
}

With all this in place, we have a great foundation for creating a highly active user experience with soft scrollling and animations.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Chris | Compact Framework | Windows Mobile | User Interface

TouchUI - Painting (part 6)

by Chris 14. November 2009 14:00

As most things happening in a TouchUI app, the action begins in the main form (MainForm), and that is also true for the painting of all the graphics. To take full control over how the graphics is drawn in an app, the form's OnPaint method should be overridden, like this:

protected override void OnPaint(PaintEventArgs e)
{
   
base.OnPaint(e);

    dialogStack.Last().Paint(Common.Instance.PaintGraphics);
   
Rectangle r = Common.Instance.ClientRectangle;
    e.Graphics.DrawImage(Common.Instance.PaintBitmap, r, r, GraphicsUnit.Pixel);
}

The Common.Instance is a singleton used to store global things in the app, and is hold the current screen dimensions (ClientRectangle). It also holds an instance of the GDI Graphics class (PaintGraphics) pointing to a bitmap (PaintBitmap) that is used to draw to the screen (via e.Graphics). The bitmap is used as a buffer instead of doing the different GDI painting operations directly to the screen, and that is a good practice (called "double buffering") that will help you avoid things like screen flickering. Another important measure to avoid flickering is to override the OnBackGround like this:

 

protected override void OnPaintBackground(PaintEventArgs e) { }

Note that I don't call the form's (base class) implementation (base.OnPaintBackground).

So, we have a way of making the currently active dialog responsible for the painting of the screen, and this is an example of the Paint method of the main dialog:

public override void Paint(Graphics g)
{
    g.Clear(BackColor);

    g.DrawString(this.Text, NormalFont, HighBrush, 8, 5);
    g.DrawLine(Normal
Pen, 0, 23, Rectangle.Width, 23);
}

This code takes care of painting (clearing) the background, the heading text, and the horizontal line below the heading. The result looks something like this:

image

As we will see in some of the upcoming parts of this series, the dialog continues to pass the responsibility of painting to each of its controls, that does their work in much the same way.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Chris | Compact Framework | Windows Mobile | User Interface

TouchUI - One Form, Many Dialogs (part 5)

by Chris 7. November 2009 13:59

One of the hardest parts of writing apps for CF is the handling of the different forms, and also the navigation between the forms. The handling involves loading the forms (that takes time), showing the forms (and hiding the other forms to prevent strange behavior when switching between apps), and the memory management of forms (especially if they contain many controls, they use a lot of memory). After years with different form handling solutions (stacks with preloaded forms, etc), I finally decided to leave all those troubles behind by only having one form in my apps.

The single form in my TouchUI apps is usually called MainForm and even if it's not a part of the framework, it's really the main controller for the user interface. That is where initialization takes place, and all the events are fired in that single form. Each event (or overridden method) then gets passed on to the currently active dialog. A dialog is a TouchUI class that represent a logical form in the app, and the MainForm host a stack of currently loaded dialogs to ease navigation back up the dialog hierarchy. This is the first code (in the form's Load event) that runs when the app starts:

dialogStack = new List<Dialog>();
MainDialog mainDialog = new MainDialog(...);
dialogStack.Add(mainDialog);

As the dialog stack is a simple List, most of the other code in the MainForm will access the currently shown dialog with:

dialogStack.Last()

 

Here are some examples:

private void MainForm_MouseUp(object sender, MouseEventArgs e)
{
    dialogStack.Last().MouseUp(
new Point(e.X, e.Y));
}
private void MainForm_KeyDown(object sender, KeyEventArgs e)
{
    dialogStack.Last().KeyDown(e);
}
if(dialogStack.Last() is MainDialog)
{
   
MainDialog mainDialog = (MainDialog)dialogStack.Last();
   
DetailDialog detailDialog = new DetailDialog(...);
    dialogStack.Add(detailDialog);
}

 

The last code snippet is from the logic that navigates from the main dialog to a new dialog, and as you can see it resembles the initial code above. Therefore, moving up the hierarchy means to simply remove the last dialog in the stack (list).

Currently rated 5.0 by 2 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Chris | Compact Framework | Windows Mobile | User Interface

TouchUI - GDI (part 4)

by Chris 30. October 2009 13:57

The technology that makes the main requirement for the TouchUI framework (see part 3) come true is GDI, which also has a managed wrapper. The main player in that API is the Graphics class, and here are a couple of its methods:

  • Clear()
  • DrawEllipse()
  • DrawImage()
  • DrawLine()
  • DrawPolygon()
  • DrawRectangle()
  • DrawString()
  • FillEllipse()
  • FillPolygon()
  • FillRectangle()
  • MeasureString()

This is actually most of its methods, but still this is all we need to create any graphics that we want. Also, since it's not that extensive, it's not too difficult to learn GDI. Here's some code:

Graphics g;
...
Pen bluePen =
new Pen(Color.Blue, 3); // 3 pixels thick
g.DrawRectangle(bluePen, 0, 0, 50, 50); // x, y, width, height
Pen redPen = new Pen(Color.Red, 2);
g.DrawEllipse(redPen, 0, 50, 80, 60);

Let's not bother with how the graphics instance (g) is created, and assume it is, then the result would look something like this:

image

That's what I like with GDI: get whatever graphics you want with just a few lines of code! In the coming posts in this series I will not explain the details about the different GDI methods used, but there are many good resources to find on the Web if you want to learn more about GDI.

GDI is actually the way that the core operating system (Windows CE) is drawing things to the screen, and if you are interested in knowing more about how this technology performs on a device, you should check out the article Native vs. Managed Code: GDI Performance by Chris Tacke. As you will see, there are some performance gain to be made by calling the native APIs instead of using the managed wrappers. But since I had a goal of pure managed code for the TouchUI framework, I will stick with the managed wrappers in this series.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Chris | Compact Framework | Windows Mobile | User Interface

Powered by BlogEngine.NET 1.4.5.0
Theme by Mads Kristensen