Click here to Skip to main content
15,886,518 members
Articles / Desktop Programming / WPF

WPF: Sticky Notes ListBox

Rate me:
Please Sign up or sign in to vote.
4.93/5 (79 votes)
4 Nov 2009CPOL5 min read 250.7K   5.1K   192  
A notes listbox that you could use in your own app.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;

namespace NotesListBox
{

    public delegate void NoteEventHandler(object sender, NoteEventArgs e);

    /// <summary>
    /// A simple NotesListBoxControl, that hosts a number of 
    /// <see cref="NotesListBox.Note">NotesListBox.Note</see>s
    /// </summary>
    public partial class NotesListBoxControl : UserControl
    {
        #region Data
        private readonly int itemOffset = 110;
        private ObservableCollection<Note> notes;
        #endregion

        #region Ctor
        public NotesListBoxControl()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(NotesListBoxControl_Loaded);
        }
        #endregion

        #region Routed Events

        #region Note Added Event
        public static readonly RoutedEvent NoteAddedEvent =
            EventManager.RegisterRoutedEvent(
            "NoteAdded", RoutingStrategy.Bubble,
            typeof(NoteEventHandler),
            typeof(NotesListBoxControl));

        //add remove handlers
        public event NoteEventHandler NoteAdded
        {
            add { AddHandler(NoteAddedEvent, value); }
            remove { RemoveHandler(NoteAddedEvent, value); }
        }
        #endregion

        #region Note Removed Event
        public static readonly RoutedEvent NoteRemovedEvent =
            EventManager.RegisterRoutedEvent(
            "NoteRemoved", RoutingStrategy.Bubble,
            typeof(NoteEventHandler),
            typeof(NotesListBoxControl));

        //add remove handlers
        public event NoteEventHandler NoteRemoved
        {
            add { AddHandler(NoteRemovedEvent, value); }
            remove { RemoveHandler(NoteRemovedEvent, value); }
        }
        #endregion

        #region Note Changed Event
        public static readonly RoutedEvent NoteChangedEvent =
            EventManager.RegisterRoutedEvent(
            "NoteChanged", RoutingStrategy.Bubble,
            typeof(NoteEventHandler),
            typeof(NotesListBoxControl));

        //add remove handlers
        public event NoteEventHandler NoteChanged
        {
            add { AddHandler(NoteChangedEvent, value); }
            remove { RemoveHandler(NoteChangedEvent, value); }
        }
        #endregion

        #region Close Notes Event
        public static readonly RoutedEvent CloseNotesEvent =
            EventManager.RegisterRoutedEvent(
            "CloseNotes", RoutingStrategy.Bubble,
            typeof(EventHandler),
            typeof(NotesListBoxControl));

        //add remove handlers
        public event EventHandler CloseNotes
        {
            add { AddHandler(CloseNotesEvent, value); }
            remove { RemoveHandler(CloseNotesEvent, value); }
        }
        #endregion

        #endregion

        #region Public Properties
        public ObservableCollection<Note> Notes
        {
            get { return notes; }
            set
            {
                if (value != null)
                {
                    notes = value;
                    lstNotes.ItemsSource = notes;
                    foreach (INotifyPropertyChanged note in notes)
                    {
                        note.PropertyChanged -= Note_PropertyChanged;
                        note.PropertyChanged += Note_PropertyChanged;
                    }
                    this.notes.CollectionChanged += Notes_CollectionChanged;
                }
            }
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Resize the ListBoxControl to fill the available space 
        /// (The hosting Window/UserControl may have changed Size)
        /// </summary>
        /// <param name="heightToUse">The available Height that can be used</param>
        public void ResizeToFillAvailableSpace(double heightToUse)
        {
            gridRowForList.Height = new GridLength((double)
            heightToUse - gridRowForHeader.Height.Value, GridUnitType.Pixel);

        }
        #endregion




        #region Private Methods
        private void NotesListBoxControl_Loaded(object sender, RoutedEventArgs e)
        {
            ResizeToFillAvailableSpace(this.ActualHeight);
            lstNotes.Style = CreateStyleForListBox();
        }


        /// <summary>
        /// When The Notes collection changes create a new Style which contains a 
        /// Canvas just big enough for all the ListBox items.
        /// </summary>
        private void Notes_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            lstNotes.Style = CreateStyleForListBox();
            foreach (INotifyPropertyChanged note in Notes)
            {
                note.PropertyChanged -= Note_PropertyChanged;
                note.PropertyChanged += Note_PropertyChanged;
            }
        }


        /// <summary>
        /// Creates a ListBox style that caters for a maximum size for the ListBox ItemsPanel based
        /// on the number of actual ListBox items
        /// </summary>
        /// <returns></returns>
        private Style CreateStyleForListBox()
        {
            Style style = new Style(typeof(ListBox));
            style.Setters.Add(new Setter(Control.BackgroundProperty, Brushes.Transparent));
            style.Setters.Add(new Setter(Control.BorderBrushProperty, Brushes.Transparent));
            FrameworkElementFactory feCanvas = new FrameworkElementFactory(typeof(Canvas));
            feCanvas.SetValue(Control.BackgroundProperty, Brushes.Transparent);
            feCanvas.SetValue(Control.WidthProperty, 200.0);
            feCanvas.SetValue(Control.MarginProperty, new Thickness(5));
            feCanvas.SetValue(Control.HeightProperty, (double)(lstNotes.Items.Count * itemOffset));
            ItemsPanelTemplate itemsPanelTemplate = new ItemsPanelTemplate(feCanvas);
            style.Setters.Add(new Setter(ItemsControl.ItemsPanelProperty, itemsPanelTemplate));
            return style;
        }


        private void btnAdd_Click(object sender, RoutedEventArgs e)
        {
            Note newNote = new Note
            {
                DateCreated = DateTime.Now.Subtract(TimeSpan.FromDays(1.0)),
                Data = string.Empty
            };

            Notes.Add(newNote);

            foreach (INotifyPropertyChanged note in Notes)
            {
                note.PropertyChanged -= Note_PropertyChanged;
                note.PropertyChanged += Note_PropertyChanged;
            }

            lstNotes.SelectedIndex = lstNotes.Items.Count - 1;

            NoteEventArgs args = new NoteEventArgs(NoteAddedEvent, newNote);
            RaiseEvent(args);    
        }


        private void btnRemove_Click(object sender, RoutedEventArgs e)
        {
            if (lstNotes.SelectedIndex >= 0)
            {
                Note noteToRemove = lstNotes.SelectedItem as Note;
                Notes.Remove(noteToRemove);

                NoteEventArgs args = new NoteEventArgs(NoteRemovedEvent, noteToRemove);
                RaiseEvent(args);  
            }
        }


        private void Note_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            Note noteToUpdate = sender as Note;
            NoteEventArgs args = new NoteEventArgs(NoteChangedEvent, noteToUpdate);
            RaiseEvent(args);    
        }


        private void txt_GotFocus(object sender, RoutedEventArgs e)
        {
            ListBoxItem lbi = GetAncestorByType(
                   e.OriginalSource as DependencyObject,
                   typeof(ListBoxItem)) as ListBoxItem;

            if (lbi != null)
            {
                lstNotes.SelectedIndex =
                    lstNotes.ItemContainerGenerator.IndexFromContainer(lbi);
            }
        }
        

        private static DependencyObject GetAncestorByType(DependencyObject element, Type type)
        {
            if (element == null) return null;
            if (element.GetType() == type) return element;
            return GetAncestorByType(VisualTreeHelper.GetParent(element), type);
        }


        private void btnClose_Click(object sender, RoutedEventArgs e)
        {
            RoutedEventArgs args = new RoutedEventArgs(CloseNotesEvent);
            RaiseEvent(args);  
        }
        #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)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions