Jens Kühner

Geek stuff about the .NET, Compact and Micro Framework.

October 2007 - Posts

List box with a background (image) for the .NET Micro Framework

The development boards and devices for the .NET Micro Framework have great color lcd displays. So a list box menu with a white or solid background looks quite boring. Why not add a background to a listbox?

Therefore I derived my custom list box called BackgroundImageListBox from ListBox. It has a property BackgroundIBitmap to specify a certain bitmap as background. If StretchImage is true the bitmap will be stretched to fill the whole list box, if false the bitmap will be centered. If no bitmap is specified, a color gradient background is drawn. This is done by overriding the OnRender method.

List box with color gradient background

And here the custom list box control:

using System;

using Microsoft.SPOT;

using Microsoft.SPOT.Presentation.Controls;

using Microsoft.SPOT.Presentation.Media;

 

namespace Kuehner.SPOT.Presentation.Controls

{

    public class BackgroundImageListBox : ListBox

    {

        private Bitmap backgroundBitmap;

        private bool stretchImage;

 

        public BackgroundImageListBox()

        {

            ScrollViewer v = base.LogicalChildren[0] as ScrollViewer;

            v.Background = null;

            this.Background = null;

        }

 

        public override void OnRender(DrawingContext dc)

        {

            base.OnRender(dc);

            if (this.backgroundBitmap != null)

            {

                if (this.stretchImage)

                    dc.Bitmap.StretchImage(0, 0, this.backgroundBitmap, this.Width, this.Height, 0xFF); //stretch to fit

                else

                {

                    //center

                    int x = (this.Width - this.backgroundBitmap.Width) / 2;

                    int y = (this.Height - this.backgroundBitmap.Height) / 2;

                    dc.DrawImage(this.backgroundBitmap, x, y); //draw unscaled

                }

            }

            else

            {

                //draw gradient if no bitmap specified

                dc.Bitmap.DrawRectangle(Color.Black, 0,

                                        0, 0, this.Width, this.Height, 0, 0,

                                        ColorUtility.ColorFromRGB(255, 96, 96), 0, 0,

                                        ColorUtility.ColorFromRGB(96, 96, 255), 0, this.Height,

                                        0xFF);

            }

        }

 

        public Bitmap BackgroundBitmap

        {

            get { return this.backgroundBitmap; }

            set { this.backgroundBitmap = value; }

        }

 

        public bool StretchImage

        {

            get { return this.stretchImage; }

            set { this.stretchImage = value; }

        }

    }

}

When you polulate your list box you need to set the Background of a list box item to null so that it is painted transparently.

Heres some code to populate your list box. 

        public Window CreateWindow()

        {

            // Create a window object and set its size to the

            // size of the display.

            mainWindow = new Window();

            mainWindow.Height = SystemMetrics.ScreenHeight;

            mainWindow.Width = SystemMetrics.ScreenWidth;

 

            BackgroundImageListBox listBox = new BackgroundImageListBox();

            listBox.BackgroundBitmap = Resources.GetBitmap(Resources.BitmapResources.MFsnowflake);

            //listBox.StretchImage = true;

            listBox.Width = mainWindow.Width;

            listBox.Height = mainWindow.Height;

 

            Font font = Resources.GetFont(Resources.FontResources.small);

            foreach (String s in new String[] { "One", "Two", "Three",

                                                "Four", "Five", "Six", "Seven", "Eight" })

            {

                Text text = new Text(font, "Menu " + s);

                text.Width = listBox.Width;

                text.Height = 20;

                text.TextAlignment = TextAlignment.Center;

                ListBoxItem item = new ListBoxItem();

                item.Child = text;

                item.Background = null; //important

                listBox.Items.Add(item);

            }

 

            mainWindow.Child = listBox;

 

            // Connect the button handler to all of the buttons.

            mainWindow.AddHandler(Buttons.ButtonUpEvent, new ButtonEventHandler(OnButtonUp), false);

 

            // Set the window visibility to visible.

            mainWindow.Visibility = Visibility.Visible;

 

            // Attach the button focus to the window.

            Buttons.Focus(mainWindow);

 

            return mainWindow;

        }

Extreme HAL debugging

I really like the  .NET Micro Framework and especially the emulator story. There is nearly nothing that you cannot do. It could be a little bit easier if some classes and methods where not internal. But ok, excellent and a huge step ahead anyway.

I found a way to decorate the low level HAL (hardware abstraction layer) drivers to monitor when they are accessed by an emulated MF application. So you could debug and monitor low level access eg. to GPIO-Ports and serial ports.

First we need a driver that wraps the original driver (so that we do not need to write all from scratch) and delegates the method calls to the original after logging the call and the parameters.

using System;

using System.Diagnostics;

using Microsoft.SPOT.Emulator;

using Microsoft.SPOT.Emulator.Gpio;

 

namespace Kuehner.SPOT.Emulator

{

    internal class GpioDriverDecorator : EmulatorComponent, IGpioDriver

    {

        private readonly IGpioDriver decoratedDriver;

 

        public GpioDriverDecorator(IGpioDriver decoratedDriver)

        {

            if (decoratedDriver == null)

                throw new ArgumentNullException("decoratedDriver");

            this.decoratedDriver = decoratedDriver;

        }

 

        #region IGpioDriver Members

        uint IGpioDriver.Attributes(uint pin)

        {

            return this.decoratedDriver.Attributes(pin);

        }

 

        void IGpioDriver.DisablePin(uint pin, int resistorState, uint direction, uint altFunction)

        {

            this.decoratedDriver.DisablePin(pin, resistorState, direction, altFunction);

        }

 

        bool IGpioDriver.EnableInputPin(uint pin, bool glitchFilterEnable, IntPtr isr, IntPtr isrParam, int interruptEdge, int resistorState)

        {

            return this.decoratedDriver.EnableInputPin(pin, glitchFilterEnable, isr, isrParam, interruptEdge, resistorState);

        }

 

        bool IGpioDriver.EnableInputPin(uint pin, bool glitchFilterEnable, IntPtr isr, int interruptEdge, int resistorState)

        {

            return this.decoratedDriver.EnableInputPin(pin, glitchFilterEnable, isr, interruptEdge, resistorState);

        }

 

        void IGpioDriver.EnableOutputPin(uint pin, bool initialState)

        {

            this.decoratedDriver.EnableOutputPin(pin, initialState);

        }

 

        uint IGpioDriver.GetDebounce()

        {

            return this.decoratedDriver.GetDebounce();

        }

 

        int IGpioDriver.GetPinCount()

        {

            return this.decoratedDriver.GetPinCount();

        }

 

        bool IGpioDriver.GetPinState(uint pin)

        {

            bool pinState = this.decoratedDriver.GetPinState(pin);

            Trace.WriteLine(string.Format("GetPinState pin={0} is {1}", pin, pinState), "IGpioDriver");

            return pinState;

        }

 

        bool IGpioDriver.Initialize()

        {

            return this.decoratedDriver.Initialize();

        }

 

        bool IGpioDriver.PinIsBusy(uint pin)

        {

            return this.decoratedDriver.PinIsBusy(pin);

        }

 

        bool IGpioDriver.ReservePin(uint pin, bool reserve)

        {

            return this.decoratedDriver.ReservePin(pin, reserve);

        }

 

        bool IGpioDriver.SetDebounce(long debounceTime)

        {

            return this.decoratedDriver.SetDebounce(debounceTime);

        }

 

        void IGpioDriver.SetPinState(uint pin, bool pinState)

        {

            Trace.WriteLine(string.Format("SetPinState pin={0}, pinState={1}", pin, pinState), "IGpioDriver");

            this.decoratedDriver.SetPinState(pin, pinState);

        }

 

        bool IGpioDriver.Uninitialize()

        {

            return this.decoratedDriver.Uninitialize();

        }

        #endregion

    }

}

Then we need a managed HAL to hook up the drivers.

 

using System;

using System.Reflection;

using Microsoft.SPOT.Emulator;

using Microsoft.SPOT.Emulator.Com;

using Microsoft.SPOT.Emulator.Gpio;

 

namespace Kuehner.SPOT.Emulator

{

    public class MonitoringHal : Hal

    {

        private readonly IComDriver decoratedComDriver;

        private readonly IGpioDriver decoratedGpioDriver;

 

        public MonitoringHal()

        {

            this.decoratedComDriver = this.Com;

            this.Com = new ComDriverDecorator(this.decoratedComDriver);

            this.decoratedGpioDriver = this.Gpio;

            this.Gpio = new GpioDriverDecorator(this.decoratedGpioDriver);

        }

 

        public override void SetupComponent()

        {

            base.SetupComponent();

            //set the internal emulator property for the original component since this property

            //is only set if the component is registered and it is needed to work correctly

            SetEmulator(this.decoratedComDriver);

            SetEmulator(this.decoratedGpioDriver);

        }

 

        private void SetEmulator(object component)

        {

            System.Diagnostics.Debug.Assert(component is EmulatorComponent);

            MethodInfo mi = typeof(EmulatorComponent).GetMethod("SetEmulator", BindingFlags.NonPublic | BindingFlags.Instance);

            mi.Invoke(component, new object[] { this.Emulator });

        }

    }

}

 

This was very extreme stuff and you usually might not need it. But it helps to understand how the emulator works.