Click here to Skip to main content
15,894,405 members
Articles / Programming Languages / C#

FloatingWindow - Multi-windows Interface for Silverlight 4

Rate me:
Please Sign up or sign in to vote.
4.92/5 (80 votes)
16 May 2011CPOL8 min read 508.2K   3.9K   123  
Resizable windows simulating multi-windows desktop interface for Silverlight 4
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace SilverFlow.Controls
{
    /// <summary>
    /// Iconbar containing minimized windows.
    /// </summary>
    [TemplatePart(Name = PART_LayoutRoot, Type = typeof(Canvas))]
    [TemplatePart(Name = PART_Grid, Type = typeof(Grid))]
    [TemplatePart(Name = PART_Bar, Type = typeof(Border))]
    [TemplatePart(Name = PART_SlidingBar, Type = typeof(Border))]
    [TemplatePart(Name = PART_Carousel, Type = typeof(StackPanel))]
    [TemplatePart(Name = PART_OpenButton, Type = typeof(BootstrapButton))]
    public class Iconbar : ContentControl
    {
        // Template parts
        private const string PART_LayoutRoot = "LayoutRoot";
        private const string PART_Grid = "Grid";
        private const string PART_Bar = "Bar";
        private const string PART_SlidingBar = "SlidingBar";
        private const string PART_Carousel = "Carousel";
        private const string PART_OpenButton = "OpenButton";

        // VSM groups
        private const string VSMGROUP_States = "VisualStateGroup";

        // VSM states
        private const string VSMSTATE_StateOpenButton = "OpenBootstrapButton";
        private const string VSMSTATE_StateClosedButton = "ClosedBootstrapButton";
        private const string VSMSTATE_StateVisible = "Visible";
        private const string VSMSTATE_StateHidden = "Hidden";

        // Animation duration in milliseconds
        private const double SlidingDurationInMilliseconds = 200;

        private Canvas layoutRoot;
        private Grid grid;
        private Border bar;
        private Border slidingBar;
        private StackPanel carousel;
        private BootstrapButton bootstrapButton;
        private Storyboard closingStoryboard;
        private Storyboard openingStoryboard;

        /// <summary>
        /// Gets the FloatingWindowHost containing the Iconbar.
        /// </summary>
        /// <value>FloatingWindowHost containing the Iconbar.</value>
        private FloatingWindowHost Host
        {
            get { return this.Parent as FloatingWindowHost; }
        }

        /// <summary>
        /// Gets or sets the width of the icon.
        /// </summary>
        /// <value>The width of the icon.</value>
        public double IconWidth { get; set; }

        /// <summary>
        /// Gets or sets the height of the icon.
        /// </summary>
        /// <value>The height of the icon.</value>
        public double IconHeight { get; set; }

        /// <summary>
        /// Gets or sets the width of the iconbar item.
        /// </summary>
        /// <value>The width of the iconbar item.</value>
        public double WindowIconWidth { get; set; }

        /// <summary>
        /// Gets or sets the height of the iconbar item.
        /// </summary>
        /// <value>The height of the iconbar item.</value>
        public double WindowIconHeight { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="Iconbar"/> class.
        /// </summary>
        public Iconbar()
        {
            DefaultStyleKey = typeof(Iconbar);
        }

        /// <summary>
        /// Builds the visual tree for the <see cref="Iconbar" /> control 
        /// when a new template is applied.
        /// </summary>
        public override void OnApplyTemplate()
        {
            UnsubscribeFromStoryBoardEvents();

            base.OnApplyTemplate();

            layoutRoot = GetTemplateChild(PART_LayoutRoot) as Canvas;
            grid = GetTemplateChild(PART_Grid) as Grid;
            bar = GetTemplateChild(PART_Bar) as Border;
            slidingBar = GetTemplateChild(PART_SlidingBar) as Border;
            carousel = GetTemplateChild(PART_Carousel) as StackPanel;
            bootstrapButton = GetTemplateChild(PART_OpenButton) as BootstrapButton;

            if (layoutRoot == null)
                throw new NotImplementedException("Template Part PART_LayoutRoot is required to display Iconbar.");

            if (grid == null)
                throw new NotImplementedException("Template Part PART_Grid is required to display Iconbar.");

            if (bar == null)
                throw new NotImplementedException("Template Part PART_Bar is required to display Iconbar.");

            if (slidingBar == null)
                throw new NotImplementedException("Template Part PART_SlidingBar is required to display Iconbar.");

            if (carousel == null)
                throw new NotImplementedException("Template Part PART_Carousel is required to display Iconbar.");

            if (bootstrapButton == null)
                throw new NotImplementedException("Template Part PART_OpenButton is required to display Iconbar.");

            if (Host != null)
                Host.SizeChanged += new SizeChangedEventHandler(Host_SizeChanged);

            GetStoryboards();
            SubscribeToStoryBoardEvents();

            bar.MouseMove += new MouseEventHandler(Bar_MouseMove);
            bootstrapButton.Click += new RoutedEventHandler(BootstrapButton_Click);

            SetSizeAndPosition();
        }

        /// <summary>
        /// Sets the bootstrap button visible.
        /// </summary>
        /// <param name="visible">if set to <c>true</c> the bootstrap button is visible.</param>
        public void SetBootstrapButtonVisible(bool visible)
        {
            VisualStateManager.GoToState(
                this,
                visible ? VSMSTATE_StateOpenButton : VSMSTATE_StateClosedButton, 
                true);
        }

        /// <summary>
        /// Shows the iconbar.
        /// </summary>
        public void ShowIconbar()
        {
            VisualStateManager.GoToState(this, VSMSTATE_StateVisible, true);
            Canvas.SetLeft(slidingBar, 0);
            FillCarousel();
        }

        /// <summary>
        /// Hides the iconbar.
        /// </summary>
        public void HideIconbar()
        {
            carousel.Children.Clear();
            VisualStateManager.GoToState(
                this,
                this.Host.WindowsCount > 0 ? VSMSTATE_StateHidden : VSMSTATE_StateClosedButton, 
                true);
        }

        /// <summary>
        /// Handles the Click event of the BootstrapButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void BootstrapButton_Click(object sender, RoutedEventArgs e)
        {
            if (bootstrapButton.StateOpen)
                ShowIconbar();
            else
                HideIconbar();
        }

        /// <summary>
        /// Gets the storyboards defined in the <see cref="Iconbar" /> style.
        /// </summary>
        private void GetStoryboards()
        {
            if (layoutRoot != null)
            {
                var groups = VisualStateManager.GetVisualStateGroups(layoutRoot) as Collection<VisualStateGroup>;
                if (groups != null)
                {
                    var states = (from stategroup in groups
                                  where stategroup.Name == Iconbar.VSMGROUP_States
                                  select stategroup.States).FirstOrDefault() as Collection<VisualState>;

                    if (states != null)
                    {
                        closingStoryboard = (from state in states
                                             where state.Name == Iconbar.VSMSTATE_StateHidden
                                             select state.Storyboard).FirstOrDefault();

                        openingStoryboard = (from state in states
                                             where state.Name == Iconbar.VSMSTATE_StateVisible
                                             select state.Storyboard).FirstOrDefault();
                    }
                }
            }
        }

        /// <summary>
        /// Subscribes to events that are on the storyboards. 
        /// </summary>
        private void SubscribeToStoryBoardEvents()
        {
            if (closingStoryboard != null)
                closingStoryboard.Completed += new EventHandler(Closing_Completed);

            if (openingStoryboard != null)
                openingStoryboard.Completed += new EventHandler(Opening_Completed);
        }

        /// <summary>
        /// Unsubscribe from events that are subscribed on the storyboards. 
        /// </summary>
        private void UnsubscribeFromStoryBoardEvents()
        {
            if (closingStoryboard != null)
                closingStoryboard.Completed -= new EventHandler(Closing_Completed);

            if (openingStoryboard != null)
                openingStoryboard.Completed -= new EventHandler(Opening_Completed);
        }

        /// <summary>
        /// Executed when the Closing storyboard ends. Bootstrap button state shall be "Open"
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">Event args.</param>
        private void Closing_Completed(object sender, EventArgs e)
        {
            bootstrapButton.StateOpen = true;
        }

        /// <summary>
        /// Executed when the Opening storyboard ends. Bootstrap button state shall be "Closed"
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">Event args.</param>
        private void Opening_Completed(object sender, EventArgs e)
        {
            bootstrapButton.StateOpen = false;
        }

        /// <summary>
        /// Add windows icons to the carousel.
        /// </summary>
        private void FillCarousel()
        {
            carousel.Children.Clear();

            var windows = from window in Host.FloatingWindows
                          where window.IsOpen && window.ShowInIconbar &&
                          !(Host.ShowMinimizedOnlyInIconbar && window.WindowState != WindowState.Minimized)
                          orderby window.IconText
                          select window;

            foreach (var window in windows)
            {
                WindowIcon icon = new WindowIcon()
                {
                    Title = window.IconText,
                    Thumbnail = window.WindowThumbnail,
                    FlowDirection = window.FlowDirection,
                    Window = window,
                    IconWidth = this.IconWidth,
                    IconHeight = this.IconHeight,
                    Width = this.WindowIconWidth,
                    Height = this.WindowIconHeight
                };

                icon.Click += new RoutedEventHandler(Icon_Click);
                carousel.Children.Add(icon);
            }
        }

        /// <summary>
        /// Handles the Click event of the Icon control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void Icon_Click(object sender, RoutedEventArgs e)
        {
            WindowIcon icon = sender as WindowIcon;

            if (icon != null && icon.Window != null)
            {
                icon.Window.RestoreWindow();
                HideIconbar();
            }
        }

        /// <summary>
        /// Updates position of the Iconbar on host's SizeChanged event.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.SizeChangedEventArgs"/> instance containing the event data.</param>
        private void Host_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            SetSizeAndPosition();
        }

        /// <summary>
        /// Sets the iconbar size and position.
        /// </summary>
        private void SetSizeAndPosition()
        {
            if (Host != null)
            {
                Width = Host.ActualWidth;
                grid.Width = Width;
                Canvas.SetTop(this, Host.ActualHeight);
            }
        }

        /// <summary>
        /// Handles the MouseMove event of the Bar control. It implements "Carousel" logic.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseEventArgs"/> instance containing the event data.</param>
        private void Bar_MouseMove(object sender, MouseEventArgs e)
        {
            WindowIcon icon = (from item in carousel.Children.OfType<WindowIcon>()
                               where item.IsMouseOver || item.IsPressed
                               select item).FirstOrDefault();

            // If there is at least one icon on the bar
            if (icon != null)
            {
                double a = e.GetPosition(bar).X;
                double b = bar.ActualWidth - bar.Padding.Horizontal();
                double c = slidingBar.ActualWidth;

                // If sliding bar does not fit into the bar, shift the sliding bar
                if (c > b)
                {
                    double width = b - icon.ActualWidth;
                    if (width != 0)
                    {
                        a -= icon.ActualWidth / 2;
                        if (a < 0) a = 0;
                        if (a > width) a = width;

                        double x = (a / width) * (b - c);

                        Storyboard storyboard = slidingBar.AnimateDoubleProperty("(Canvas.Left)", null, x, SlidingDurationInMilliseconds);

                        // Get absolute mouse position
                        Point mousePosition = e.GetPosition(null);

                        storyboard.Completed += (s, args) =>
                            {
                                // Select an icon on storyboard completion
                                // That is necessary because MouseOver state won't be proceeded correctly
                                SlidingBarStoryboardCompleted(mousePosition);
                            };
                    }
                }
            }
        }

        /// <summary>
        /// Handles the Completed event of the SlidingBarStoryboard control.
        /// </summary>
        /// <param name="mousePosition">Absolute mouse position.</param>
        private void SlidingBarStoryboardCompleted(Point mousePosition)
        {
            // Find selected icon
            var selectedIcon = (from item in VisualTreeHelper.FindElementsInHostCoordinates(mousePosition, carousel).OfType<WindowIcon>()
                                select item).FirstOrDefault();

            // Select an icon in mouse position
            foreach (var icon in carousel.Children.OfType<WindowIcon>())
            {
                icon.Selected = (selectedIcon != null && icon == selectedIcon);
            }
        }
    }
}

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
Latvia Latvia
Jevgenij lives in Riga, Latvia. He started his programmer's career in 1983 developing software for radio equipment CAD systems. Created computer graphics for TV. Developed Internet credit card processing systems for banks.
Now he is System Analyst in Accenture.

Comments and Discussions