Click here to Skip to main content
15,886,035 members
Articles / Mobile Apps / Windows Mobile

Icon Menu for the .NET Compact Framework

Rate me:
Please Sign up or sign in to vote.
5.00/5 (13 votes)
21 Apr 2009CPOL7 min read 51.9K   906   65  
How to implement an iPhone style icon menu using the .NET Compact Framework.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;
using System.Collections;
using System.Diagnostics;

namespace Bornander.IconMenu
{

    public delegate void IconDeletedHandler(Icon icon);

    public partial class IconMenu : UserControl
    {
        private enum State
        {
            Launch,
            Edit
        }

        private Image offscreenImage = null;
        private Graphics offscreen = null;

        private int slotWidth = 48;
        private int slotHeight = 48;

        private List<Slot> slots = new List<Slot>();
        private List<Icon> icons = new List<Icon>();

        private bool mouseIsDown = false;
        private Vector mouseDownPosition = new Vector();
        private DateTime mouseDownTimestamp = DateTime.Now;

        private Icon selectedIcon = null;

        private Image deleteMarker = null;

        private State state = State.Launch;

        private TimeSpan clickDelay = new TimeSpan(0, 0, 0, 0, 500);

        public event IconDeletedHandler Deleted;


        public IconMenu()
        {
            InitializeComponent();


            animationTimer.Enabled = true;

            this.MouseDown += new MouseEventHandler(HandleMouseDown);
            this.MouseMove += new MouseEventHandler(HandleMouseMove);
            this.MouseUp += new MouseEventHandler(HandleMouseUp);
        }

        private void OnDeleted(Icon icon)
        {
            if (Deleted != null)
            {
                Deleted(icon);
            }
        }
        
        private void HandleMouseUp(object sender, MouseEventArgs e)
        {
            mouseIsDown = false;
            if (selectedIcon != null)
            {
                if (DateTime.Now - mouseDownTimestamp < clickDelay)
                {
                    switch (state)
                    {
                        case State.Launch:
                            selectedIcon.OnLaunch();
                            break;
                        case State.Edit:
                            Slot slot = GetSlotAtPoint((Point)mouseDownPosition);
                            if (deleteMarker != null && slot != null && !slot.IsEmpty && !slot.Icon.IsReadOnly)
                            {
                                Vector slotPosition = (Vector)slot.Icon.Location;
                                Vector positionInSlot = mouseDownPosition - slotPosition;

                                if (positionInSlot.X < deleteMarker.Width && positionInSlot.Y < deleteMarker.Height)
                                {
                                    DialogResult result = MessageBox.Show("Are you sure you want to delete this?", "Delete", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2);
                                    if (result == DialogResult.OK)
                                    {
                                        icons.Remove(slot.Icon);
                                        OnDeleted(slot.Icon);
                                        slot.Icon = null;
                                        Relayout();
                                    }
                                    break;
                                }
                            }
                            state = State.Launch;
                            selectedIcon.IsMouseControlled = false;
                            break;
                    }
                }
                else
                {
                    int selectedIndex = slots.IndexOf(selectedIcon.Slot);
                    if (selectedIndex > icons.Count - 1)
                    {
                        selectedIcon.Slot.Icon = null;
                        Slot slot = slots[icons.Count - 1];
                        slot.Icon = selectedIcon;
                        selectedIcon.Slot = slot;
                    }
                }
                selectedIcon.IsMouseControlled = false;
                selectedIcon = null;
            }
            else
            {
                if (state == State.Edit)
                {
                    state = State.Launch;
                }
            }
        }



        void HandleMouseMove(object sender, MouseEventArgs e)
        {
            if (selectedIcon != null)
            {
                if (state == State.Edit)
                {
                    Point mouseLocation = new Point(e.X, e.Y);
                    selectedIcon.UpdateOnMouseLocation(mouseLocation);

                    Slot slot = GetSlotAtPoint(mouseLocation);
                    if (slot != null)
                    {
                        int selectedIndex = slots.IndexOf(selectedIcon.Slot);
                        int currentIndex = slots.IndexOf(slot);
                        if (selectedIndex != currentIndex)
                        {
                            for (int i = selectedIndex; i != currentIndex; i += Math.Sign(currentIndex - selectedIndex))
                            {
                                Slot slotA = slots[i];
                                Slot slotB = slots[i + Math.Sign(currentIndex - selectedIndex)];

                                slotA.Icon = slotB.Icon;
                                if (slotA.Icon != null)
                                {
                                    slotA.Icon.Slot = slotA;
                                }
                            }

                            slot.Icon = selectedIcon;
                            selectedIcon.Slot = slot;
                        }
                    }
                }
                else
                {
                    if (DateTime.Now - mouseDownTimestamp >= clickDelay)
                    {
                        state = State.Edit;
                        selectedIcon.IsMouseControlled = true;
                    }
                }
            }
        }

        private void HandleMouseDown(object sender, MouseEventArgs e)
        {
            mouseIsDown = true;
            mouseDownTimestamp = DateTime.Now;

            mouseDownPosition = new Vector(e.X, e.Y);
            Icon icon = GetIconAtPoint((Point)mouseDownPosition);
            if (icon != null)
            {
                selectedIcon = icon;
            }
        }

        private Slot GetSlotAtPoint(Point point)
        {
            foreach(Slot slot in slots)
            {
                if (slot.Bounds.Contains(point))
                {
                    return slot;
                }
            }

            return null;
        }

        private Icon GetIconAtPoint(Point point)
        {
            foreach (Icon icon in icons)
            {
                if (icon.Slot.Bounds.Contains(point))
                {
                    return icon;
                }
            }
            return null;
        }

        private void AssignSlotToIcons()
        {
            foreach (Slot slot in slots)
            {
                slot.Icon = null;
            }

            foreach (Icon icon in icons)
            {
                icon.Slot = null;
                foreach (Slot slot in slots)
                {
                    if (slot.IsEmpty)
                    {
                        icon.Slot = slot;
                        slot.Icon = icon;
                        break;
                    }
                }
            }
        }

        private void CreateOffscreen()
        {
            offscreenImage = new Bitmap(Math.Max(Width, 1), Math.Max(Height, 1));
            offscreen = Graphics.FromImage(offscreenImage);
        }


        private void Relayout()
        {
            offscreen = null;
            int numberOfHorizontalSlots = Width / slotWidth;
            int numberOfVerticalSlots = Height / slotHeight;

            int horizontalPadding = (Width % slotWidth) / 2;
            int verticalPadding = (Height % slotHeight) / 2;

            icons.Sort(delegate(Icon left, Icon right)
            {
                if (left.Slot == null)
                    return 1;
                else
                {
                    if (right.Slot == null)
                        return -1;
                }

                return slots.IndexOf(left.Slot) - slots.IndexOf(right.Slot);
            });

            slots = new List<Slot>();

            for (int i = 0; verticalPadding + (i + 1) * slotHeight < Height; ++i)
            {
                for (int j = 0; j < numberOfHorizontalSlots; ++j)
                {
                    slots.Add(new Slot(new Rectangle(horizontalPadding + j * slotWidth, verticalPadding + i * slotHeight, slotWidth, slotHeight)));
                }
            }

            AssignSlotToIcons();

            Invalidate();
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            Relayout();
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            if (offscreen == null)
            {
                CreateOffscreen();
            }

            offscreen.FillRectangle(new SolidBrush(BackColor), ClientRectangle);

            foreach (Icon icon in icons)
            {
                if (!icon.IsMouseControlled)
                {
                    icon.Paint(offscreen, state == State.Edit, deleteMarker);
                }
            }

            if (selectedIcon != null)
            {
                selectedIcon.Paint(offscreen, state == State.Edit, deleteMarker);
            }
            e.Graphics.DrawImage(offscreenImage, 0, 0);
        }

        public void AddIcon(Icon icon)
        {
            icons.Add(icon);
            foreach (Slot slot in slots)
            {
                if (slot.IsEmpty)
                {
                    icon.Slot = slot;
                    slot.Icon = icon;
                    break;
                }
            }
        }

        private void OnAnimationStep(object sender, EventArgs e)
        {
            if (mouseIsDown && selectedIcon != null && (DateTime.Now - mouseDownTimestamp >= clickDelay))
            {
                state = State.Edit;
                selectedIcon.IsMouseControlled = true;
            }

            foreach (Icon icon in icons)
            {
                icon.Animate(0.2f, state == State.Edit);
            }
            Refresh();
            
        }

        #region Public properties

        public Image DeleteMarker
        {
            set { deleteMarker = value; }
            get { return deleteMarker; }
        }

        public int SlotWidth
        {
            set { slotWidth = value; }
            get { return slotWidth; }
        }

        public int SlotHeight
        {
            set { slotHeight = value; }
            get { return slotHeight; }
        }


        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Sweden Sweden
Article videos
Oakmead Apps Android Games

21 Feb 2014: Best VB.NET Article of January 2014 - Second Prize
18 Oct 2013: Best VB.NET article of September 2013
23 Jun 2012: Best C++ article of May 2012
20 Apr 2012: Best VB.NET article of March 2012
22 Feb 2010: Best overall article of January 2010
22 Feb 2010: Best C# article of January 2010

Comments and Discussions