Mercurial > silverbladetech
changeset 63:536498832a79
Latest version before changing bindings to Listbox
line wrap: on
line diff
--- a/.hgignore Sun Apr 22 09:01:20 2012 +0100 +++ b/.hgignore Sun Apr 22 13:33:42 2012 +0100 @@ -9,3 +9,4 @@ */packages/* *.stats */Bin/* +*/ClientBin/*
--- a/Glimpse/Glimpse Services/GlimpseService.vb Sun Apr 22 09:01:20 2012 +0100 +++ b/Glimpse/Glimpse Services/GlimpseService.vb Sun Apr 22 13:33:42 2012 +0100 @@ -86,9 +86,10 @@ Me.RootVisual = TryCast(objApp.RootVisual, FrameworkElement) Me.HostApplicationName = strHostApplicationName - Dim fw As New ChildWindow + Dim fw As New FloatableWindow() fw.Title = Me.HostApplicationName fw.Content = New GlimpseViewer + fw.ParentLayoutRoot = DirectCast(VisualTreeHelper.GetChild(RootVisual, 0), Panel) If Double.IsNaN(Me.RootVisual.Width) Then 'if the host control is autosized (consumes the browser window) then locate Glimpse in the top, left @@ -103,7 +104,7 @@ dblLeft = 0 End If - fw.Show() + fw.Show(dblLeft, 10) End If End Sub
--- a/Glimpse/Glimpse.vbproj Sun Apr 22 09:01:20 2012 +0100 +++ b/Glimpse/Glimpse.vbproj Sun Apr 22 13:33:42 2012 +0100 @@ -158,6 +158,12 @@ <Install>true</Install> </BootstrapperPackage> </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\SilverlightGlimpse\FloatableWindow\FloatableWindow.csproj"> + <Project>{D47E6045-91BB-4CD0-942F-FF015F10F7F2}</Project> + <Name>FloatableWindow</Name> + </ProjectReference> + </ItemGroup> <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Silverlight\$(SilverlightVersion)\Microsoft.Silverlight.VisualBasic.targets" /> <!-- To modify your build process, add your task inside one of the targets below and uncomment it. Other similar extension points exist, see Microsoft.Common.targets.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/Controllers/IResizableElement.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,82 @@ +using System.Windows; +using System.Windows.Input; + +namespace SilverFlow.Controls.Controllers +{ + /// <summary> + /// This interface shall be implemented by an element to be resized. + /// </summary> + public interface IResizableElement + { + /// <summary> + /// Gets or sets the width of the element. + /// </summary> + /// <value>The width.</value> + double Width { get; set; } + + /// <summary> + /// Gets or sets the height of the element. + /// </summary> + /// <value>The height.</value> + double Height { get; set; } + + /// <summary> + /// Gets or sets minimal width of the element. + /// </summary> + /// <value>Minimal width.</value> + double MinWidth { get; set; } + + /// <summary> + /// Gets or sets minimal height of the element. + /// </summary> + /// <value>Minimal height.</value> + double MinHeight { get; set; } + + /// <summary> + /// Gets or sets maximal width of the element. + /// </summary> + /// <value>Maximal width.</value> + double MaxWidth { get; set; } + + /// <summary> + /// Gets or sets maximal height of the element. + /// </summary> + /// <value>Maximal height.</value> + double MaxHeight { get; set; } + + /// <summary> + /// Gets the actual width. + /// </summary> + /// <value>The actual width.</value> + double ActualWidth { get; } + + /// <summary> + /// Gets the actual height. + /// </summary> + /// <value>The actual height.</value> + double ActualHeight { get; } + + /// <summary> + /// Gets or sets the position of the element. + /// </summary> + /// <value>The position.</value> + Point Position { get; set; } + + /// <summary> + /// Gets the parent of the element. + /// </summary> + /// <value>The parent object.</value> + DependencyObject Parent { get; } + + /// <summary> + /// Gets or sets the cursor of the element. + /// </summary> + /// <value>The cursor of the element.</value> + Cursor Cursor { get; set; } + + /// <summary> + /// Snapin controller. + /// </summary> + ISnapinController SnapinController { get; } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/Controllers/ISnapinController.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,68 @@ +using System.Collections.Generic; +using System.Windows; + +namespace SilverFlow.Controls.Controllers +{ + public interface ISnapinController + { + /// <summary> + /// Gets or sets a value indicating whether snap in is enabled. + /// </summary> + /// <value><c>true</c> if snap in is enabled; otherwise, <c>false</c>.</value> + bool SnapinEnabled { get; set; } + + /// <summary> + /// Gets or sets snap in bounds. + /// </summary> + /// <value>Snap in bounds.</value> + IEnumerable<Rect> SnapinBounds { get; set; } + + /// <summary> + /// Gets or sets snap in distance. + /// </summary> + /// <value>Snap in distance.</value> + double SnapinDistance { get; set; } + + /// <summary> + /// Gets or sets a value snap in margin - distance between adjacent edges. + /// </summary> + /// <value>Snap in margin.</value> + double SnapinMargin { get; set; } + + /// <summary> + /// Returns new position of the specified rectangle + /// taking into account bounds the rectangle can be attracted to. + /// </summary> + /// <param name="rect">The rectangle.</param> + /// <returns>New position.</returns> + Point SnapRectangle(Rect rect); + + /// <summary> + /// Snaps the bottom right corner of the specified rectangle to the nearest bounds. + /// </summary> + /// <param name="rect">The rectangle.</param> + /// <returns>New position.</returns> + Point SnapBottomRightCorner(Rect rect); + + /// <summary> + /// Snaps the upper left corner of the specified rectangle to the nearest bounds. + /// </summary> + /// <param name="rect">The rectangle.</param> + /// <returns>New position.</returns> + Point SnapTopLeftCorner(Rect rect); + + /// <summary> + /// Snaps the lower left corner of the specified rectangle to the nearest bounds. + /// </summary> + /// <param name="rect">The rectangle.</param> + /// <returns>New position.</returns> + Point SnapBottomLeftCorner(Rect rect); + + /// <summary> + /// Snaps the upper right corner of the specified rectangle to the nearest bounds. + /// </summary> + /// <param name="rect">The rectangle.</param> + /// <returns>New position.</returns> + Point SnapTopRightCorner(Rect rect); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/Controllers/InertiaController.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,165 @@ +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Media.Animation; +using SilverFlow.Controls.Extensions; +using SilverFlow.Geometry; + +namespace SilverFlow.Controls.Controllers +{ + /// <summary> + /// Calculates inertial motion of a dragged element. + /// </summary> + public class InertiaController + { + private const double SCREEN_DPI = 96; + private const double INCH = 0.0254; + private const double GRAVITATIONAL_ACCELERATION = 9.81; + private const double COEFFICIENT_OF_SLIDING_FRICTION = 0.015; + private const double MIN_DELTA = 0.001; + private const double DELAY_BEFORE_MOUSE_UP_IN_MILLISECONDS = 100; + private const int MAX_LAST_POINTS_TO_COUNT = 4; + + private double pixelsPerMeter = SCREEN_DPI / INCH; + + private Queue<Trail> mouseTrails = new Queue<Trail>(); + + /// <summary> + /// An element starts its motion. + /// </summary> + /// <param name="point">The starting point of the motion.</param> + public void StartMotion(Point point) + { + mouseTrails.Clear(); + mouseTrails.Enqueue(new Trail(point, DateTime.Now)); + } + + /// <summary> + /// Saves the last position of the moved element. + /// </summary> + /// <param name="point">Current position.</param> + public void MoveToPoint(Point point) + { + while (mouseTrails.Count >= MAX_LAST_POINTS_TO_COUNT) + { + // Remove all points except the last ones + mouseTrails.Dequeue(); + } + + mouseTrails.Enqueue(new Trail(point, DateTime.Now)); + } + + /// <summary> + /// Gets inertial motion parameters: distance and duration. + /// </summary> + /// <param name="hostBounds">The host bounds.</param> + /// <param name="windowBounds">The window bounds.</param> + /// <returns> + /// The inertial motion parameters: distance and duration. + /// </returns> + public InertialMotion GetInertialMotionParameters(Rect hostBounds, Rect windowBounds) + { + if (mouseTrails.Count < 2) return null; + + var mouseTrailsArray = mouseTrails.ToArray(); + + Point startPosition = mouseTrailsArray[0].Position; + DateTime startTime = mouseTrailsArray[0].Timestamp; + Point endPosition = mouseTrailsArray[mouseTrails.Count - 1].Position; + DateTime endTime = mouseTrailsArray[mouseTrails.Count - 1].Timestamp; + + double timeBetweenNowAndLastMove = (DateTime.Now - endTime).TotalMilliseconds; + Vector2 vector = new Vector2(startPosition, endPosition); + + if (timeBetweenNowAndLastMove < DELAY_BEFORE_MOUSE_UP_IN_MILLISECONDS && !vector.IsZero) + { + double time = (endTime - startTime).TotalSeconds; + time = (time == 0) ? 0.001 : time; + + double distance = vector.Length / pixelsPerMeter; + + double intialVelocity = distance / time; + + double expectedDistance = ((intialVelocity * intialVelocity) / (2 * COEFFICIENT_OF_SLIDING_FRICTION * GRAVITATIONAL_ACCELERATION)); + double expectedTime = (2 * expectedDistance) / intialVelocity; + + double shiftX = Math.Round(vector.LengthX * expectedDistance / distance); + double shiftY = Math.Round(vector.LengthY * expectedDistance / distance); + + // New Inertial Motion Vector + Vector2 imVector = new Vector2(endPosition, shiftX, shiftY).Round(); + double expectedLength = imVector.Length; + + Rect bounds = hostBounds.Add(-windowBounds.Width, -windowBounds.Height); + + if (bounds.Contains(endPosition)) + { + imVector = EnsureEndPointInBounds(imVector, bounds).Round(); + } + else if (hostBounds.Contains(endPosition)) + { + imVector = EnsureEndPointInBounds(imVector, hostBounds).Round(); + } + + // Reduce expected time if the Inertial Motion Vector was truncated by the bounds + double realTime = (expectedLength == 0) ? 0 : (expectedTime * imVector.Length / expectedLength); + + var motion = new InertialMotion() + { + Seconds = realTime, + EndPosition = imVector.End, + EasingFunction = new CubicEase() + { + EasingMode = EasingMode.EaseOut + } + }; + + return motion; + } + + return null; + } + + /// <summary> + /// Ensures the end point is in bounds. + /// </summary> + /// <param name="vector">The vector to check its ending point.</param> + /// <param name="bounds">The bounds.</param> + /// <returns>A vector with the ending point within specified bounds.</returns> + private Vector2 EnsureEndPointInBounds(Vector2 vector, Rect bounds) + { + if (!vector.IsZero && !bounds.Contains(vector.End)) + { + if (vector.IsVertical) + { + vector.End = vector.End.EnsureInVerticalBounds(bounds); + } + else if (vector.IsHorizontal) + { + vector.End = vector.End.EnsureInHorizontalBounds(bounds); + } + else + { + double k = (vector.LengthY) / (vector.LengthX); + Point point = vector.End; + + if (vector.End.X < bounds.Left || vector.End.X > bounds.Right) + { + point = point.EnsureInHorizontalBounds(bounds); + point.Y = (k * (point.X - vector.Start.X)) + vector.Start.Y; + } + + if (point.Y < bounds.Top || point.Y > bounds.Bottom) + { + point = point.EnsureInVerticalBounds(bounds); + point.X = ((point.Y - vector.Start.Y) / k) + vector.Start.X; + } + + vector.End = point.EnsureInBounds(bounds); + } + } + + return vector; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/Controllers/InertialMotion.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,37 @@ +using System.Windows; +using System.Windows.Media.Animation; + +namespace SilverFlow.Controls.Controllers +{ + /// <summary> + /// Represents a vector and duration of the inertial motion, calculated by the InertiaController. + /// </summary> + public class InertialMotion + { + /// <summary> + /// Gets or sets ending position of the displaced element. + /// </summary> + /// <value>The ending position.</value> + public Point EndPosition { get; set; } + + /// <summary> + /// Gets or sets duration of the inertial motion in seconds. + /// </summary> + /// <value>Duration of the inertial motion in seconds.</value> + public double Seconds { get; set; } + + /// <summary> + /// Gets or sets the Easing Function applied to the animation. + /// </summary> + /// <value>The Easing Function.</value> + public IEasingFunction EasingFunction { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="InertialMotion"/> class. + /// </summary> + public InertialMotion() + { + this.Seconds = 0; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/Controllers/ResizeController.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,453 @@ +using System; +using System.Windows; +using System.Windows.Input; +using SilverFlow.Controls.Enums; +using SilverFlow.Controls.Extensions; + +namespace SilverFlow.Controls.Controllers +{ + /// <summary> + /// Calculates resizing element position and size. + /// </summary> + public class ResizeController + { + /// <summary> + /// Thickness of resizing area. + /// </summary> + private const double ResizingAreaDefaultValue = 6; + + /// <summary> + /// Minimal width of the resized window if MinWidth is set to 0. + /// </summary> + private const double MinResizedWidth = 20; + + /// <summary> + /// Resizing bounds, i.e. an area within which the resizing cursor can move. + /// </summary> + private Rect bounds; + + /// <summary> + /// Initial element size when resizing started. + /// </summary> + private Size initialSize; + + /// <summary> + /// Initial position of the element when resizing started. + /// </summary> + private Point initialPosition; + + /// <summary> + /// A corner or edge of the element used for resizing. + /// </summary> + private ResizeAnchor anchor; + + /// <summary> + /// An element implementing IResizableElement interface. + /// </summary> + private IResizableElement element; + + /// <summary> + /// Gets a framework element hosting resizing element. + /// </summary> + /// <value>The host.</value> + private FrameworkElement Host + { + get + { + return element.Parent as FrameworkElement; + } + } + + /// <summary> + /// Gets a value indicating whether the resizing can be started. + /// </summary> + /// <value> + /// <c>true</c> if resizing can be started; otherwise, <c>false</c>. + /// </value> + public bool CanResize + { + get + { + return anchor != ResizeAnchor.None; + } + } + + /// <summary> + /// Gets or sets the width of the resizing area. + /// </summary> + /// <value>The width of the resizing area. Default value is 6.</value> + public double ResizingArea { get; set; } + + /// <summary> + /// Gets a value indicating whether this instance can be resized horizontally. + /// </summary> + /// <value> + /// <c>true</c> if this instance can be resized horizontally; otherwise, <c>false</c>. + /// </value> + private bool CanResizeHorizontally + { + get + { + return !(element.MinWidth == element.MaxWidth && element.MinWidth != 0); + } + } + + /// <summary> + /// Gets a value indicating whether this instance can be resized vertically. + /// </summary> + /// <value> + /// <c>true</c> if this instance can be resized vertically; otherwise, <c>false</c>. + /// </value> + private bool CanResizeVertically + { + get + { + return !(element.MinHeight == element.MaxHeight && element.MinHeight != 0); + } + } + + /// <summary> + /// Initializes a new instance of the <see cref="ResizeController"/> class. + /// </summary> + /// <param name="resizableElement">The resizable element.</param> + public ResizeController(IResizableElement resizableElement) + { + if (resizableElement == null) + throw new ArgumentNullException("resizableElement"); + + element = resizableElement; + anchor = ResizeAnchor.None; + ResizingArea = ResizingAreaDefaultValue; + } + + /// <summary> + /// Starts resizing of the element. + /// </summary> + public void StartResizing() + { + initialSize = new Size(element.ActualWidth, element.ActualHeight); + initialPosition = element.Position; + + CalculateBoundsForResizing(); + } + + /// <summary> + /// Resizes the element. + /// </summary> + /// <param name="dx">Displacement by X-coordinate.</param> + /// <param name="dy">Displacement by Y-coordinate.</param> + public void Resize(double dx, double dy) + { + switch (anchor) + { + case ResizeAnchor.BottomRight: + ResizeBottomRight(dx, dy); + break; + + case ResizeAnchor.Right: + ResizeBottomRight(dx, 0); + break; + + case ResizeAnchor.Bottom: + ResizeBottomRight(0, dy); + break; + + case ResizeAnchor.TopLeft: + ResizeTopLeft(dx, dy); + break; + + case ResizeAnchor.Top: + ResizeTopLeft(0, dy); + break; + + case ResizeAnchor.Left: + ResizeTopLeft(dx, 0); + break; + + case ResizeAnchor.BottomLeft: + ResizeBottomLeft(dx, dy); + break; + + case ResizeAnchor.TopRight: + ResizeTopRight(dx, dy); + break; + } + } + + /// <summary> + /// Determines resizing anchor and sets appropriate Cursor. + /// </summary> + /// <param name="p">Coordinates of the mouse pointer.</param> + public void SetCursor(Point p) + { + if (p.Y < ResizingArea && p.X < ResizingArea) + { + anchor = ResizeAnchor.TopLeft; + element.Cursor = Cursors.SizeNWSE; + } + else if (p.Y < ResizingArea && p.X >= (element.ActualWidth - ResizingArea)) + { + anchor = ResizeAnchor.TopRight; + element.Cursor = Cursors.SizeNESW; + } + else if (p.Y < ResizingArea) + { + if (CanResizeVertically) + { + anchor = ResizeAnchor.Top; + element.Cursor = Cursors.SizeNS; + } + } + else if (p.X < ResizingArea && p.Y >= (element.ActualHeight - ResizingArea)) + { + anchor = ResizeAnchor.BottomLeft; + element.Cursor = Cursors.SizeNESW; + } + else if (p.X < ResizingArea) + { + if (CanResizeHorizontally) + { + anchor = ResizeAnchor.Left; + element.Cursor = Cursors.SizeWE; + } + } + else if (p.X >= (element.ActualWidth - ResizingArea) && p.Y >= (element.ActualHeight - ResizingArea)) + { + anchor = ResizeAnchor.BottomRight; + element.Cursor = Cursors.SizeNWSE; + } + else if (p.X >= (element.ActualWidth - ResizingArea)) + { + if (CanResizeHorizontally) + { + anchor = ResizeAnchor.Right; + element.Cursor = Cursors.SizeWE; + } + } + else if (p.Y >= (element.ActualHeight - ResizingArea)) + { + if (CanResizeVertically) + { + anchor = ResizeAnchor.Bottom; + element.Cursor = Cursors.SizeNS; + } + } + else + { + anchor = ResizeAnchor.None; + element.Cursor = null; + } + } + + /// <summary> + /// Calculates bounds for resizing. + /// </summary> + private void CalculateBoundsForResizing() + { + switch (anchor) + { + case ResizeAnchor.BottomRight: + case ResizeAnchor.Right: + case ResizeAnchor.Bottom: + bounds = GetBoundsForLowerRightCorner(); + break; + + case ResizeAnchor.TopLeft: + case ResizeAnchor.Top: + case ResizeAnchor.Left: + bounds = GetBoundsForUpperLeftCorner(); + break; + + case ResizeAnchor.BottomLeft: + bounds = GetBoundsForLowerLeftCorner(); + break; + + case ResizeAnchor.TopRight: + bounds = GetBoundsForUpperRightCorner(); + break; + } + } + + /// <summary> + /// Calculates bounds for resizing by the lower right corner. + /// </summary> + /// <returns>Bounding rectangle.</returns> + private Rect GetBoundsForLowerRightCorner() + { + double minWidth = (element.MinWidth != 0) ? element.MinWidth : MinResizedWidth; + double minHeight = element.MinHeight; + + double maxWidth = Host.ActualWidth - initialPosition.X; + + if (!element.MaxWidth.IsNotSet() && element.MaxWidth > 0) + maxWidth = Math.Min(maxWidth, element.MaxWidth); + + double maxHeight = Host.ActualHeight - initialPosition.Y; + if (!element.MaxHeight.IsNotSet() && element.MaxHeight > 0) + maxHeight = Math.Min(maxHeight, element.MaxHeight); + + Point p1 = initialPosition.Add(minWidth, minHeight); + Point p2 = initialPosition.Add(maxWidth, maxHeight); + + return new Rect(p1, p2); + } + + /// <summary> + /// Calculates bounds for resizing by the upper left corner. + /// </summary> + /// <returns>Bounding rectangle.</returns> + private Rect GetBoundsForUpperLeftCorner() + { + double minWidth = (element.MinWidth != 0) ? element.MinWidth : MinResizedWidth; + double minHeight = element.MinHeight; + + Point point = initialPosition.Add(initialSize); + double maxWidth = point.X; + double maxHeight = point.Y; + + if (!element.MaxWidth.IsNotSet() && element.MaxWidth > 0) + maxWidth = Math.Min(maxWidth, element.MaxWidth); + + if (!element.MaxHeight.IsNotSet() && element.MaxHeight > 0) + maxHeight = Math.Min(maxHeight, element.MaxHeight); + + Point p1 = point.Add(-maxWidth, -maxHeight); + Point p2 = point.Add(-minWidth, -minHeight); + + return new Rect(p1, p2); + } + + /// <summary> + /// Calculates bounds for resizing by the lower left corner. + /// </summary> + /// <returns>Bounding rectangle.</returns> + private Rect GetBoundsForLowerLeftCorner() + { + double minWidth = (element.MinWidth != 0) ? element.MinWidth : MinResizedWidth; + double minHeight = element.MinHeight; + + Point point = initialPosition.Add(initialSize.Width, 0); + double maxWidth = point.X; + + if (!element.MaxWidth.IsNotSet() && element.MaxWidth > 0) + maxWidth = Math.Min(maxWidth, element.MaxWidth); + + double maxHeight = Host.ActualHeight - initialPosition.Y; + if (!element.MaxHeight.IsNotSet() && element.MaxHeight > 0) + maxHeight = Math.Min(maxHeight, element.MaxHeight); + + Point p1 = point.Add(-maxWidth, minHeight); + Point p2 = point.Add(-minWidth, maxHeight); + + return new Rect(p1, p2); + } + + /// <summary> + /// Calculates bounds for resizing by the upper right corner. + /// </summary> + /// <returns>Bounding rectangle.</returns> + private Rect GetBoundsForUpperRightCorner() + { + double minWidth = (element.MinWidth != 0) ? element.MinWidth : MinResizedWidth; + double minHeight = element.MinHeight; + + Point point = initialPosition.Add(0, initialSize.Height); + double maxWidth = Host.ActualWidth - initialPosition.X; + + if (!element.MaxWidth.IsNotSet() && element.MaxWidth > 0) + maxWidth = Math.Min(maxWidth, element.MaxWidth); + + double maxHeight = point.Y; + if (!element.MaxHeight.IsNotSet() && element.MaxHeight > 0) + maxHeight = Math.Min(maxHeight, element.MaxHeight); + + Point p1 = point.Add(minWidth, -maxHeight); + Point p2 = point.Add(maxWidth, -minHeight); + + return new Rect(p1, p2); + } + + /// <summary> + /// Resizes the window by the bottom right corner of the window. + /// </summary> + /// <param name="dx">Increment by X-coordinate.</param> + /// <param name="dy">Increment by Y-coordinate.</param> + private void ResizeBottomRight(double dx, double dy) + { + Rect newBounds = new Rect(initialPosition, initialSize.Add(dx, dy)); + Point point = element.SnapinController.SnapBottomRightCorner(newBounds); + + // If only one coordinate was changed - restore the other after snap in + if (dx == 0) + point.X = newBounds.BottomRight().X; + + if (dy == 0) + point.Y = newBounds.BottomRight().Y; + + point = point.EnsureInBounds(bounds); + + element.Width = point.X - initialPosition.X; + element.Height = point.Y - initialPosition.Y; + } + + /// <summary> + /// Resizes the window by the top left corner of the window. + /// </summary> + /// <param name="dx">Increment by X-coordinate.</param> + /// <param name="dy">Increment by Y-coordinate.</param> + private void ResizeTopLeft(double dx, double dy) + { + Rect newBounds = new Rect(initialPosition.Add(dx, dy), initialSize); + Point point = element.SnapinController.SnapTopLeftCorner(newBounds).EnsureInBounds(bounds); + + // If only one coordinate was changed - restore the other after snap in + if (dx == 0) + point.X = newBounds.Position().X; + + if (dy == 0) + point.Y = newBounds.Position().Y; + + element.Position = point; + + Point lowerRight = initialPosition.Add(initialSize); + element.Width = lowerRight.X - point.X; + element.Height = lowerRight.Y - point.Y; + } + + /// <summary> + /// Resizes the window by the lower left corner of the window. + /// </summary> + /// <param name="dx">Increment by X-coordinate.</param> + /// <param name="dy">Increment by Y-coordinate.</param> + private void ResizeBottomLeft(double dx, double dy) + { + Rect newBounds = new Rect(initialPosition.Add(dx, 0), initialSize.Add(-dx, dy)); + + Point lowerLeft = element.SnapinController.SnapBottomLeftCorner(newBounds).EnsureInBounds(bounds); + Point topLeft = new Point(lowerLeft.X, initialPosition.Y); + + element.Position = topLeft; + + element.Width = initialSize.Width + initialPosition.X - topLeft.X; + element.Height = lowerLeft.Y - initialPosition.Y; + } + + /// <summary> + /// Resizes the window by the top right corner of the window. + /// </summary> + /// <param name="dx">Increment by X-coordinate.</param> + /// <param name="dy">Increment by Y-coordinate.</param> + private void ResizeTopRight(double dx, double dy) + { + Rect newBounds = new Rect(initialPosition.Add(0, dy), initialSize.Add(dx, -dy)); + + Point topRight = element.SnapinController.SnapTopRightCorner(newBounds).EnsureInBounds(bounds); + Point topLeft = new Point(initialPosition.X, topRight.Y); + + element.Position = topLeft; + + element.Width = topRight.X - initialPosition.X; + element.Height = initialSize.Height + initialPosition.Y - topRight.Y; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/Controllers/SnapinController.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,333 @@ +using System.Collections.Generic; +using System.Windows; +using SilverFlow.Controls.Extensions; +using SilverFlow.Geometry; + +namespace SilverFlow.Controls.Controllers +{ + /// <summary> + /// Provides helpers methods for calculating window position + /// during movement and resizing. + /// </summary> + public class SnapinController : ISnapinController + { + /// <summary> + /// Distance on which snapping works. + /// </summary> + private const double SnapinDistanceDefaultValue = 5; + + /// <summary> + /// Gets or sets a value indicating whether snap in is enabled. + /// </summary> + /// <value><c>true</c> if snap in is enabled; otherwise, <c>false</c>.</value> + public bool SnapinEnabled { get; set; } + + /// <summary> + /// Gets or sets snap in bounds. + /// </summary> + /// <value>Snap in bounds.</value> + public IEnumerable<Rect> SnapinBounds { get; set; } + + /// <summary> + /// Gets or sets snap in distance. + /// </summary> + /// <value>Snap in distance.</value> + public double SnapinDistance { get; set; } + + /// <summary> + /// Gets or sets a value snap in margin - distance between adjacent edges. + /// </summary> + /// <value>Snap in margin.</value> + public double SnapinMargin { get; set; } + + /// <summary> + /// Gets calculations accuracy. + /// </summary> + /// <value>Accuracy.</value> + private double Accuracy + { + get { return SnapinDistance + SnapinMargin; } + } + + /// <summary> + /// Returns new position of the specified rectangle + /// taking into account bounds the rectangle can be attracted to. + /// </summary> + /// <param name="rect">The rectangle.</param> + /// <returns>New position.</returns> + public Point SnapRectangle(Rect rect) + { + Point point = rect.Position(); + + if (SnapinEnabled) + { + Distance minDistance = new Distance(); + foreach (var bound in SnapinBounds) + { + Distance distance = DistanceBetweenRectangles(rect, bound); + minDistance = Distance.Min(distance, minDistance); + } + + point = point.Add(minDistance); + } + + return point; + } + + /// <summary> + /// Snaps the bottom right corner of the specified rectangle to the nearest bounds. + /// </summary> + /// <param name="rect">The rectangle.</param> + /// <returns>New position.</returns> + public Point SnapBottomRightCorner(Rect rect) + { + Point point = rect.BottomRight(); + + if (SnapinEnabled) + { + Distance minDistance = new Distance(); + foreach (var bound in SnapinBounds) + { + Distance distance = new Distance() + { + X = DistanceBetweenRightEdgeAndRectangle(rect, bound), + Y = DistanceBetweenBottomEdgeAndRectangle(rect, bound) + }; + + minDistance = Distance.Min(distance, minDistance); + } + + point = point.Add(minDistance); + } + + return point; + } + + /// <summary> + /// Snaps the upper left corner of the specified rectangle to the nearest bounds. + /// </summary> + /// <param name="rect">The rectangle.</param> + /// <returns>New position.</returns> + public Point SnapTopLeftCorner(Rect rect) + { + Point point = rect.Position(); + + if (SnapinEnabled) + { + Distance minDistance = new Distance(); + foreach (var bound in SnapinBounds) + { + Distance distance = new Distance() + { + X = DistanceBetweenLeftEdgeAndRectangle(rect, bound), + Y = DistanceBetweenTopEdgeAndRectangle(rect, bound) + }; + + minDistance = Distance.Min(distance, minDistance); + } + + point = point.Add(minDistance); + } + + return point; + } + + /// <summary> + /// Snaps the lower left corner of the specified rectangle to the nearest bounds. + /// </summary> + /// <param name="rect">The rectangle.</param> + /// <returns>New position.</returns> + public Point SnapBottomLeftCorner(Rect rect) + { + Point point = rect.BottomLeft(); + + if (SnapinEnabled) + { + Distance minDistance = new Distance(); + foreach (var bound in SnapinBounds) + { + Distance distance = new Distance() + { + X = DistanceBetweenLeftEdgeAndRectangle(rect, bound), + Y = DistanceBetweenBottomEdgeAndRectangle(rect, bound) + }; + + minDistance = Distance.Min(distance, minDistance); + } + + point = point.Add(minDistance); + } + + return point; + } + + /// <summary> + /// Snaps the upper right corner of the specified rectangle to the nearest bounds. + /// </summary> + /// <param name="rect">The rectangle.</param> + /// <returns>New position.</returns> + public Point SnapTopRightCorner(Rect rect) + { + Point point = rect.TopRight(); + + if (SnapinEnabled) + { + Distance minDistance = new Distance(); + foreach (var bound in SnapinBounds) + { + Distance distance = new Distance() + { + X = DistanceBetweenRightEdgeAndRectangle(rect, bound), + Y = DistanceBetweenTopEdgeAndRectangle(rect, bound) + }; + + minDistance = Distance.Min(distance, minDistance); + } + + point = point.Add(minDistance); + } + + return point; + } + + /// <summary> + /// Returns mininal distance between two rectangles. + /// </summary> + /// <param name="first">First rectangle.</param> + /// <param name="second">Second rectangle.</param> + /// <returns>Minimal distance.</returns> + private Distance DistanceBetweenRectangles(Rect first, Rect second) + { + double x1 = DistanceBetweenRightEdgeAndRectangle(first, second); + double x2 = DistanceBetweenLeftEdgeAndRectangle(first, second); + double y1 = DistanceBetweenBottomEdgeAndRectangle(first, second); + double y2 = DistanceBetweenTopEdgeAndRectangle(first, second); + + Distance distance = new Distance() + { + X = MathExtensions.AbsMin(x1, x2), + Y = MathExtensions.AbsMin(y1, y2) + }; + + return distance; + } + + /// <summary> + /// Returns distance between the right edge of the rectangle and another rectangle. + /// </summary> + /// <param name="first">First rectangle.</param> + /// <param name="second">Second rectangle.</param> + /// <returns>Minimal distance.</returns> + private double DistanceBetweenRightEdgeAndRectangle(Rect first, Rect second) + { + double snap; + double distance = double.NaN; + double x = first.X + first.Width - 1; + + if (first.OverlapsVertically(second, Accuracy)) + { + snap = second.X - 1 - SnapinMargin; + if (x.IsNear(snap, SnapinDistance)) + { + distance = MathExtensions.AbsMin(snap - x, distance); + } + + snap = second.X + second.Width - 1; + if (x.IsNear(snap, SnapinDistance)) + { + distance = MathExtensions.AbsMin(snap - x, distance); + } + } + + return distance; + } + + /// <summary> + /// Returns distance between the left edge of the rectangle and another rectangle. + /// </summary> + /// <param name="first">First rectangle.</param> + /// <param name="second">Second rectangle.</param> + /// <returns>Minimal distance.</returns> + private double DistanceBetweenLeftEdgeAndRectangle(Rect first, Rect second) + { + double snap; + double distance = double.NaN; + + if (first.OverlapsVertically(second, Accuracy)) + { + snap = second.X; + if (first.X.IsNear(snap, SnapinDistance)) + { + distance = MathExtensions.AbsMin(snap - first.X, distance); + } + + snap = second.X + second.Width + SnapinMargin; + if (first.X.IsNear(snap, SnapinDistance)) + { + distance = MathExtensions.AbsMin(snap - first.X, distance); + } + } + + return distance; + } + + /// <summary> + /// Returns distance between the bottom edge of the rectangle and another rectangle. + /// </summary> + /// <param name="first">First rectangle.</param> + /// <param name="second">Second rectangle.</param> + /// <returns>Minimal distance.</returns> + private double DistanceBetweenBottomEdgeAndRectangle(Rect first, Rect second) + { + double snap; + double distance = double.NaN; + double y = first.Y + first.Height - 1; + + if (first.OverlapsHorizontally(second, Accuracy)) + { + snap = second.Y - 1 - SnapinMargin; + if (y.IsNear(snap, SnapinDistance)) + { + distance = MathExtensions.AbsMin(snap - y, distance); + } + + snap = second.Y + second.Height - 1; + if (y.IsNear(snap, SnapinDistance)) + { + distance = MathExtensions.AbsMin(snap - y, distance); + } + } + + return distance; + } + + /// <summary> + /// Returns distance between the top edge of the rectangle and another rectangle. + /// </summary> + /// <param name="first">First rectangle.</param> + /// <param name="second">Second rectangle.</param> + /// <returns>Minimal distance.</returns> + private double DistanceBetweenTopEdgeAndRectangle(Rect first, Rect second) + { + double snap; + double distance = double.NaN; + + if (first.OverlapsHorizontally(second, Accuracy)) + { + snap = second.Y; + if (first.Y.IsNear(snap, SnapinDistance)) + { + distance = MathExtensions.AbsMin(snap - first.Y, distance); + } + + snap = second.Y + second.Height + SnapinMargin; + if (first.Y.IsNear(snap, SnapinDistance)) + { + distance = MathExtensions.AbsMin(snap - first.Y, distance); + } + } + + return distance; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/Converters/BooleanToVisibilityConverter.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,32 @@ +using System; +using System.Windows; +using System.Windows.Data; + +namespace SilverFlow.Controls.Converters +{ + public class BooleanToVisibilityConverter : IValueConverter + { + /// <summary> + /// Converts a boolean value to the display state of an element. + /// </summary> + /// <param name="value">The source data being passed to the target.</param> + /// <param name="targetType">The <see cref="T:System.Type"/> of data expected by the target dependency property.</param> + /// <param name="parameter">An optional parameter to be used in the converter logic.</param> + /// <param name="culture">The culture of the conversion.</param> + /// <returns> + /// The value to be passed to the target dependency property. + /// </returns> + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (value == null) + throw new ArgumentNullException("value"); + + return (bool)value ? Visibility.Visible : Visibility.Collapsed; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/Enums/ResizeAnchor.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,19 @@ +namespace SilverFlow.Controls.Enums +{ + /// <summary> + /// Defines window's border or a corner where the user + /// clicked the mouse to start resizing. + /// </summary> + public enum ResizeAnchor + { + None = 0, + TopLeft = 1, + Top = 2, + TopRight = 3, + Right = 4, + BottomRight = 5, + Bottom = 6, + BottomLeft = 7, + Left = 9 + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/Enums/WindowAction.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,23 @@ +namespace SilverFlow.Controls.Enums +{ + /// <summary> + /// Defines mouse action. + /// </summary> + internal enum WindowAction + { + /// <summary> + /// Default + /// </summary> + None = 0, + + /// <summary> + /// Moving the window + /// </summary> + Move = 1, + + /// <summary> + /// Resizing the window + /// </summary> + Resize = 2 + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/Extensions/AnimationExtensions.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,163 @@ +using System; +using System.Windows; +using System.Windows.Media.Animation; + +namespace SilverFlow.Controls.Extensions +{ + /// <summary> + /// Useful Silverlight animation extensions + /// </summary> + public static class AnimationExtensions + { + /// <summary> + /// Animates specified property of the object. + /// </summary> + /// <param name="target">The target object to animate.</param> + /// <param name="propertyPath">Property path, e.g. Canvas.Top.</param> + /// <param name="from">Animation's starting value.</param> + /// <param name="to">Animation's ending value.</param> + /// <param name="milliseconds">Duration of the animation in milliseconds.</param> + /// <param name="easingFunction">Easing function applied to the animation.</param> + /// <param name="completed">Event handler called when animation completed.</param> + /// <returns>Returns started storyboard.</returns> + public static Storyboard AnimateDoubleProperty(this DependencyObject target, string propertyPath, double? from, double? to, double milliseconds, + IEasingFunction easingFunction = null, EventHandler completed = null) + { + Duration duration = new Duration(TimeSpan.FromMilliseconds(milliseconds)); + DoubleAnimation doubleAnimation = new DoubleAnimation() + { + From = from, + To = to, + Duration = duration, + EasingFunction = easingFunction + }; + + Storyboard storyboard = new Storyboard(); + storyboard.Duration = duration; + storyboard.Children.Add(doubleAnimation); + + Storyboard.SetTarget(doubleAnimation, target); + Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath(propertyPath)); + + if (completed != null) + storyboard.Completed += completed; + + storyboard.Begin(); + + return storyboard; + } + + /// <summary> + /// Fade in the specified object. + /// </summary> + /// <param name="target">The target object.</param> + /// <param name="milliseconds">Duration of the animation in milliseconds.</param> + /// <param name="completed">Event handler called when animation completed.</param> + /// <returns>Returns started storyboard.</returns> + public static Storyboard FadeIn(this DependencyObject target, double milliseconds, EventHandler completed = null) + { + return AnimateDoubleProperty(target, "Opacity", 0, 1, milliseconds, null, completed); + } + + /// <summary> + /// Fade out the specified object. + /// </summary> + /// <param name="target">The target object.</param> + /// <param name="milliseconds">Duration of the animation in milliseconds.</param> + /// <param name="completed">Event handler called when animation completed.</param> + /// <returns>Returns started storyboard.</returns> + public static Storyboard FadeOut(this DependencyObject target, double milliseconds, EventHandler completed = null) + { + return AnimateDoubleProperty(target, "Opacity", 1, 0, milliseconds, null, completed); + } + + /// <summary> + /// Moves and resizes the object. + /// </summary> + /// <param name="target">The target object.</param> + /// <param name="position">Ending position.</param> + /// <param name="width">New width.</param> + /// <param name="height">New height.</param> + /// <param name="milliseconds">Duration of the animation in milliseconds.</param> + /// <param name="completed">Event handler called when animation completed.</param> + public static void MoveAndResize(this DependencyObject target, Point position, double width, double height, + double milliseconds, EventHandler completed = null) + { + Duration duration = new Duration(TimeSpan.FromMilliseconds(milliseconds)); + DoubleAnimation doubleAnimation1 = new DoubleAnimation(); + DoubleAnimation doubleAnimation2 = new DoubleAnimation(); + PointAnimation pointAnimation = new PointAnimation(); + + doubleAnimation1.Duration = duration; + doubleAnimation2.Duration = duration; + pointAnimation.Duration = duration; + + Storyboard storyboard = new Storyboard(); + storyboard.Duration = duration; + + storyboard.Children.Add(doubleAnimation1); + storyboard.Children.Add(doubleAnimation2); + storyboard.Children.Add(pointAnimation); + + Storyboard.SetTarget(doubleAnimation1, target); + Storyboard.SetTarget(doubleAnimation2, target); + Storyboard.SetTarget(pointAnimation, target); + + Storyboard.SetTargetProperty(doubleAnimation1, new PropertyPath("(Width)")); + Storyboard.SetTargetProperty(doubleAnimation2, new PropertyPath("(Height)")); + Storyboard.SetTargetProperty(pointAnimation, new PropertyPath("(Position)")); + + doubleAnimation1.To = width; + if (!height.IsNotSet()) + doubleAnimation2.To = height; + + pointAnimation.To = position; + + if (completed != null) + storyboard.Completed += completed; + + storyboard.Begin(); + } + + /// <summary> + /// Animates the translate transform object, responsible for displacement of the target. + /// </summary> + /// <param name="target">The target object.</param> + /// <param name="storyboard">The storyboard.</param> + /// <param name="to">Animation's ending value.</param> + /// <param name="seconds">Duration of the animation in seconds.</param> + /// <param name="easingFunction">Easing function applied to the animation.</param> + public static void AnimateTranslateTransform(this DependencyObject target, Storyboard storyboard, Point to, + double seconds, IEasingFunction easingFunction = null) + { + Duration duration = new Duration(TimeSpan.FromSeconds(seconds)); + + DoubleAnimation doubleAnimationX = new DoubleAnimation() + { + To = to.X, + Duration = duration, + EasingFunction = easingFunction + }; + + DoubleAnimation doubleAnimationY = new DoubleAnimation() + { + To = to.Y, + Duration = duration, + EasingFunction = easingFunction + }; + + storyboard.Stop(); + storyboard.Children.Clear(); + storyboard.Duration = duration; + storyboard.Children.Add(doubleAnimationX); + storyboard.Children.Add(doubleAnimationY); + + Storyboard.SetTarget(doubleAnimationX, target); + Storyboard.SetTarget(doubleAnimationY, target); + Storyboard.SetTargetProperty(doubleAnimationX, (target as UIElement).GetPropertyPathForTranslateTransformX()); + Storyboard.SetTargetProperty(doubleAnimationY, (target as UIElement).GetPropertyPathForTranslateTransformY()); + + storyboard.Begin(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/Extensions/ControlExtensions.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,230 @@ +using System; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; + +namespace SilverFlow.Controls.Extensions +{ + /// <summary> + /// Useful Silverlight controls extensions + /// </summary> + public static class ControlExtensions + { + /// <summary> + /// Sets the visible of the UIElement. + /// </summary> + /// <param name="uiElement">The UI element.</param> + /// <param name="visible">Set Visible if <c>true</c>; otherwise - Collapsed.</param> + public static void SetVisible(this UIElement uiElement, bool visible = true) + { + if (uiElement != null) + { + uiElement.Visibility = (visible) ? Visibility.Visible : Visibility.Collapsed; + } + } + + /// <summary> + /// Determines whether the specified UI element is visible. + /// </summary> + /// <param name="uiElement">The UI element.</param> + /// <returns><c>true</c> if the specified UI element is visible; otherwise, <c>false</c>.</returns> + public static bool IsVisible(this UIElement uiElement) + { + return uiElement != null && uiElement.Visibility == Visibility.Visible; + } + + /// <summary> + /// Determines whether the specified element contains the point. + /// </summary> + /// <param name="element">This FrameworkElement.</param> + /// <param name="point">The point to check.</param> + /// <param name="origin">Relative origin (optional).</param> + /// <returns><c>true</c> if the specified element contains the point; otherwise, <c>false</c>.</returns> + public static bool ContainsPoint(this FrameworkElement element, Point point, UIElement origin = null) + { + bool result = false; + + if (element != null) + { + Point elementOrigin = (origin == null) ? new Point(0, 0) : element.GetRelativePosition(origin); + Rect rect = new Rect(elementOrigin, new Size(element.ActualWidth, element.ActualHeight)); + result = rect.Contains(point); + } + + return result; + } + + /// <summary> + /// Gets position of the element relative to another specified (root) element. + /// </summary> + /// <param name="element">The element.</param> + /// <param name="rootElement">The root element.</param> + /// <returns>Relative position.</returns> + /// <exception cref="ArgumentNullException">Input parameter is null.</exception> + public static Point GetRelativePosition(this UIElement element, UIElement rootElement) + { + if (element == null) + throw new ArgumentNullException("element"); + + if (rootElement == null) + throw new ArgumentNullException("rootElement"); + + var transformFromClientToRoot = element.TransformToVisual(rootElement); + return transformFromClientToRoot.Transform(new Point(0, 0)); + } + + /// <summary> + /// Gets total horizontal value of the specified thickness. + /// </summary> + /// <param name="thickness">The thickness.</param> + /// <returns>Horizontal thickness.</returns> + /// <exception cref="ArgumentNullException">Input parameter is null.</exception> + public static double Horizontal(this Thickness thickness) + { + if (thickness == null) + throw new ArgumentNullException("thickness"); + + return thickness.Left + thickness.Right; + } + + /// <summary> + /// Gets total vertical value of the specified thickness. + /// </summary> + /// <param name="thickness">The thickness.</param> + /// <returns>Vertical thickness.</returns> + /// <exception cref="ArgumentNullException">Input parameter is null.</exception> + public static double Vertical(this Thickness thickness) + { + if (thickness == null) + throw new ArgumentNullException("thickness"); + + return thickness.Top + thickness.Bottom; + } + + /// <summary> + /// Gets the actual bounding rectangle of the FrameworkElement. + /// </summary> + /// <param name="element">The element.</param> + /// <returns>Actual bounding rectangle.</returns> + public static Rect GetActualBoundingRectangle(this FrameworkElement element) + { + if (element == null) + throw new ArgumentNullException("element"); + + return new Rect(0, 0, element.ActualWidth, element.ActualHeight); + } + + /// <summary> + /// Gets a PropertyPath for the TranslateTransform.X property. + /// </summary> + /// <param name="element">The element.</param> + /// <returns>A path to the property.</returns> + public static PropertyPath GetPropertyPathForTranslateTransformX(this UIElement element) + { + return element.GetPropertyPathForTranslateTransform("X"); + } + + /// <summary> + /// Gets a PropertyPath for the TranslateTransform.Y property. + /// </summary> + /// <param name="element">The element.</param> + /// <returns>A path to the property.</returns> + public static PropertyPath GetPropertyPathForTranslateTransformY(this UIElement element) + { + return element.GetPropertyPathForTranslateTransform("Y"); + } + + /// <summary> + /// Gets scale factor to fit the element to the specified size. + /// </summary> + /// <param name="element">The element to scale.</param> + /// <param name="maxWidth">Maximal width of the element.</param> + /// <param name="maxHeight">Maximal height of the element.</param> + /// <returns>Scale factor required to fit the element to the specified size.</returns> + public static double GetScaleFactorToFit(this FrameworkElement element, double maxWidth, double maxHeight) + { + if (element == null) + throw new ArgumentNullException("element"); + + double width = element.Width.IsNotSet() ? element.ActualWidth : element.Width; + double height = element.Height.IsNotSet() ? element.ActualHeight : element.Height; + + double scaleX = maxWidth / width; + double scaleY = maxHeight / height; + double minScale = Math.Min(scaleX, scaleY); + + return minScale; + } + + /// <summary> + /// Moves the element to the new container. The container can be a Panel or a Border derived class. + /// </summary> + /// <param name="element">The element.</param> + /// <param name="newContainer">The new container of the element.</param> + /// <exception cref="ArgumentNullException">New Container is null.</exception> + public static void MoveToContainer(this FrameworkElement element, FrameworkElement newContainer) + { + if (element == null) + throw new ArgumentNullException("element"); + + if (newContainer == null) + throw new ArgumentNullException("newContainer"); + + // Remove the element from the old container + element.RemoveFromContainer(); + + if (newContainer is Panel) + { + var container = newContainer as Panel; + if (!container.Children.Contains(element)) + container.Children.Add(element); + } + else if (newContainer is Border) + { + (newContainer as Border).Child = element; + } + } + + /// <summary> + /// Removes the element from its container. The container can be a Panel or a Border derived class. + /// </summary> + /// <param name="element">The element.</param> + public static void RemoveFromContainer(this FrameworkElement element) + { + if (element != null && element.Parent != null) + { + // Remove the element from the old container + if (element.Parent is Panel) + { + (element.Parent as Panel).Children.Remove(element); + } + else if (element.Parent is Border) + { + (element.Parent as Border).Child = null; + } + } + } + + /// <summary> + /// Gets a PropertyPath for the TranslateTransform property. + /// </summary> + /// <param name="element">The element.</param> + /// <param name="translateBy">"X" or "Y" TranslateTransform property.</param> + /// <returns>A path to the property.</returns> + private static PropertyPath GetPropertyPathForTranslateTransform(this UIElement element, string translateBy) + { + if (element == null) + throw new ArgumentNullException("element"); + + var transformGroup = element.RenderTransform as TransformGroup; + var translateTransform = transformGroup.Children.OfType<TranslateTransform>().FirstOrDefault(); + int translateTransformIndex = transformGroup.Children.IndexOf(translateTransform); + + string path = string.Format("(FrameworkElement.RenderTransform).(TransformGroup.Children)[{0}].(TranslateTransform.{1})", + translateTransformIndex, translateBy); + + return new PropertyPath(path); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/Extensions/GeometryExtensions.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,292 @@ +using System; +using System.Windows; +using SilverFlow.Geometry; + +namespace SilverFlow.Controls.Extensions +{ + /// <summary> + /// Geometry extensions + /// </summary> + public static class GeometryExtensions + { + /// <summary> + /// Rounds the specified point to the nearest integer coordinates. + /// </summary> + /// <param name="point">The point to round coordinates.</param> + /// <returns>New point with rounded coordinates.</returns> + public static Point Round(this Point point) + { + return new Point(Math.Round(point.X), Math.Round(point.Y)); + } + + /// <summary> + /// Ensures that coordinates are positive. + /// </summary> + /// <param name="point">The point.</param> + /// <returns>Point with positive coordinates.</returns> + public static Point EnsurePositive(this Point point) + { + return new Point() + { + X = (point.X < 0) ? 0 : point.X, + Y = (point.Y < 0) ? 0 : point.Y + }; + } + + /// <summary> + /// Gets position of the specified rectangle. + /// </summary> + /// <param name="rect">Rectangle.</param> + /// <returns>Upper left corner of the rectangle.</returns> + public static Point Position(this Rect rect) + { + return new Point(rect.X, rect.Y); + } + + /// <summary> + /// Gets position the lower right corner of the rectangle. + /// </summary> + /// <param name="rect">Rectangle.</param> + /// <returns>Lower right corner of the rectangle.</returns> + public static Point BottomRight(this Rect rect) + { + return new Point(rect.Right, rect.Bottom); + } + + /// <summary> + /// Gets position the lower left corner of the rectangle. + /// </summary> + /// <param name="rect">Rectangle.</param> + /// <returns>Lower left corner of the rectangle.</returns> + public static Point BottomLeft(this Rect rect) + { + return new Point(rect.X, rect.Bottom); + } + + /// <summary> + /// Gets position the upper right corner of the rectangle. + /// </summary> + /// <param name="rect">Rectangle.</param> + /// <returns>Upper right corner of the rectangle.</returns> + public static Point TopRight(this Rect rect) + { + return new Point(rect.Right, rect.Y); + } + + /// <summary> + /// Ensures that coordinates of the point are in the specified bounds. + /// </summary> + /// <param name="point">The point.</param> + /// <param name="bounds">The bounds.</param> + /// <returns>Point within the boinds.</returns> + public static Point EnsureInBounds(this Point point, Rect bounds) + { + return new Point() + { + X = point.X.EnsureInRange(bounds.X, bounds.Right), + Y = point.Y.EnsureInRange(bounds.Y, bounds.Bottom) + }; + } + + /// <summary> + /// Ensures that the X-coordinate of the point is in the specified horizontal bounds. + /// </summary> + /// <param name="point">The point.</param> + /// <param name="bounds">The bounds.</param> + /// <returns>Point with the X-coordinate within the boinds.</returns> + public static Point EnsureInHorizontalBounds(this Point point, Rect bounds) + { + return new Point() + { + X = point.X.EnsureInRange(bounds.X, bounds.Right), + Y = point.Y + }; + } + + /// <summary> + /// Ensures that the Y-coordinate of the point is in the specified vertical bounds. + /// </summary> + /// <param name="point">The point.</param> + /// <param name="bounds">The bounds.</param> + /// <returns>Point with the Y-coordinate within the boinds.</returns> + public static Point EnsureInVerticalBounds(this Point point, Rect bounds) + { + return new Point() + { + X = point.X, + Y = point.Y.EnsureInRange(bounds.Y, bounds.Bottom) + }; + } + + /// <summary> + /// Determines whether coordinates of the point are Not a Number (NaN). + /// </summary> + /// <param name="point">The point.</param> + /// <returns> + /// <c>true</c> if coordinates are not specified; otherwise, <c>false</c>. + /// </returns> + public static bool IsNotSet(this Point point) + { + return double.IsNaN(point.X) || double.IsNaN(point.Y); + } + + /// <summary> + /// Adds an offset to the specified point. + /// </summary> + /// <param name="point">The point.</param> + /// <param name="x">Distance along X-coordinate.</param> + /// <param name="y">Distance along Y-coordinate.</param> + /// <returns>New point.</returns> + public static Point Add(this Point point, double x, double y) + { + return new Point(point.X + x, point.Y + y); + } + + /// <summary> + /// Adds an offset specified by the Size to the specified point. + /// </summary> + /// <param name="point">The point.</param> + /// <param name="size">Distance to add.</param> + /// <returns>New point</returns> + public static Point Add(this Point point, Size size) + { + return new Point(point.X + size.Width, point.Y + size.Height); + } + + /// <summary> + /// Determines whether dimensions are Not a Number (NaN). + /// </summary> + /// <param name="size">The size.</param> + /// <returns> + /// <c>true</c> if dimensions are not specified; otherwise, <c>false</c>. + /// </returns> + public static bool IsNotSet(this Size size) + { + return double.IsNaN(size.Width) || double.IsNaN(size.Height); + } + + /// <summary> + /// Increments size to the specified values. + /// </summary> + /// <param name="size">The size.</param> + /// <param name="x">Increment by X-coordinate.</param> + /// <param name="y">Increment by Y-coordinate.</param> + /// <returns>New size.</returns> + public static Size Add(this Size size, double x, double y) + { + double width = size.Width + x; + double height = size.Height + y; + + return new Size() + { + Width = width < 0 ? 0 : width, + Height = height < 0 ? 0 : height + }; + } + + /// <summary> + /// Increases size if the rectangle to the specified width and height. + /// </summary> + /// <param name="rect">The rectangle.</param> + /// <param name="width">Value to add to the width of the rectangle.</param> + /// <param name="height">Value to add to the height of the rectangle.</param> + /// <returns>New rectangle.</returns> + public static Rect Add(this Rect rect, double width, double height) + { + return new Rect + { + X = rect.X, + Y = rect.Y, + Width = Math.Max(0, rect.Width + width), + Height = Math.Max(0, rect.Height + height) + }; + } + + /// <summary> + /// Shifts the point to the specified distance. + /// </summary> + /// <param name="point">The point.</param> + /// <param name="distance">The distance.</param> + /// <returns>Point shifted to the specified distance.</returns> + public static Point Add(this Point point, Distance distance) + { + return new Point() + { + X = distance.X.IsNotSet() ? point.X : point.X + distance.X, + Y = distance.Y.IsNotSet() ? point.Y : point.Y + distance.Y + }; + } + + /// <summary> + /// Tests whether the rectangle overlaps with another one vertically. + /// </summary> + /// <param name="rect">The rectangle.</param> + /// <param name="testRect">Test rectangle.</param> + /// <param name="accuracy">Accuracy.</param> + /// <returns><c>true</c> if overlaps vertically; otherwise, <c>false</c>.</returns> + public static bool OverlapsVertically(this Rect rect, Rect testRect, double accuracy = 0) + { + double y1 = rect.Y + rect.Height - 1; + double y2 = testRect.Y + testRect.Height - 1; + + if (rect.Y <= testRect.Y) + { + return y1 >= (testRect.Y - accuracy); + } + else if (rect.Y <= (y2 + accuracy)) + { + return true; + } + + return false; + } + + /// <summary> + /// Tests whether the rectangle overlaps with another one horizontally. + /// </summary> + /// <param name="rect">The rectangle.</param> + /// <param name="testRect">Test rectangle.</param> + /// <param name="accuracy">Accuracy.</param> + /// <returns><c>true</c> if overlaps horizontally; otherwise, <c>false</c>.</returns> + public static bool OverlapsHorizontally(this Rect rect, Rect testRect, double accuracy = 0) + { + double x1 = rect.X + rect.Width - 1; + double x2 = testRect.X + testRect.Width - 1; + + if (rect.X <= testRect.X) + { + return x1 >= (testRect.X - accuracy); + } + else if (rect.X <= (x2 + accuracy)) + { + return true; + } + + return false; + } + + /// <summary> + /// Tests whether the point overlaps with the rectangle vertically. + /// </summary> + /// <param name="point">Test point.</param> + /// <param name="rect">Test rectangle.</param> + /// <param name="accuracy">Accuracy.</param> + /// <returns><c>true</c> if overlaps vertically; otherwise, <c>false</c>.</returns> + public static bool OverlapsVertically(this Point point, Rect rect, double accuracy = 0) + { + return (point.Y >= (rect.Y - accuracy) && point.Y <= (rect.Y + rect.Height - 1 + accuracy)); + } + + /// <summary> + /// Tests whether the point overlaps with the rectangle horizontally. + /// </summary> + /// <param name="point">Test point.</param> + /// <param name="rect">Test rectangle.</param> + /// <param name="accuracy">Accuracy.</param> + /// <returns><c>true</c> if overlaps horizontally; otherwise, <c>false</c>.</returns> + public static bool OverlapsHorizontally(this Point point, Rect rect, double accuracy = 0) + { + return (point.X >= (rect.X - accuracy) && point.X <= (rect.X + rect.Width - 1 + accuracy)); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/Extensions/MathExtensions.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,85 @@ +using System; + +namespace SilverFlow.Controls.Extensions +{ + /// <summary> + /// Math extensions + /// </summary> + public static class MathExtensions + { + /// <summary> + /// Determines whether the number is NaN. + /// </summary> + /// <param name="number">The number.</param> + /// <returns> + /// <c>true</c> if is NaN; otherwise, <c>false</c>. + /// </returns> + public static bool IsNotSet(this double number) + { + return double.IsNaN(number); + } + + /// <summary> + /// Determines whether the specified number is near the test number. + /// </summary> + /// <param name="number">The number.</param> + /// <param name="testNumber">The test number.</param> + /// <param name="accuracy">Accuracy.</param> + /// <returns> + /// <c>true</c> if the specified number is near the testing one; otherwise, <c>false</c>. + /// </returns> + public static bool IsNear(this double number, double testNumber, double accuracy) + { + return number >= (testNumber - accuracy) && number <= (testNumber + accuracy); + } + + /// <summary> + /// Compares two numbers without their signs and gets absolutely minimal value. + /// </summary> + /// <param name="num1">First number.</param> + /// <param name="num2">Second number.</param> + /// <returns>Absolutely minimal value.</returns> + public static double AbsMin(double num1, double num2) + { + if (num1.IsNotSet() && num2.IsNotSet()) + return double.NaN; + + if (num1.IsNotSet()) return num2; + if (num2.IsNotSet()) return num1; + + double abs1 = Math.Abs(num1); + double abs2 = Math.Abs(num2); + + if (abs1 < abs2) return num1; + if (abs2 < abs1) return num2; + + // Abs. values are equal. Return first number + return num1; + } + + /// <summary> + /// Ensures the given number is in the specified range. + /// </summary> + /// <param name="number">The number.</param> + /// <param name="low">The lower limit.</param> + /// <param name="high">The upper limit.</param> + /// <returns>A number in the specified range.</returns> + public static double EnsureInRange(this double number, double low, double high) + { + if (number.IsNotSet()) return double.NaN; + + double result = Math.Max(number, low); + return Math.Min(result, high); + } + + /// <summary> + /// Returns a double value if it is not a NAN, or zero. + /// </summary> + /// <param name="number">The number.</param> + /// <returns>Double value if it is not a NAN, or zero.</returns> + public static double ValueOrZero(this double number) + { + return double.IsNaN(number) ? 0 : number; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/FloatingWindow/ActiveWindowChangedEventArgs.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,31 @@ +using System; + +namespace SilverFlow.Controls +{ + /// <summary> + /// ActiveWindowChanged event arguments class. + /// </summary> + public class ActiveWindowChangedEventArgs : EventArgs + { + public FloatingWindow Old { get; set; } + public FloatingWindow New { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="ActiveWindowChangedEventArgs"/> class. + /// </summary> + public ActiveWindowChangedEventArgs() + { + } + + /// <summary> + /// Initializes a new instance of the <see cref="ActiveWindowChangedEventArgs"/> class. + /// </summary> + /// <param name="oldWindow">The old active FloatingWindow.</param> + /// <param name="newWindow">The new active FloatingWindow.</param> + public ActiveWindowChangedEventArgs(FloatingWindow oldWindow, FloatingWindow newWindow) + { + this.Old = oldWindow; + this.New = newWindow; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/FloatingWindow/BootstrapButton.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,72 @@ +using System.Windows; +using System.Windows.Controls; + +namespace SilverFlow.Controls +{ + /// <summary> + /// Two-state button with an up/down arrow. + /// </summary> + [TemplateVisualState(Name = VSMSTATE_StateClose, GroupName = VSMGROUP_ButtonStates)] + [TemplateVisualState(Name = VSMSTATE_StateOpen, GroupName = VSMGROUP_ButtonStates)] + public class BootstrapButton : Button + { + // VSM groups + private const string VSMGROUP_ButtonStates = "ButtonStates"; + + // VSM states + private const string VSMSTATE_StateOpen = "Open"; + private const string VSMSTATE_StateClose = "Close"; + + #region public bool IsOpen + + /// <summary> + /// Gets or sets a value indicating whether the bootstrap button is in the "Open" state. + /// </summary> + /// <value><c>true</c> if the bootstrap button is in the "Open" state; otherwise, <c>false</c>.</value> + public bool IsOpen + { + get { return (bool)GetValue(IsOpenProperty); } + set { SetValue(IsOpenProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="BootstrapButton.IsOpen" /> dependency property. + /// </summary> + /// <value> + /// The identifier for the <see cref="BootstrapButton.IsOpen" /> dependency property. + /// </value> + public static readonly DependencyProperty IsOpenProperty = + DependencyProperty.Register( + "IsOpen", + typeof(bool), + typeof(BootstrapButton), + new PropertyMetadata(false, IsOpenPropertyChanged)); + + /// <summary> + /// IsOpenProperty PropertyChangedCallback call back static function. + /// </summary> + /// <param name="d">BootstrapButton object whose IsOpenProperty property is changed.</param> + /// <param name="e">DependencyPropertyChangedEventArgs which contains the old and new values.</param> + private static void IsOpenPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + BootstrapButton button = (BootstrapButton)d; + VisualStateManager.GoToState( + button, + (bool)e.NewValue ? VSMSTATE_StateClose : VSMSTATE_StateOpen, + true); + } + + #endregion + + /// <summary> + /// Raises the <see cref="E:System.Windows.Controls.Primitives.ButtonBase.Click"/> event. + /// </summary> + protected override void OnClick() + { + // Toggle open/closed state + IsOpen = !IsOpen; + + base.OnClick(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/FloatingWindow/FloatingWindow.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,2210 @@ +using System; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using SilverFlow.Controls.Controllers; +using SilverFlow.Controls.Enums; +using SilverFlow.Controls.Extensions; +using SilverFlow.Controls.Helpers; + +namespace SilverFlow.Controls +{ + /// <summary> + /// Provides a window that can be displayed over a parent window not blocking + /// interaction with the parent window. + /// </summary> + [TemplatePart(Name = PART_Chrome, Type = typeof(FrameworkElement))] + [TemplatePart(Name = PART_TitleContent, Type = typeof(ContentControl))] + [TemplatePart(Name = PART_CloseButton, Type = typeof(ButtonBase))] + [TemplatePart(Name = PART_ContentPresenter, Type = typeof(FrameworkElement))] + [TemplatePart(Name = PART_ContentRoot, Type = typeof(FrameworkElement))] + [TemplatePart(Name = PART_ContentBorder, Type = typeof(FrameworkElement))] + [TemplatePart(Name = PART_Root, Type = typeof(FrameworkElement))] + [TemplateVisualState(Name = VSMSTATE_StateClosed, GroupName = VSMGROUP_Window)] + [TemplateVisualState(Name = VSMSTATE_StateOpen, GroupName = VSMGROUP_Window)] + [TemplateVisualState(Name = VSMSTATE_StateMinimized, GroupName = VSMGROUP_Window)] + [TemplateVisualState(Name = VSMSTATE_StateRestored, GroupName = VSMGROUP_Window)] + [TemplateVisualState(Name = VSMSTATE_StateNormal, GroupName = VSMGROUP_Button)] + [StyleTypedProperty(Property = PROPERTY_TitleStyle, StyleTargetType = typeof(ContentControl))] + [StyleTypedProperty(Property = PROPERTY_CloseButtonStyle, StyleTargetType = typeof(Button))] + [StyleTypedProperty(Property = PROPERTY_MinimizeButtonStyle, StyleTargetType = typeof(Button))] + [StyleTypedProperty(Property = PROPERTY_MaximizeButtonStyle, StyleTargetType = typeof(Button))] + [StyleTypedProperty(Property = PROPERTY_RestoreButtonStyle, StyleTargetType = typeof(Button))] + public class FloatingWindow : ContentControl, IResizableElement, IDisposable + { + #region Constants + + // Template parts + private const string PART_Chrome = "Chrome"; + private const string PART_TitleContent = "TitleContent"; + private const string PART_CloseButton = "CloseButton"; + private const string PART_MaximizeButton = "MaximizeButton"; + private const string PART_RestoreButton = "RestoreButton"; + private const string PART_MinimizeButton = "MinimizeButton"; + private const string PART_ContentPresenter = "ContentPresenter"; + private const string PART_ContentRoot = "ContentRoot"; + private const string PART_ContentBorder = "ContentBorder"; + private const string PART_Root = "Root"; + + // VSM groups + private const string VSMGROUP_Window = "WindowStates"; + private const string VSMGROUP_Button = "CommonStates"; + + // VSM states + private const string VSMSTATE_StateNormal = "Normal"; + private const string VSMSTATE_StateClosed = "Closed"; + private const string VSMSTATE_StateOpen = "Open"; + private const string VSMSTATE_StateMinimized = "Minimized"; + private const string VSMSTATE_StateRestored = "Restored"; + + // Style typed properties + private const string PROPERTY_TitleStyle = "TitleStyle"; + private const string PROPERTY_CloseButtonStyle = "CloseButtonStyle"; + private const string PROPERTY_MinimizeButtonStyle = "MinimizeButtonStyle"; + private const string PROPERTY_MaximizeButtonStyle = "MaximizeButtonStyle"; + private const string PROPERTY_RestoreButtonStyle = "RestoreButtonStyle"; + + // Thickness of resizing area + private const double ResizingAreaDefaultValue = 6; + + // Animation duration in milliseconds + private const double MaximizingDurationInMilliseconds = 20; + private const double MinimizingDurationInMilliseconds = 200; + private const double RestoringDurationInMilliseconds = 20; + + #endregion + + #region public bool ShowCloseButton + + /// <summary> + /// Gets or sets a value indicating whether to show Close button. + /// </summary> + /// <value><c>true</c> if to show Close button; otherwise, <c>false</c>.</value> + public bool ShowCloseButton + { + get { return (bool)GetValue(ShowCloseButtonProperty); } + set { SetValue(ShowCloseButtonProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindow.ShowCloseButton" /> dependency property. + /// </summary> + /// <value> + /// The identifier for the <see cref="FloatingWindow.ShowCloseButton" /> dependency property. + /// </value> + public static readonly DependencyProperty ShowCloseButtonProperty = + DependencyProperty.Register( + "ShowCloseButton", + typeof(bool), + typeof(FloatingWindow), + new PropertyMetadata(true, OnShowCloseButtonPropertyChanged)); + + /// <summary> + /// ShowCloseButtonProperty PropertyChangedCallback call back static function. + /// </summary> + /// <param name="d">FloatingWindow object whose ShowCloseButton property is changed.</param> + /// <param name="e">DependencyPropertyChangedEventArgs which contains the old and new values.</param> + private static void OnShowCloseButtonPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + FloatingWindow window = (FloatingWindow)d; + + if (window.closeButton != null) + window.closeButton.SetVisible((bool)e.NewValue); + } + + #endregion + + #region public bool ShowMaximizeButton + + /// <summary> + /// Gets or sets a value indicating whether to show Maximize button. + /// </summary> + /// <value><c>true</c> if to show Maximize button; otherwise, <c>false</c>.</value> + public bool ShowMaximizeButton + { + get { return (bool)GetValue(ShowMaximizeButtonProperty); } + set { SetValue(ShowMaximizeButtonProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindow.ShowMaximizeButton" /> dependency property. + /// </summary> + /// <value> + /// The identifier for the <see cref="FloatingWindow.ShowMaximizeButton" /> dependency property. + /// </value> + public static readonly DependencyProperty ShowMaximizeButtonProperty = + DependencyProperty.Register( + "ShowMaximizeButton", + typeof(bool), + typeof(FloatingWindow), + new PropertyMetadata(true, ShowMaximizeButtonPropertyChanged)); + + /// <summary> + /// ShowMaximizeButtonProperty PropertyChangedCallback call back static function. + /// </summary> + /// <param name="d">FloatingWindow object whose ShowMaximizeButton property is changed.</param> + /// <param name="e">DependencyPropertyChangedEventArgs which contains the old and new values.</param> + private static void ShowMaximizeButtonPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + FloatingWindow window = (FloatingWindow)d; + bool visible = window.IsModal ? false : (bool)e.NewValue; + + if (window.maximizeButton != null) + window.maximizeButton.SetVisible(visible); + } + + #endregion + + #region public bool ShowMinimizeButton + + /// <summary> + /// Gets or sets a value indicating whether to show Minimize button. + /// </summary> + /// <value><c>true</c> if to show Minimize button; otherwise, <c>false</c>.</value> + public bool ShowMinimizeButton + { + get { return (bool)GetValue(ShowMinimizeButtonProperty); } + set { SetValue(ShowMinimizeButtonProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindow.ShowMinimizeButton" /> dependency property. + /// </summary> + /// <value> + /// The identifier for the <see cref="FloatingWindow.ShowMinimizeButton" /> dependency property. + /// </value> + public static readonly DependencyProperty ShowMinimizeButtonProperty = + DependencyProperty.Register( + "ShowMinimizeButton", + typeof(bool), + typeof(FloatingWindow), + new PropertyMetadata(true, ShowMinimizeButtonPropertyChanged)); + + /// <summary> + /// ShowMinimizeButtonProperty PropertyChangedCallback call back static function. + /// </summary> + /// <param name="d">FloatingWindow object whose ShowMinimizeButton property is changed.</param> + /// <param name="e">DependencyPropertyChangedEventArgs which contains the old and new values.</param> + private static void ShowMinimizeButtonPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + FloatingWindow window = (FloatingWindow)d; + bool visible = window.IsModal ? false : (bool)e.NewValue; + + if (window.minimizeButton != null) + window.minimizeButton.SetVisible(visible); + } + + #endregion + + #region public bool ShowRestoreButton + + /// <summary> + /// Gets or sets a value indicating whether to show Restore button. + /// </summary> + /// <value><c>true</c> if to show Restore button; otherwise, <c>false</c>.</value> + public bool ShowRestoreButton + { + get { return (bool)GetValue(ShowRestoreButtonProperty); } + set { SetValue(ShowRestoreButtonProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindow.ShowRestoreButton" /> dependency property. + /// </summary> + /// <value> + /// The identifier for the <see cref="FloatingWindow.ShowRestoreButton" /> dependency property. + /// </value> + public static readonly DependencyProperty ShowRestoreButtonProperty = + DependencyProperty.Register( + "ShowRestoreButton", + typeof(bool), + typeof(FloatingWindow), + new PropertyMetadata(true, ShowRestoreButtonPropertyChanged)); + + /// <summary> + /// ShowRestoreButtonProperty PropertyChangedCallback call back static function. + /// </summary> + /// <param name="d">FloatingWindow object whose ShowRestoreButton property is changed.</param> + /// <param name="e">DependencyPropertyChangedEventArgs which contains the old and new values.</param> + private static void ShowRestoreButtonPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + FloatingWindow window = (FloatingWindow)d; + bool visible = window.IsModal ? false : (bool)e.NewValue; + + if (window.restoreButton != null) + window.restoreButton.SetVisible(visible); + } + + #endregion + + #region public object Title + + /// <summary> + /// Gets or sets title content that is displayed on the top of the window. + /// Can contain any UI elements - not only a text. + /// </summary> + /// <value> + /// The title displayed at the top of the window. The default is null. + /// </value> + public object Title + { + get { return GetValue(TitleProperty); } + set { SetValue(TitleProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindow.Title" /> dependency property. + /// </summary> + /// <value> + /// The identifier for the <see cref="FloatingWindow.Title" /> dependency property. + /// </value> + public static readonly DependencyProperty TitleProperty = + DependencyProperty.Register( + "Title", + typeof(object), + typeof(FloatingWindow), + null); + + #endregion + + #region public object Icon + + /// <summary> + /// Gets or sets content that is displayed as an icon of the window on the iconbar. + /// </summary> + /// <value> + /// The content displayed as an icon of the window on the iconbar. The default is null. + /// </value> + public object Icon + { + get { return GetValue(IconProperty); } + set { SetValue(IconProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindow.Icon" /> dependency property. + /// </summary> + /// <value> + /// The identifier for the <see cref="FloatingWindow.Icon" /> dependency property. + /// </value> + public static readonly DependencyProperty IconProperty = + DependencyProperty.Register( + "Icon", + typeof(object), + typeof(FloatingWindow), + null); + + #endregion + + #region public string IconText + + /// <summary> + /// Gets or sets a text displayed on the icon of the minimized window. + /// </summary> + /// <value> + /// The text displayed on the icon. + /// </value> + public string IconText + { + get { return (string)GetValue(IconTextProperty); } + set { SetValue(IconTextProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindow.IconText" /> dependency property. + /// </summary> + /// <value> + /// The identifier for the <see cref="FloatingWindow.IconText" /> dependency property. + /// </value> + public static readonly DependencyProperty IconTextProperty = + DependencyProperty.Register( + "IconText", + typeof(string), + typeof(FloatingWindow), + null); + + #endregion + + #region public Brush TitleBackground + + /// <summary> + /// Gets or sets the title background. + /// </summary> + /// <value>The title background.</value> + public Brush TitleBackground + { + get { return (Brush)GetValue(TitleBackgroundProperty); } + set { SetValue(TitleBackgroundProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindow.TitleBackground" /> dependency property. + /// </summary> + /// <value> + /// The identifier for the <see cref="FloatingWindow.TitleBackground" /> dependency property. + /// </value> + public static readonly DependencyProperty TitleBackgroundProperty = + DependencyProperty.Register( + "TitleBackground", + typeof(Brush), + typeof(FloatingWindow), + new PropertyMetadata(new SolidColorBrush(Colors.Transparent), null)); + + #endregion + + #region public bool ResizeEnabled + + /// <summary> + /// Gets or sets a value indicating whether resizing is enabled. + /// </summary> + /// <value><c>true</c> if resizing is enabled; otherwise, <c>false</c>.</value> + public bool ResizeEnabled + { + get { return (bool)GetValue(ResizeEnabledProperty); } + set { SetValue(ResizeEnabledProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindow.ResizeEnabled" /> dependency property. + /// </summary> + /// <value> + /// The identifier for the <see cref="FloatingWindow.ResizeEnabled" /> dependency property. + /// </value> + public static readonly DependencyProperty ResizeEnabledProperty = + DependencyProperty.Register( + "ResizeEnabled", + typeof(bool), + typeof(FloatingWindow), + new PropertyMetadata(true, null)); + + #endregion + + #region public double ResizingAreaThickness + + /// <summary> + /// Gets or sets the width of the resizing area. + /// </summary> + /// <value>The width of the resizing area.</value> + public double ResizingAreaThickness + { + get { return (double)GetValue(ResizingAreaThicknessProperty); } + set { SetValue(ResizingAreaThicknessProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindow.ResizingAreaThickness" /> dependency property. + /// </summary> + /// <value> + /// The identifier for the <see cref="FloatingWindow.ResizingAreaThickness" /> dependency property. + /// </value> + public static readonly DependencyProperty ResizingAreaThicknessProperty = + DependencyProperty.Register( + "ResizingAreaThickness", + typeof(double), + typeof(FloatingWindow), + new PropertyMetadata(ResizingAreaDefaultValue, OnResizingAreaThicknessPropertyChanged)); + + /// <summary> + /// ResizingAreaThicknessProperty PropertyChangedCallback call back static function. + /// </summary> + /// <param name="d">FloatingWindow object whose ResizingAreaThickness property is changed.</param> + /// <param name="e">DependencyPropertyChangedEventArgs which contains the old and new values.</param> + private static void OnResizingAreaThicknessPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + FloatingWindow window = (FloatingWindow)d; + window.resizeController.ResizingArea = (double)e.NewValue; + } + + #endregion + + #region public Point Position + + /// <summary> + /// Gets or sets current window position. + /// </summary> + /// <value>Current position.</value> + public Point Position + { + get { return (Point)GetValue(PositionProperty); } + set { SetValue(PositionProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindow.Position" /> dependency property. + /// </summary> + /// <value> + /// The identifier for the <see cref="FloatingWindow.Position" /> dependency property. + /// </value> + public static readonly DependencyProperty PositionProperty = + DependencyProperty.Register( + "Position", + typeof(Point), + typeof(FloatingWindow), + new PropertyMetadata(new Point(double.NaN, double.NaN), OnPositionPropertyChanged)); + + /// <summary> + /// PositionProperty PropertyChangedCallback call back static function. + /// </summary> + /// <param name="d">FloatingWindow object whose Position property is changed.</param> + /// <param name="e">DependencyPropertyChangedEventArgs which contains the old and new values.</param> + private static void OnPositionPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + FloatingWindow window = (FloatingWindow)d; + + if (window != null) + { + if (window.FloatingWindowHost.IsLayoutUpdated) + window.MoveWindow((Point)e.NewValue); + } + } + + #endregion + + #region public bool ShowInIconbar + + /// <summary> + /// Gets or sets a value indicating whether to show minimized window in the iconbar. + /// </summary> + /// <value><c>true</c> if to show minimized window in the iconbar; otherwise, <c>false</c>.</value> + public bool ShowInIconbar + { + get { return (bool)GetValue(ShowInIconbarProperty); } + set { SetValue(ShowInIconbarProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindow.ShowInIconbar" /> dependency property. + /// </summary> + /// <value> + /// The identifier for the <see cref="FloatingWindow.ShowInIconbar" /> dependency property. + /// </value> + public static readonly DependencyProperty ShowInIconbarProperty = + DependencyProperty.Register( + "ShowInIconbar", + typeof(bool), + typeof(FloatingWindow), + new PropertyMetadata(true, null)); + + #endregion + + #region public FlowDirection FlowDirection + + /// <summary> + /// Gets or sets the direction that title text flows within window's icon. + /// </summary> + /// <value>A constant name from the FlowDirection enumeration, either LeftToRight or RightToLeft.</value> + public FlowDirection FlowDirection + { + get { return (FlowDirection)GetValue(FlowDirectionProperty); } + set { SetValue(FlowDirectionProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindow.FlowDirection" /> dependency property. + /// </summary> + /// <value> + /// The identifier for the <see cref="FloatingWindow.FlowDirection" /> dependency property. + /// </value> + public static readonly DependencyProperty FlowDirectionProperty = + DependencyProperty.Register( + "FlowDirection", + typeof(FlowDirection), + typeof(FloatingWindow), + new PropertyMetadata(FlowDirection.LeftToRight, null)); + + #endregion + + #region public Style TitleStyle + + /// <summary> + /// Gets or sets the style that is used when rendering the Title of the window. + /// </summary> + public Style TitleStyle + { + get { return GetValue(TitleStyleProperty) as Style; } + set { SetValue(TitleStyleProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindow.TitleStyleProperty" /> dependency property. + /// </summary> + public static readonly DependencyProperty TitleStyleProperty = + DependencyProperty.Register( + "TitleStyle", + typeof(Style), + typeof(FloatingWindow), + new PropertyMetadata(OnTitleStylePropertyChanged)); + + /// <summary> + /// TitleStyle PropertyChangedCallback call back static function. + /// </summary> + /// <param name="d">FloatingWindow object whose TitleStyle property is changed.</param> + /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param> + private static void OnTitleStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + FloatingWindow window = (FloatingWindow)d; + if (window != null && window.titleContent != null) + { + Style style = e.NewValue as Style; + window.titleContent.Style = style; + } + } + + #endregion + + #region public Style CloseButtonStyle + + /// <summary> + /// Gets or sets the style of the Close button. + /// </summary> + public Style CloseButtonStyle + { + get { return GetValue(CloseButtonStyleProperty) as Style; } + set { SetValue(CloseButtonStyleProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindow.CloseButtonStyleProperty" /> dependency property. + /// </summary> + public static readonly DependencyProperty CloseButtonStyleProperty = + DependencyProperty.Register( + "CloseButtonStyle", + typeof(Style), + typeof(FloatingWindow), + new PropertyMetadata(OnCloseButtonStylePropertyChanged)); + + /// <summary> + /// CloseButtonStyle PropertyChangedCallback call back static function. + /// </summary> + /// <param name="d">FloatingWindow object whose CloseButtonStyle property is changed.</param> + /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param> + private static void OnCloseButtonStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var window = (FloatingWindow)d; + if (window != null) + { + Style style = e.NewValue as Style; + window.CloseButtonStyle = style; + + if (window.closeButton != null) + window.closeButton.Style = style; + } + } + + #endregion + + #region public Style MinimizeButtonStyle + + /// <summary> + /// Gets or sets the style of the Minimize button. + /// </summary> + public Style MinimizeButtonStyle + { + get { return GetValue(MinimizeButtonStyleProperty) as Style; } + set { SetValue(MinimizeButtonStyleProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindow.MinimizeButtonStyleProperty" /> dependency property. + /// </summary> + public static readonly DependencyProperty MinimizeButtonStyleProperty = + DependencyProperty.Register( + "MinimizeButtonStyle", + typeof(Style), + typeof(FloatingWindow), + new PropertyMetadata(OnMinimizeButtonStylePropertyChanged)); + + /// <summary> + /// CloseButtonStyle PropertyChangedCallback call back static function. + /// </summary> + /// <param name="d">FloatingWindow object whose MinimizeButtonStyle property is changed.</param> + /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param> + private static void OnMinimizeButtonStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var window = (FloatingWindow)d; + if (window != null) + { + Style style = e.NewValue as Style; + window.CloseButtonStyle = style; + + if (window.minimizeButton != null) + window.minimizeButton.Style = style; + } + } + + #endregion + + #region public Style MaximizeButtonStyle + + /// <summary> + /// Gets or sets the style of the Maximize button. + /// </summary> + public Style MaximizeButtonStyle + { + get { return GetValue(MaximizeButtonStyleProperty) as Style; } + set { SetValue(MaximizeButtonStyleProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindow.MaximizeButtonStyleProperty" /> dependency property. + /// </summary> + public static readonly DependencyProperty MaximizeButtonStyleProperty = + DependencyProperty.Register( + "MaximizeButtonStyle", + typeof(Style), + typeof(FloatingWindow), + new PropertyMetadata(OnMaximizeButtonStylePropertyChanged)); + + /// <summary> + /// MaximizeButtonStyle PropertyChangedCallback call back static function. + /// </summary> + /// <param name="d">FloatingWindow object whose MaximizeButtonStyle property is changed.</param> + /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param> + private static void OnMaximizeButtonStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var window = (FloatingWindow)d; + if (window != null) + { + Style style = e.NewValue as Style; + window.CloseButtonStyle = style; + + if (window.maximizeButton != null) + window.maximizeButton.Style = style; + } + } + + #endregion + + #region public Style RestoreButtonStyle + + /// <summary> + /// Gets or sets the style of the Restore button. + /// </summary> + public Style RestoreButtonStyle + { + get { return GetValue(RestoreButtonStyleProperty) as Style; } + set { SetValue(RestoreButtonStyleProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindow.RestoreButtonStyleProperty" /> dependency property. + /// </summary> + public static readonly DependencyProperty RestoreButtonStyleProperty = + DependencyProperty.Register( + "RestoreButtonStyle", + typeof(Style), + typeof(FloatingWindow), + new PropertyMetadata(OnRestoreButtonStylePropertyChanged)); + + /// <summary> + /// RestoreButtonStyle PropertyChangedCallback call back static function. + /// </summary> + /// <param name="d">FloatingWindow object whose RestoreButtonStyle property is changed.</param> + /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param> + private static void OnRestoreButtonStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var window = (FloatingWindow)d; + if (window != null) + { + Style style = e.NewValue as Style; + window.CloseButtonStyle = style; + + if (window.restoreButton != null) + window.restoreButton.Style = style; + } + } + + #endregion + + #region public bool? DialogResult + + /// <summary> + /// Gets or sets a value that indicates whether the FloatingWindow was accepted or canceled. + /// </summary> + /// <value>true if the FloatingWindow was accepted; false - if canceled. The default is null.</value> + [TypeConverter(typeof(NullableBoolConverter))] + public bool? DialogResult { get; set; } + + #endregion + + #region Member Fields + + // Mouse click point + private Point clickPoint; + + // Window position when the mouse was clicked + private Point clickWindowPosition; + + // Window position, size and state when it was maximized or minimized + private Point previousPosition; + private Size previousSize; + private WindowState previousWindowState; + + private Thickness contentBorderThickness; + private CornerRadius contentBorderCornerRadius; + private CornerRadius chromeBorderCornerRadius; + + private Storyboard openingStoryboard; + private Storyboard closingStoryboard; + private Storyboard inertialMotionStoryboard; + + private FrameworkElement root; + private FrameworkElement contentPresenter; + private FrameworkElement contentRoot; + private FrameworkElement chrome; + private ContentControl titleContent; + private Border contentBorder; + + private ButtonBase closeButton; + private ButtonBase maximizeButton; + private ButtonBase restoreButton; + private ButtonBase minimizeButton; + + private bool isAppExit; + private bool isMouseCaptured; + + private ResizeController resizeController; + private ISnapinController snapinController; + private InertiaController inertiaController; + private ILocalStorage localStorage; + private IBitmapHelper bitmapHelper; + + // Current window state + private WindowState windowState = WindowState.Normal; + + // Specifies whether the window is moving or resizing + private WindowAction windowAction; + + // Bitmap containing thumbnail image + private ImageSource minimizedWindowThumbnail; + + #endregion Member Fields + + /// <summary> + /// Initializes a new instance of the <see cref="FloatingWindow" /> class. + /// </summary> + public FloatingWindow() + { + DefaultStyleKey = typeof(FloatingWindow); + + resizeController = new ResizeController(this); + resizeController.ResizingArea = ResizingAreaThickness; + snapinController = new SnapinController(); + inertiaController = new InertiaController(); + localStorage = new LocalStorage(); + bitmapHelper = new BitmapHelper(); + + this.SetVisible(false); + } + + #region Events + + /// <summary> + /// Occurs when the <see cref="FloatingWindow" /> is activated. + /// </summary> + /// <remarks>Not visible <see cref="FloatingWindow" /> cannot be the active. </remarks> + public event EventHandler Activated; + + /// <summary> + /// Occurs when the <see cref="FloatingWindow" /> is deactivated. + /// </summary> + public event EventHandler Deactivated; + + /// <summary> + /// Occurs when the <see cref="FloatingWindow" /> is closed. + /// </summary> + public event EventHandler Closed; + + /// <summary> + /// Occurs when the <see cref="FloatingWindow" /> is maximized. + /// </summary> + public event EventHandler Maximized; + + /// <summary> + /// Occurs when the <see cref="FloatingWindow" /> is minimized. + /// </summary> + public event EventHandler Minimized; + + /// <summary> + /// Occurs when the <see cref="FloatingWindow" /> is restored. + /// </summary> + public event EventHandler Restored; + + /// <summary> + /// Occurs when the <see cref="FloatingWindow" /> is closing. + /// </summary> + public event EventHandler<CancelEventArgs> Closing; + + #endregion Events + + #region Properties + + /// <summary> + /// Gets or sets a reference to the FloatingWindowHost, containing the window. + /// </summary> + /// <value>The floating window host.</value> + public FloatingWindowHost FloatingWindowHost { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether the window is always displayed in front of other windows. + /// </summary> + /// <value><c>true</c> if top most; otherwise, <c>false</c>.</value> + public bool TopMost { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this window is open. + /// </summary> + /// <value><c>true</c> if this window is open; otherwise, <c>false</c>.</value> + public bool IsOpen { get; private set; } + + /// <summary> + /// Gets or sets a value indicating whether this window is modal. + /// </summary> + /// <value><c>true</c> if this window is modal; otherwise, <c>false</c>.</value> + public bool IsModal { get; private set; } + + /// <summary> + /// Gets the window thumbnail. + /// </summary> + /// <value>The window thumbnail.</value> + public ImageSource WindowThumbnail + { + get + { + return (windowState == WindowState.Minimized) ? minimizedWindowThumbnail : GetThumbnailImage(); + } + } + + /// <summary> + /// Gets the state of the window. + /// </summary> + /// <value>Current state of the window.</value> + public WindowState WindowState + { + get { return windowState; } + } + + /// <summary> + /// Gets or sets the minimum height constraint of a <see cref="T:System.Windows.FrameworkElement"/>. + /// </summary> + /// <value>he minimum height of the window.</value> + /// <returns>The minimum height of the window, in pixels. The default is 0. + /// This value can be any value equal to or greater than 0. + /// However, <see cref="F:System.Double.PositiveInfinity"/> is not valid.</returns> + public new double MinHeight + { + get + { + double minHeight = base.MinHeight; + + if ((base.MinHeight.IsNotSet() || base.MinHeight == 0) && + (chrome != null && contentRoot != null)) + { + // Set minimal height to the height of the chrome element of the window + minHeight = chrome.GetRelativePosition(contentRoot).Y + chrome.ActualHeight; + } + + return minHeight; + } + + set + { + base.MinHeight = value; + } + } + + /// <summary> + /// Gets a bounding rectangle of the window. + /// </summary> + /// <value>Bounding rectangle.</value> + public Rect BoundingRectangle + { + get { return new Rect(Position.X, Position.Y, ActualWidth, ActualHeight); } + } + + /// <summary> + /// Gets a Snapin controller. + /// </summary> + /// <value>Snapin controller.</value> + public ISnapinController SnapinController + { + get { return snapinController; } + } + + /// <summary> + /// Gets the host panel, containing the floating windows. + /// </summary> + /// <value>The host panel.</value> + public Panel HostPanel + { + get { return this.FloatingWindowHost == null ? null : this.FloatingWindowHost.HostPanel; } + } + + /// <summary> + /// Gets a value indicating whether window size is explicitly set. + /// </summary> + /// <value><c>true</c> if window size is set; otherwise, <c>false</c>.</value> + private bool IsWindowSizeSet + { + get { return !Width.IsNotSet() && !Height.IsNotSet(); } + } + + /// <summary> + /// Gets a value indicating whether the window Tag property is set. + /// </summary> + /// <value><c>true</c> if the Tag set; otherwise, <c>false</c>.</value> + private bool IsWindowTagSet + { + get + { + string tag = this.Tag as string; + return !string.IsNullOrWhiteSpace(tag); + } + } + + /// <summary> + /// Gets coordinates of the window placed in the center of its host. + /// </summary> + /// <value>The centered window position.</value> + private Point CenteredWindowPosition + { + get + { + return new Point((HostPanel.ActualWidth - Width.ValueOrZero()) / 2, (HostPanel.ActualHeight - Height.ValueOrZero()) / 2); + } + } + + #endregion Properties + + /// <summary> + /// Shows the window as a modal one. + /// </summary> + public void ShowModal() + { + IsModal = true; + Position = new Point(double.NaN, double.NaN); + ShowMaximizeButton = false; + ShowMinimizeButton = false; + + Show(); + } + + /// <summary> + /// Opens the <see cref="FloatingWindow" /> in previously saved position or + /// in the center of the <see cref="FloatingWindowHost" />. + /// </summary> + public void Show() + { + Show(Position); + } + + /// <summary> + /// Shows the window in the specified coordinates, relative to the window's Host. + /// </summary> + /// <param name="x">X-coordinate.</param> + /// <param name="y">Y-coordinate.</param> + public void Show(double x, double y) + { + Show(new Point(x, y)); + } + + /// <summary> + /// Shows a <see cref="FloatingWindow"/> at maximal size taking into account + /// specified margins and current <see cref="FloatingWindowHost"/> size. + /// </summary> + /// <param name="margins">Window margins.</param> + public void Show(Thickness margins) + { + CheckHost(); + Action<Thickness> action = new Action<Thickness>(ShowWindow); + this.FloatingWindowHost.ShowWindow(action, margins); + } + + /// <summary> + /// Shows the window in the specified coordinates, relative to the window's Host. + /// </summary> + /// <param name="point">Coordinates of the upper-left corner of the window.</param> + public void Show(Point point) + { + CheckHost(); + Action<Point> action = new Action<Point>(ShowWindow); + this.FloatingWindowHost.ShowWindow(action, point); + } + + /// <summary> + /// Shows a <see cref="FloatingWindow"/> at maximal size taking into account + /// specified margins and current <see cref="FloatingWindowHost"/> size. + /// </summary> + /// <param name="margins">Window margins.</param> + private void ShowWindow(Thickness margins) + { + Width = Math.Max(MinWidth, HostPanel.ActualWidth - margins.Horizontal()); + Height = Math.Max(MinHeight, HostPanel.ActualHeight - margins.Vertical()); + + ShowWindow(new Point(margins.Left, margins.Top)); + } + + /// <summary> + /// Shows the window in the specified coordinates, relative to the window's Host. + /// </summary> + /// <param name="point">Coordinates of the upper-left corner of the window.</param> + /// <exception cref="System.InvalidOperationException">"The FloatingWindow was not added to the host.</exception> + private void ShowWindow(Point point) + { + if (!IsOpen) + { + if (IsModal) + this.FloatingWindowHost.ShowWindowAsModal(this); + + SubscribeToEvents(); + SubscribeToTemplatePartEvents(); + SubscribeToStoryBoardEvents(); + + // Guarantee that the visual tree of an element is complete + ApplyTemplate(); + + // Brings current window to the front + SetTopmost(); + + Point position = point; + + if (point.IsNotSet()) + position = CenteredWindowPosition; + + MoveWindow(position); + this.SetVisible(true); + + if (!IsWindowSizeSet && point.IsNotSet()) + { + // If window size is not set explicitly we should wait + // when the window layout is updated and update its position + contentRoot.SizeChanged += new SizeChangedEventHandler(ContentRoot_SizeChanged); + } + + VisualStateManager.GoToState(this, VSMSTATE_StateOpen, true); + IsOpen = true; + } + else + { + MoveWindow(point); + this.SetVisible(true); + } + } + + /// <summary> + /// Handles the SizeChanged event of the ContentRoot control to update window position + /// only once when the window is opened. + /// </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 ContentRoot_SizeChanged(object sender, SizeChangedEventArgs e) + { + contentRoot.SizeChanged -= new SizeChangedEventHandler(ContentRoot_SizeChanged); + double dx = -(e.NewSize.Width.ValueOrZero() - e.PreviousSize.Width.ValueOrZero()) / 2; + double dy = -(e.NewSize.Height.ValueOrZero() - e.PreviousSize.Height.ValueOrZero()) / 2; + Point point = Position.Add(dx, dy); + MoveWindow(point); + } + + /// <summary> + /// Restores window state, size and its position. + /// </summary> + public void RestoreWindow() + { + switch (windowState) + { + case WindowState.Minimized: + if (previousWindowState == WindowState.Maximized) + { + Width = HostPanel.ActualWidth; + Height = HostPanel.ActualHeight; + } + + SetTopmost(); + windowState = previousWindowState; + VisualStateManager.GoToState(this, VSMSTATE_StateRestored, true); + OnRestored(EventArgs.Empty); + break; + + case WindowState.Normal: + SetTopmost(); + EnsureVisible(); + break; + + case WindowState.Maximized: + SetTopmost(); + break; + } + + Focus(); + } + + /// <summary> + /// Makes the window topmost and tries to set focus on it. + /// </summary> + public void Activate() + { + SetTopmost(); + } + + /// <summary> + /// Brings current window to the front. + /// </summary> + private void SetTopmost() + { + if (this.FloatingWindowHost != null) + this.FloatingWindowHost.SetTopmostWindow(this); + } + + /// <summary> + /// Ensures the window is visible. + /// </summary> + private void EnsureVisible() + { + if (HostPanel != null && (Position.X >= HostPanel.ActualWidth || Position.Y >= HostPanel.ActualHeight)) + { + Position = CenteredWindowPosition; + } + } + + /// <summary> + /// Executed when the application is exited. + /// </summary> + /// <param name="sender">The sender.</param> + /// <param name="e">Event args.</param> + private void Application_Exit(object sender, EventArgs e) + { + if (IsOpen) + { + isAppExit = true; + try + { + Close(); + } + finally + { + isAppExit = false; + } + } + } + + /// <summary> + /// Closes a <see cref="FloatingWindow" />. + /// </summary> + public void Close() + { + SaveSizeAndPosition(); + + CancelEventArgs e = new CancelEventArgs(); + OnClosing(e); + + // On ApplicationExit, Close() cannot be cancelled + if (IsOpen && (!e.Cancel || isAppExit)) + { + IsOpen = false; + + if (IsModal) + this.FloatingWindowHost.RemoveOverlay(); + + if (closingStoryboard != null) + { + VisualStateManager.GoToState(this, VSMSTATE_StateClosed, true); + } + else + { + this.Visibility = Visibility.Collapsed; + OnClosed(EventArgs.Empty); + } + + UnSubscribeFromEvents(); + UnsubscribeFromTemplatePartEvents(); + } + } + + /// <summary> + /// Restores the size and position stored in the IsolatedStorage on closing. + /// </summary> + public void RestoreSizeAndPosition() + { + if (IsWindowTagSet) + { + string positionKey = GetAppSettingsKey("Position"); + string sizeKey = GetAppSettingsKey("Size"); + + if (localStorage.Contains(positionKey)) + Position = (Point)localStorage[positionKey]; + + if (localStorage.Contains(sizeKey)) + { + Size size = (Size)localStorage[sizeKey]; + Width = size.Width == 0 ? double.NaN : size.Width; + Height = size.Height == 0 ? double.NaN : size.Height; + } + } + } + + /// <summary> + /// Executed when the Close button is clicked. + /// </summary> + /// <param name="sender">Sender object.</param> + /// <param name="e">Routed event args.</param> + private void CloseButton_Click(object sender, RoutedEventArgs e) + { + Close(); + } + + /// <summary> + /// Executed when the Closing storyboard ends. + /// </summary> + /// <param name="sender">Sender object.</param> + /// <param name="e">Event args.</param> + private void Closing_Completed(object sender, EventArgs e) + { + if (closingStoryboard != null) + closingStoryboard.Completed -= new EventHandler(Closing_Completed); + + this.Visibility = Visibility.Collapsed; + OnClosed(EventArgs.Empty); + } + + /// <summary> + /// Builds the visual tree for the <see cref="FloatingWindow" /> control + /// when a new template is applied. + /// </summary> + public override void OnApplyTemplate() + { + UnsubscribeFromTemplatePartEvents(); + UnsubscribeFromStoryBoardEvents(); + + base.OnApplyTemplate(); + + root = GetTemplateChild(PART_Root) as FrameworkElement; + contentRoot = GetTemplateChild(PART_ContentRoot) as FrameworkElement; + contentBorder = GetTemplateChild(PART_ContentBorder) as Border; + chrome = GetTemplateChild(PART_Chrome) as FrameworkElement; + titleContent = GetTemplateChild(PART_TitleContent) as ContentControl; + contentPresenter = GetTemplateChild(PART_ContentPresenter) as FrameworkElement; + closeButton = GetTemplateChild(PART_CloseButton) as ButtonBase; + maximizeButton = GetTemplateChild(PART_MaximizeButton) as ButtonBase; + minimizeButton = GetTemplateChild(PART_MinimizeButton) as ButtonBase; + restoreButton = GetTemplateChild(PART_RestoreButton) as ButtonBase; + + if (root == null) + throw new NotImplementedException("Template Part PART_Root is required to display FloatingWindow."); + + if (contentRoot == null) + throw new NotImplementedException("Template Part PART_ContentRoot is required to display FloatingWindow."); + + if (contentPresenter == null) + throw new NotImplementedException("Template Part PART_ContentPresenter is required to display FloatingWindow."); + + SetStyles(); + GetStoryboards(); + SetInitialRootPosition(); + InitializeContentRootTransformGroup(); + + if (closeButton != null) + closeButton.SetVisible(ShowCloseButton); + + if (minimizeButton != null) + minimizeButton.SetVisible(ShowMinimizeButton); + + if (maximizeButton != null) + maximizeButton.SetVisible(ShowMaximizeButton); + + SubscribeToTemplatePartEvents(); + SubscribeToStoryBoardEvents(); + } + + /// <summary> + /// Sets styles that are applied for different template parts. + /// </summary> + private void SetStyles() + { + if (titleContent != null && this.TitleStyle != null) + titleContent.Style = this.TitleStyle; + + if (minimizeButton != null && this.MinimizeButtonStyle != null) + minimizeButton.Style = this.MinimizeButtonStyle; + + if (maximizeButton != null && this.MaximizeButtonStyle != null) + maximizeButton.Style = this.MaximizeButtonStyle; + + if (restoreButton != null && this.RestoreButtonStyle != null) + restoreButton.Style = this.RestoreButtonStyle; + + if (closeButton != null && this.CloseButtonStyle != null) + closeButton.Style = this.CloseButtonStyle; + } + + /// <summary> + /// Gets the storyboards defined in the <see cref="FloatingWindow" /> style. + /// </summary> + private void GetStoryboards() + { + if (root != null) + { + var groups = VisualStateManager.GetVisualStateGroups(root) as Collection<VisualStateGroup>; + if (groups != null) + { + var states = (from stategroup in groups + where stategroup.Name == FloatingWindow.VSMGROUP_Window + select stategroup.States).FirstOrDefault() as Collection<VisualState>; + + if (states != null) + { + closingStoryboard = (from state in states + where state.Name == FloatingWindow.VSMSTATE_StateClosed + select state.Storyboard).FirstOrDefault(); + + openingStoryboard = (from state in states + where state.Name == FloatingWindow.VSMSTATE_StateOpen + select state.Storyboard).FirstOrDefault(); + } + } + + if (inertialMotionStoryboard == null) + inertialMotionStoryboard = new Storyboard(); + } + } + + /// <summary> + /// Shift the root of the window to compensate its margins. + /// </summary> + private void SetInitialRootPosition() + { + double x = Math.Round(-this.Margin.Left); + double y = Math.Round(-this.Margin.Top); + + var transformGroup = root.RenderTransform as TransformGroup; + if (transformGroup == null) + { + transformGroup = new TransformGroup(); + transformGroup.Children.Add(root.RenderTransform); + root.RenderTransform = transformGroup; + } + + var translateTransform = transformGroup.Children.OfType<TranslateTransform>().FirstOrDefault(); + if (translateTransform == null) + { + transformGroup.Children.Add(new TranslateTransform() { X = x, Y = y }); + } + else + { + translateTransform.X = x; + translateTransform.Y = y; + } + } + + /// <summary> + /// Checks the TransformGroup of the content root or creates it if necesary. + /// </summary> + private void InitializeContentRootTransformGroup() + { + var transformGroup = contentRoot.RenderTransform as TransformGroup; + if (transformGroup == null) + { + transformGroup = new TransformGroup(); + transformGroup.Children.Add(contentRoot.RenderTransform); + contentRoot.RenderTransform = transformGroup; + } + + // Check that ScaleTransform exists in the TransformGroup + // ScaleTransform is used as a target in Storyboards + var scaleTransform = transformGroup.Children.OfType<ScaleTransform>().FirstOrDefault(); + + if (scaleTransform == null) + transformGroup.Children.Insert(0, new ScaleTransform()); + + var translateTransform = transformGroup.Children.OfType<TranslateTransform>().FirstOrDefault(); + + if (translateTransform == null) + transformGroup.Children.Add(new TranslateTransform()); + } + + /// <summary> + /// Raises the <see cref="FloatingWindow.Activated" /> event. + /// </summary> + /// <param name="e">The event data.</param> + protected virtual void OnActivated(EventArgs e) + { + EventHandler handler = Activated; + if (handler != null) + { + handler(this, e); + } + } + + /// <summary> + /// Raises the <see cref="FloatingWindow.Deactivated" /> event. + /// </summary> + /// <param name="e">The event data.</param> + protected virtual void OnDeactivated(EventArgs e) + { + EventHandler handler = Deactivated; + if (handler != null) + { + handler(this, e); + } + } + + /// <summary> + /// Raises the <see cref="FloatingWindow.Closed" /> event. + /// </summary> + /// <param name="e">The event data.</param> + protected virtual void OnClosed(EventArgs e) + { + this.FloatingWindowHost.Remove(this); + this.FloatingWindowHost.ActivateTopmostWindow(); + UnsubscribeFromStoryBoardEvents(); + + EventHandler handler = Closed; + if (handler != null) + { + handler(this, e); + } + + Dispose(); + } + + /// <summary> + /// Raises the <see cref="FloatingWindow.Closing" /> event. + /// </summary> + /// <param name="e">The event data.</param> + protected virtual void OnClosing(CancelEventArgs e) + { + EventHandler<CancelEventArgs> handler = Closing; + if (handler != null) + { + handler(this, e); + } + } + + /// <summary> + /// Raises the <see cref="FloatingWindow.Maximized" /> event. + /// </summary> + /// <param name="e">The event data.</param> + protected virtual void OnMaximized(EventArgs e) + { + EventHandler handler = Maximized; + if (handler != null) + { + handler(this, e); + } + } + + /// <summary> + /// Raises the <see cref="FloatingWindow.Minimized" /> event. + /// </summary> + /// <param name="e">The event data.</param> + protected virtual void OnMinimized(EventArgs e) + { + EventHandler handler = Minimized; + if (handler != null) + { + handler(this, e); + } + } + + /// <summary> + /// Raises the <see cref="FloatingWindow.Restored" /> event. + /// </summary> + /// <param name="e">The event data.</param> + protected virtual void OnRestored(EventArgs e) + { + EventHandler handler = Restored; + if (handler != null) + { + handler(this, e); + } + } + + /// <summary> + /// This method is called every time a <see cref="FloatingWindow" /> is displayed. + /// </summary> + protected virtual void OnOpened() + { + if (!Focus()) + { + // If the Focus() fails it means there is no focusable element in the window. + // In this case we set IsTabStop to true to have the keyboard functionality + IsTabStop = true; + Focus(); + } + } + + /// <summary> + /// Executed when the opening storyboard finishes. + /// </summary> + /// <param name="sender">Sender object.</param> + /// <param name="e">Event args.</param> + private void Opening_Completed(object sender, EventArgs e) + { + if (openingStoryboard != null) + openingStoryboard.Completed -= new EventHandler(Opening_Completed); + + this.FloatingWindowHost.UpdateIconBar(); + IsOpen = true; + OnOpened(); + } + + /// <summary> + /// Subscribes to events when the window is opened. + /// </summary> + private void SubscribeToEvents() + { + if (Application.Current != null) + Application.Current.Exit += new EventHandler(Application_Exit); + + if (this.FloatingWindowHost != null) + { + this.FloatingWindowHost.SizeChanged += new SizeChangedEventHandler(Host_SizeChanged); + this.FloatingWindowHost.ActiveWindowChanged += new EventHandler<ActiveWindowChangedEventArgs>(ActiveWindowChanged); + } + + // Attach Mouse event handler to catch already handled events to bring the window to the front + this.AddHandler(UIElement.MouseLeftButtonDownEvent, new MouseButtonEventHandler(FloatingWindow_MouseLeftButtonDown), true); + } + + /// <summary> + /// Handles the MouseLeftButtonDown event to bring the window to the front. + /// </summary> + /// <param name="sender">The source of the event.</param> + /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param> + private void FloatingWindow_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + // Gets the element with keyboard focus + Control elementWithFocus = FocusManager.GetFocusedElement() as Control; + + // Brings current window to the front + SetTopmost(); + + if (elementWithFocus != null) + { + if (IsControlInVisualTree(elementWithFocus)) + { + elementWithFocus.Focus(); + } + else + { + // Try to set focus on the window + Focus(); + } + } + + // Stop any inertial motion + StopInertialMotion(); + } + + /// <summary> + /// Determines whether the control is in the visual tree of the window. + /// </summary> + /// <param name="control">The control to test.</param> + /// <returns> + /// <c>true</c> if the control is in the visual tree; otherwise, <c>false</c>. + /// </returns> + private bool IsControlInVisualTree(Control control) + { + if (control != null) + { + DependencyObject parent = control; + do + { + parent = VisualTreeHelper.GetParent(parent); + FloatingWindow window = parent as FloatingWindow; + + if (window != null && window == this) + return true; + } + while (parent != null); + } + + return false; + } + + /// <summary> + /// Unsubscribe from events when the ChildWindow is closed. + /// </summary> + private void UnSubscribeFromEvents() + { + if (Application.Current != null) + Application.Current.Exit -= new EventHandler(Application_Exit); + + if (this.FloatingWindowHost != null) + { + this.FloatingWindowHost.SizeChanged -= new SizeChangedEventHandler(Host_SizeChanged); + this.FloatingWindowHost.ActiveWindowChanged -= new EventHandler<ActiveWindowChangedEventArgs>(ActiveWindowChanged); + } + + this.RemoveHandler(UIElement.MouseLeftButtonDownEvent, new MouseButtonEventHandler(FloatingWindow_MouseLeftButtonDown)); + } + + /// <summary> + /// Subscribes to the events 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); + + if (inertialMotionStoryboard != null) + inertialMotionStoryboard.Completed += new EventHandler(InertialMotion_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); + + if (inertialMotionStoryboard != null) + inertialMotionStoryboard.Completed -= new EventHandler(InertialMotion_Completed); + } + + /// <summary> + /// Subscribes to the events on the template parts. + /// </summary> + private void SubscribeToTemplatePartEvents() + { + if (closeButton != null) + closeButton.Click += new RoutedEventHandler(CloseButton_Click); + + if (maximizeButton != null) + maximizeButton.Click += new RoutedEventHandler(MaximizeButton_Click); + + if (restoreButton != null) + restoreButton.Click += new RoutedEventHandler(RestoreButton_Click); + + if (minimizeButton != null) + minimizeButton.Click += new RoutedEventHandler(MinimizeButton_Click); + } + + /// <summary> + /// Unsubscribe from the events that are subscribed on the template part elements. + /// </summary> + private void UnsubscribeFromTemplatePartEvents() + { + if (closeButton != null) + closeButton.Click -= new RoutedEventHandler(CloseButton_Click); + + if (maximizeButton != null) + maximizeButton.Click -= new RoutedEventHandler(MaximizeButton_Click); + + if (restoreButton != null) + restoreButton.Click -= new RoutedEventHandler(RestoreButton_Click); + + if (minimizeButton != null) + minimizeButton.Click -= new RoutedEventHandler(MinimizeButton_Click); + } + + /// <summary> + /// Handles the ActiveWindowChanged event of the Host control. + /// </summary> + /// <param name="sender">The sender.</param> + /// <param name="e">The <see cref="SilverFlow.Controls.ActiveWindowChangedEventArgs"/> instance containing the event data.</param> + private void ActiveWindowChanged(object sender, ActiveWindowChangedEventArgs e) + { + if (e.Old == this) + OnDeactivated(EventArgs.Empty); + + if (e.New == this) + OnActivated(EventArgs.Empty); + } + + /// <summary> + /// Handles the Click event of the MaximizeButton 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 MaximizeButton_Click(object sender, RoutedEventArgs e) + { + MaximizeWindow(); + } + + /// <summary> + /// Handles the Click event of the RestoreButton 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 RestoreButton_Click(object sender, RoutedEventArgs e) + { + RestoreMaximizedWindow(); + } + + /// <summary> + /// Handles the Click event of the MinimizeButton 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 MinimizeButton_Click(object sender, RoutedEventArgs e) + { + MinimizeWindow(); + } + + /// <summary> + /// Minimizes the window. + /// </summary> + private void MinimizeWindow() + { + if (windowState != WindowState.Minimized) + { + if (minimizeButton != null) + VisualStateManager.GoToState(minimizeButton, VSMSTATE_StateNormal, true); + + if (windowState == WindowState.Normal) + { + // Store previous coordinates + previousPosition = Position; + previousSize = new Size(ActualWidth, ActualHeight); + } + + minimizedWindowThumbnail = GetThumbnailImage(); + + previousWindowState = windowState; + VisualStateManager.GoToState(this, VSMSTATE_StateMinimized, true); + OnMinimized(EventArgs.Empty); + } + + windowState = WindowState.Minimized; + OnDeactivated(EventArgs.Empty); + + this.FloatingWindowHost.ActivateTopmostWindow(); + } + + /// <summary> + /// Creates a thumbnail of the window. + /// </summary> + /// <returns>Bitmap containing thumbnail image.</returns> + private ImageSource GetThumbnailImage() + { + // If an Icon is specified - use it as a thumbnail displayed on the iconbar + // Otherwise, display the window itself + FrameworkElement icon = (Icon as FrameworkElement) ?? contentRoot; + ImageSource bitmap = bitmapHelper.RenderVisual(icon, FloatingWindowHost.IconWidth, FloatingWindowHost.IconHeight); + + return bitmap; + } + + /// <summary> + /// Maximizes the window. + /// </summary> + public void MaximizeWindow() + { + if (windowState != WindowState.Maximized) + { + if (maximizeButton != null && restoreButton != null && HostPanel != null) + { + if (this.ShowMaximizeButton) + maximizeButton.SetVisible(false); + + if (this.ShowRestoreButton) + restoreButton.SetVisible(true); + + VisualStateManager.GoToState(restoreButton, VSMSTATE_StateNormal, true); + + // Store previous coordinates + previousPosition = Position; + previousSize = new Size(ActualWidth, ActualHeight); + + // Hide the outer border + if (contentBorder != null) + { + contentBorderThickness = contentBorder.BorderThickness; + contentBorderCornerRadius = contentBorder.CornerRadius; + contentBorder.BorderThickness = new Thickness(0); + contentBorder.CornerRadius = new CornerRadius(0); + } + + Border border = chrome as Border; + if (border != null) + { + chromeBorderCornerRadius = border.CornerRadius; + border.CornerRadius = new CornerRadius(0); + } + + StartMaximizingAnimation(); + } + + previousWindowState = windowState; + windowState = WindowState.Maximized; + } + } + + /// <summary> + /// Checks if the floating window was added to the FloatingWindowHost. + /// </summary> + private void CheckHost() + { + if (this.FloatingWindowHost == null) + throw new InvalidOperationException("The FloatingWindow was not added to the FloatingWindowHost."); + } + + /// <summary> + /// Starts maximizing animation. + /// </summary> + private void StartMaximizingAnimation() + { + SaveActualSize(); + + this.MoveAndResize(new Point(0, 0), HostPanel.ActualWidth, HostPanel.ActualHeight, + MaximizingDurationInMilliseconds, Maximizing_Completed); + } + + /// <summary> + /// Saves the actual size if it was not set explicitly set. + /// E.g. the Width can be NaN, that means "Auto". + /// </summary> + private void SaveActualSize() + { + if (Width.IsNotSet()) + Width = ActualWidth; + + if (Height.IsNotSet()) + Height = ActualHeight; + } + + /// <summary> + /// Handles the Completed event of the Maximizing animation. + /// </summary> + /// <param name="sender">The source of the event.</param> + /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param> + private void Maximizing_Completed(object sender, EventArgs e) + { + OnMaximized(EventArgs.Empty); + } + + /// <summary> + /// Restores maximized window position and size. + /// </summary> + private void RestoreMaximizedWindow() + { + if (windowState != WindowState.Normal) + { + if (maximizeButton != null && restoreButton != null && HostPanel != null) + { + if (this.ShowMaximizeButton) + maximizeButton.SetVisible(true); + + if (this.ShowRestoreButton) + restoreButton.SetVisible(false); + + VisualStateManager.GoToState(maximizeButton, VSMSTATE_StateNormal, true); + } + + // Restore the outer border + if (contentBorder != null) + { + contentBorder.BorderThickness = contentBorderThickness; + contentBorder.CornerRadius = contentBorderCornerRadius; + } + + Border border = chrome as Border; + + if (border != null) + border.CornerRadius = chromeBorderCornerRadius; + + StartRestoringAnimation(); + windowState = WindowState.Normal; + } + else + { + Show(Position); + } + } + + /// <summary> + /// Starts restoring animation. + /// </summary> + private void StartRestoringAnimation() + { + SaveActualSize(); + + this.MoveAndResize(previousPosition, previousSize.Width, previousSize.Height, + RestoringDurationInMilliseconds, Restoring_Completed); + } + + /// <summary> + /// Handles the Completed event of the Restoring animation. + /// </summary> + /// <param name="sender">The source of the event.</param> + /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param> + private void Restoring_Completed(object sender, EventArgs e) + { + OnRestored(EventArgs.Empty); + } + + /// <summary> + /// Updates clipping region on host 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) + { + if (windowState == WindowState.Maximized) + { + Width = HostPanel.ActualWidth; + Height = double.IsInfinity(MaxHeight) ? HostPanel.ActualHeight : MaxHeight; + } + } + + /// <summary> + /// Executed when mouse left button is down. + /// </summary> + /// <param name="e">The data for the event.</param> + protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) + { + base.OnMouseLeftButtonDown(e); + + if (windowState == WindowState.Normal) + { + // Stop inertial motion before the mouse is captured + StopInertialMotion(); + + clickPoint = e.GetPosition(HostPanel); + clickWindowPosition = Position; + snapinController.SnapinDistance = this.FloatingWindowHost.SnapinDistance; + snapinController.SnapinMargin = this.FloatingWindowHost.SnapinMargin; + snapinController.SnapinEnabled = this.FloatingWindowHost.SnapinEnabled; + + if (ResizeEnabled && resizeController.CanResize) + { + snapinController.SnapinBounds = this.FloatingWindowHost.GetSnapinBounds(this); + resizeController.StartResizing(); + CaptureMouseCursor(); + windowAction = WindowAction.Resize; + } + else if (chrome != null) + { + // If the mouse was clicked on the chrome - start dragging the window + Point point = e.GetPosition(chrome); + + if (chrome.ContainsPoint(point)) + { + snapinController.SnapinBounds = this.FloatingWindowHost.GetSnapinBounds(this); + CaptureMouseCursor(); + windowAction = WindowAction.Move; + inertiaController.StartMotion(Position); + } + } + } + } + + /// <summary> + /// Executed when mouse left button is up. + /// </summary> + /// <param name="e">The data for the event.</param> + protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) + { + base.OnMouseLeftButtonUp(e); + + if (windowAction == WindowAction.Move) + { + InertialMotion motion = inertiaController.GetInertialMotionParameters( + this.FloatingWindowHost.HostPanel.GetActualBoundingRectangle(), this.BoundingRectangle); + + if (motion != null) + { + contentRoot.AnimateTranslateTransform(inertialMotionStoryboard, motion.EndPosition, motion.Seconds, motion.EasingFunction); + } + } + + if (isMouseCaptured) + { + contentRoot.ReleaseMouseCapture(); + isMouseCaptured = false; + } + + windowAction = WindowAction.None; + } + + /// <summary> + /// Handles the Completed event of the InertialMotionStoryboard. + /// </summary> + /// <param name="sender">The source of the event.</param> + /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param> + private void InertialMotion_Completed(object sender, EventArgs e) + { + // Save current window position reading it from the TranslateTransform object + Position = GetCurrentWindowPosition(); + } + + /// <summary> + /// Stops current inertial motion. + /// </summary> + private void StopInertialMotion() + { + if (inertialMotionStoryboard.GetCurrentState() != ClockState.Stopped) + { + inertialMotionStoryboard.Pause(); + + // The Position has rounded coordinates now, but real X and Y coordinates are fractional + Position = GetCurrentWindowPosition(); + + // Move the window to the rounded coordinates + MoveWindow(Position); + + inertialMotionStoryboard.Stop(); + inertialMotionStoryboard.Children.Clear(); + } + } + + /// <summary> + /// Gets current window position taking into account animation effects. + /// </summary> + /// <returns>Current window position.</returns> + private Point GetCurrentWindowPosition() + { + var transformGroup = contentRoot.RenderTransform as TransformGroup; + var translateTransform = transformGroup.Children.OfType<TranslateTransform>().FirstOrDefault(); + var position = new Point(translateTransform.X, translateTransform.Y); + + // Round coordinates to avoid blured window + return position.Round(); + } + + /// <summary> + /// Captures the mouse cursor. + /// </summary> + private void CaptureMouseCursor() + { + contentRoot.CaptureMouse(); + isMouseCaptured = true; + } + + /// <summary> + /// Executed when mouse moves. + /// </summary> + /// <param name="e">The data for the event.</param> + protected override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + + if (windowState == WindowState.Normal && ResizeEnabled && windowAction == WindowAction.None) + { + Point mousePosition = e.GetPosition(contentRoot); + + if (IsMouseOverButtons(mousePosition, contentRoot)) + this.Cursor = Cursors.Arrow; + else + resizeController.SetCursor(mousePosition); + } + + Point p = e.GetPosition(HostPanel); + double dx = p.X - clickPoint.X; + double dy = p.Y - clickPoint.Y; + + if (windowAction == WindowAction.Resize) + resizeController.Resize(dx, dy); + + if (windowAction == WindowAction.Move) + { + Point point = clickWindowPosition.Add(dx, dy); + Rect rect = new Rect(point.X, point.Y, ActualWidth, ActualHeight); + + point = snapinController.SnapRectangle(rect); + MoveWindow(point); + + inertiaController.MoveToPoint(Position); + } + } + + /// <summary> + /// Determines whether the mouse is over buttons in the the specified mouse position. + /// </summary> + /// <param name="position">The mouse position.</param> + /// <param name="origin">Relative origin.</param> + /// <returns><c>true</c> if mouse is mouse over buttons.</returns> + private bool IsMouseOverButtons(Point position, UIElement origin) + { + return (minimizeButton.IsVisible() && minimizeButton.ContainsPoint(position, origin)) || + (maximizeButton.IsVisible() && maximizeButton.ContainsPoint(position, origin)) || + (restoreButton.IsVisible() && restoreButton.ContainsPoint(position, origin)) || + (closeButton.IsVisible() && closeButton.ContainsPoint(position, origin)); + } + + /// <summary> + /// Moves the window to the specified coordinates. + /// </summary> + /// <param name="point">Coordinates of the window.</param> + private void MoveWindow(Point point) + { + if (contentRoot != null && !point.IsNotSet()) + { + // Round coordinates to avoid blured window + double x = Math.Round(Math.Max(0, point.X)); + double y = Math.Round(Math.Max(0, point.Y)); + + var transformGroup = contentRoot.RenderTransform as TransformGroup; + var translateTransform = transformGroup.Children.OfType<TranslateTransform>().FirstOrDefault(); + if (translateTransform == null) + { + transformGroup.Children.Add(new TranslateTransform() { X = x, Y = y }); + } + else + { + translateTransform.X = x; + translateTransform.Y = y; + } + + Point newPosition = new Point(x, y); + + if (Position != newPosition) + Position = newPosition; + } + } + + /// <summary> + /// Saves current size and position of the window in the IsolatedStorage. + /// The key of the settings is the Tag of the window (if not null). + /// </summary> + private void SaveSizeAndPosition() + { + if (IsWindowTagSet) + { + string positionKey = GetAppSettingsKey("Position"); + string sizeKey = GetAppSettingsKey("Size"); + + Point point = windowState == WindowState.Normal ? Position : previousPosition; + localStorage[positionKey] = point; + + Size size = windowState == WindowState.Normal ? new Size(ActualWidth, ActualHeight) : previousSize; + localStorage[sizeKey] = size; + } + } + + /// <summary> + /// Gets the application settings key used to store properties in the IsolatedStorage. + /// </summary> + /// <param name="key">The key of the property, e.g. "Position".</param> + /// <returns>Combined settings key or empty string.</returns> + private string GetAppSettingsKey(string key) + { + string tag = this.Tag as string; + return tag + ":" + key; + } + + /// <summary> + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// </summary> + public void Dispose() + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/FloatingWindow/FloatingWindowHost.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,1032 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using SilverFlow.Controls.Extensions; + +namespace SilverFlow.Controls +{ + /// <summary> + /// A Content Control containing floating windows. + /// </summary> + [TemplatePart(Name = PART_Root, Type = typeof(Grid))] + [TemplatePart(Name = PART_ContentRoot, Type = typeof(FrameworkElement))] + [TemplatePart(Name = PART_HostCanvas, Type = typeof(Canvas))] + [TemplatePart(Name = PART_ModalCanvas, Type = typeof(Canvas))] + [TemplatePart(Name = PART_IconBarContainer, Type = typeof(FrameworkElement))] + [TemplatePart(Name = PART_Overlay, Type = typeof(Grid))] + [TemplatePart(Name = PART_IconBar, Type = typeof(IconBar))] + [TemplatePart(Name = PART_BottomBar, Type = typeof(FrameworkElement))] + [TemplatePart(Name = PART_BootstrapButton, Type = typeof(BootstrapButton))] + [TemplatePart(Name = PART_BarContent, Type = typeof(ContentControl))] + [TemplateVisualState(Name = VSMSTATE_VisibleOverlay, GroupName = VSMGROUP_Overlay)] + [TemplateVisualState(Name = VSMSTATE_HiddenOverlay, GroupName = VSMGROUP_Overlay)] + [StyleTypedProperty(Property = PROPERTY_BottomBarStyle, StyleTargetType = typeof(Border))] + [StyleTypedProperty(Property = PROPERTY_BootstrapButtonStyle, StyleTargetType = typeof(BootstrapButton))] + [StyleTypedProperty(Property = PROPERTY_WindowIconStyle, StyleTargetType = typeof(WindowIcon))] + public class FloatingWindowHost : ContentControl + { + #region Constants + + // Template parts + private const string PART_Root = "PART_Root"; + private const string PART_ContentRoot = "PART_ContentRoot"; + private const string PART_HostCanvas = "PART_HostCanvas"; + private const string PART_ModalCanvas = "PART_ModalCanvas"; + private const string PART_IconBarContainer = "PART_IconBarContainer"; + private const string PART_Overlay = "PART_Overlay"; + private const string PART_IconBar = "PART_IconBar"; + private const string PART_BottomBar = "PART_BottomBar"; + private const string PART_BootstrapButton = "PART_BootstrapButton"; + private const string PART_BarContent = "PART_BarContent"; + + // VSM groups + private const string VSMGROUP_Overlay = "OverlayStates"; + + // VSM states + private const string VSMSTATE_VisibleOverlay = "VisibleOverlay"; + private const string VSMSTATE_HiddenOverlay = "HiddenOverlay"; + + // Style typed properties + private const string PROPERTY_BottomBarStyle = "BottomBarStyle"; + private const string PROPERTY_BootstrapButtonStyle = "BootstrapButtonStyle"; + private const string PROPERTY_WindowIconStyle = "WindowIconStyle"; + + // Thickness of resizing area. + private const double SnapinDistanceDefaultValue = 5.0; + + // Default icon size + private const double DefaultIconWidth = 120; + private const double DefaultIconHeight = 70; + + #endregion + + #region Member Fields + + /// <summary> + /// Current ZIndex of a child element + /// </summary> + private static int zIndex = 1; + + private Grid root; + private FrameworkElement contentRoot; + private Canvas hostCanvas; + private Canvas modalCanvas; + private FrameworkElement iconBarContainer; + private Grid overlay; + private IconBar iconBar; + private FrameworkElement bottomBar; + private BootstrapButton bootstrapButton; + private ContentControl barContent; + + private bool templateIsApplied; + + #endregion Member Fields + + #region public Style BottomBarStyle + + /// <summary> + /// Gets or sets the style of the BottomBar. + /// </summary> + public Style BottomBarStyle + { + get { return GetValue(BottomBarStyleProperty) as Style; } + set { SetValue(BottomBarStyleProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindowHost.BottomBarStyleProperty" /> dependency property. + /// </summary> + public static readonly DependencyProperty BottomBarStyleProperty = + DependencyProperty.Register( + "BottomBarStyle", + typeof(Style), + typeof(FloatingWindowHost), + new PropertyMetadata(BottomBarStylePropertyChanged)); + + /// <summary> + /// BottomBarStyle PropertyChangedCallback call back static function. + /// </summary> + /// <param name="d">FloatingWindowHost object whose BottomBarStyle property is changed.</param> + /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param> + private static void BottomBarStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + FloatingWindowHost host = (FloatingWindowHost)d; + if (host != null && host.bottomBar != null) + { + host.bottomBar.Style = e.NewValue as Style; + } + } + + #endregion + + #region public Style BootstrapButtonStyle + + /// <summary> + /// Gets or sets the style of the BootstrapButton. + /// </summary> + public Style BootstrapButtonStyle + { + get { return GetValue(BootstrapButtonStyleProperty) as Style; } + set { SetValue(BootstrapButtonStyleProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindowHost.BootstrapButtonStyleProperty" /> dependency property. + /// </summary> + public static readonly DependencyProperty BootstrapButtonStyleProperty = + DependencyProperty.Register( + "BootstrapButtonStyle", + typeof(Style), + typeof(FloatingWindowHost), + new PropertyMetadata(BootstrapButtonStylePropertyChanged)); + + /// <summary> + /// BootstrapButtonStyle PropertyChangedCallback call back static function. + /// </summary> + /// <param name="d">FloatingWindowHost object whose BootstrapButtonStyle property is changed.</param> + /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param> + private static void BootstrapButtonStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + FloatingWindowHost host = (FloatingWindowHost)d; + if (host != null && host.bottomBar != null) + { + host.bootstrapButton.Style = e.NewValue as Style; + } + } + + #endregion + + #region public Style WindowIconStyle + + /// <summary> + /// Gets or sets the style of the WindowIcon. + /// </summary> + public Style WindowIconStyle + { + get { return GetValue(WindowIconStyleProperty) as Style; } + set { SetValue(WindowIconStyleProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindowHost.WindowIconStyleProperty" /> dependency property. + /// </summary> + public static readonly DependencyProperty WindowIconStyleProperty = + DependencyProperty.Register( + "WindowIconStyle", + typeof(Style), + typeof(FloatingWindowHost), + new PropertyMetadata(WindowIconStylePropertyChanged)); + + /// <summary> + /// WindowIconStyle PropertyChangedCallback call back static function. + /// </summary> + /// <param name="d">FloatingWindowHost object whose WindowIconStyle property is changed.</param> + /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param> + private static void WindowIconStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + FloatingWindowHost host = (FloatingWindowHost)d; + if (host != null && host.iconBar != null) + { + host.iconBar.WindowIconStyle = e.NewValue as Style; + } + } + + #endregion + + #region public bool SnapinEnabled + + /// <summary> + /// Gets or sets a value indicating whether snap in is enabled. + /// </summary> + /// <value><c>true</c> if snap in is enabled; otherwise, <c>false</c>.</value> + public bool SnapinEnabled + { + get { return (bool)GetValue(SnapinEnabledProperty); } + set { SetValue(SnapinEnabledProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindowHost.SnapinEnabled" /> dependency property. + /// </summary> + /// <value> + /// The identifier for the <see cref="FloatingWindowHost.SnapinEnabled" /> dependency property. + /// </value> + public static readonly DependencyProperty SnapinEnabledProperty = + DependencyProperty.Register( + "SnapinEnabled", + typeof(bool), + typeof(FloatingWindowHost), + new PropertyMetadata(true, null)); + + #endregion + + #region public double SnapinDistance + + /// <summary> + /// Gets or sets a value of the snap in distance. + /// </summary> + /// <value>Snap in distance.</value> + public double SnapinDistance + { + get { return (double)GetValue(SnapinDistanceProperty); } + set { SetValue(SnapinDistanceProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindowHost.SnapinDistance" /> dependency property. + /// </summary> + /// <value> + /// The identifier for the <see cref="FloatingWindowHost.SnapinDistance" /> dependency property. + /// </value> + public static readonly DependencyProperty SnapinDistanceProperty = + DependencyProperty.Register( + "SnapinDistance", + typeof(double), + typeof(FloatingWindowHost), + new PropertyMetadata(SnapinDistanceDefaultValue, null)); + + #endregion + + #region public double SnapinMargin + + /// <summary> + /// Gets or sets a value of the snap in margin - distance between adjacent edges. + /// </summary> + /// <value>Snap in margin.</value> + public double SnapinMargin + { + get { return (double)GetValue(SnapinMarginProperty); } + set { SetValue(SnapinMarginProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindowHost.SnapinMargin" /> dependency property. + /// </summary> + /// <value> + /// The identifier for the <see cref="FloatingWindowHost.SnapinMargin" /> dependency property. + /// </value> + public static readonly DependencyProperty SnapinMarginProperty = + DependencyProperty.Register( + "SnapinMargin", + typeof(double), + typeof(FloatingWindowHost), + new PropertyMetadata(0.0, null)); + + #endregion + + #region public bool ShowMinimizedOnlyInIconbar + + /// <summary> + /// Gets or sets a value indicating whether to show only minimized windows in the iconbar. + /// </summary> + /// <value><c>true</c> if to show only minimized windows in the iconbar; otherwise, <c>false</c>.</value> + public bool ShowMinimizedOnlyInIconbar + { + get { return (bool)GetValue(ShowMinimizedOnlyInIconbarProperty); } + set { SetValue(ShowMinimizedOnlyInIconbarProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindowHost.ShowMinimizedOnlyInIconbarProperty" /> dependency property. + /// </summary> + /// <value> + /// The identifier for the <see cref="FloatingWindowHost.ShowMinimizedOnlyInIconbarProperty" /> dependency property. + /// </value> + public static readonly DependencyProperty ShowMinimizedOnlyInIconbarProperty = + DependencyProperty.Register( + "ShowMinimizedOnlyInIconbar", + typeof(bool), + typeof(FloatingWindowHost), + new PropertyMetadata(false, null)); + + #endregion + + #region public Brush OverlayBrush + + /// <summary> + /// Gets or sets the overlay color. + /// </summary> + /// <value>The overlay color.</value> + public Brush OverlayBrush + { + get { return (Brush)GetValue(OverlayBrushProperty); } + set { SetValue(OverlayBrushProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindowHost.OverlayBrush" /> dependency property. + /// </summary> + /// <value> + /// The identifier for the <see cref="FloatingWindowHost.OverlayBrush" /> dependency property. + /// </value> + public static readonly DependencyProperty OverlayBrushProperty = + DependencyProperty.Register( + "OverlayBrush", + typeof(Brush), + typeof(FloatingWindowHost), + new PropertyMetadata(new SolidColorBrush(Color.FromArgb(0x90, 0x20, 0x20, 0x30)), OnOverlayBrushPropertyChanged)); + + /// <summary> + /// OverlayBrushProperty PropertyChangedCallback call back static function. + /// </summary> + /// <param name="d">FloatingWindowHost object whose OverlayBrush property is changed.</param> + /// <param name="e">DependencyPropertyChangedEventArgs which contains the old and new values.</param> + private static void OnOverlayBrushPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + FloatingWindowHost host = (FloatingWindowHost)d; + + if (host != null && host.overlay != null) + host.overlay.Background = (Brush)e.NewValue; + } + + #endregion + + #region public double IconWidth + + /// <summary> + /// Gets or sets the width of the window's icon. + /// </summary> + /// <value>The width of the window's icon.</value> + public double IconWidth + { + get { return (double)GetValue(IconWidthProperty); } + set { SetValue(IconWidthProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindowHost.IconWidth" /> dependency property. + /// </summary> + /// <value> + /// The identifier for the <see cref="FloatingWindowHost.IconWidth" /> dependency property. + /// </value> + public static readonly DependencyProperty IconWidthProperty = + DependencyProperty.Register( + "IconWidth", + typeof(double), + typeof(FloatingWindowHost), + new PropertyMetadata(DefaultIconWidth, null)); + + #endregion + + #region public double IconHeight + + /// <summary> + /// Gets or sets the height of the window's icon. + /// </summary> + /// <value>The height of the window's icon.</value> + public double IconHeight + { + get { return (double)GetValue(IconHeightProperty); } + set { SetValue(IconHeightProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindowHost.IconHeight" /> dependency property. + /// </summary> + /// <value> + /// The identifier for the <see cref="FloatingWindowHost.IconHeight" /> dependency property. + /// </value> + public static readonly DependencyProperty IconHeightProperty = + DependencyProperty.Register( + "IconHeight", + typeof(double), + typeof(FloatingWindowHost), + new PropertyMetadata(DefaultIconHeight, null)); + + #endregion + + #region public object Bar + + /// <summary> + /// Gets or sets a control displayed in the BottomBar. + /// </summary> + /// <value>The control displayed in the BottomBar. The default is null.</value> + public object Bar + { + get { return (double)GetValue(BarProperty); } + set { SetValue(BarProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="FloatingWindowHost.Bar" /> dependency property. + /// </summary> + /// <value> + /// The identifier for the <see cref="FloatingWindowHost.Bar" /> dependency property. + /// </value> + public static readonly DependencyProperty BarProperty = + DependencyProperty.Register( + "Bar", + typeof(object), + typeof(FloatingWindowHost), + null); + + #endregion + + /// <summary> + /// Gets a collection of windows shown in the IconBar. + /// </summary> + /// <value>Collection of windows shown in the IconBar.</value> + public IOrderedEnumerable<FloatingWindow> WindowsInIconBar + { + get + { + var windows = from window in this.FloatingWindows + where window.IsOpen && window.ShowInIconbar && + !(ShowMinimizedOnlyInIconbar && window.WindowState != WindowState.Minimized) + orderby window.IconText + select window; + + return windows; + } + } + + /// <summary> + /// Gets the host panel, containing the floating windows. + /// </summary> + /// <value>The host panel.</value> + public Canvas HostPanel + { + get { return hostCanvas; } + } + + /// <summary> + /// Gets the floating windows collection. + /// </summary> + /// <value>The floating windows collection.</value> + public IEnumerable<FloatingWindow> FloatingWindows + { + get { return hostCanvas.Children.OfType<FloatingWindow>(); } + } + + /// <summary> + /// Gets or sets a value indicating whether the layout of the FloatingWindowHost is updated. + /// </summary> + /// <value> + /// <c>true</c> if the layout of the FloatingWindowHost is updated; otherwise, <c>false</c>. + /// </value> + internal bool IsLayoutUpdated { get; private set; } + + /// <summary> + /// Gets current modal window. + /// </summary> + /// <value>The modal window.</value> + private FloatingWindow ModalWindow + { + get { return modalCanvas.Children.OfType<FloatingWindow>().FirstOrDefault(); } + } + + /// <summary> + /// Initializes a new instance of the <see cref="FloatingWindowHost"/> class. + /// </summary> + public FloatingWindowHost() + { + DefaultStyleKey = typeof(FloatingWindowHost); + } + + /// <summary> + /// Builds the visual tree for the <see cref="FloatingWindowHost" /> control + /// when a new template is applied. + /// </summary> + public override void OnApplyTemplate() + { + UnsubscribeFromEvents(); + UnsubscribeFromTemplatePartEvents(); + + base.OnApplyTemplate(); + + root = GetTemplatePart<Grid>(PART_Root); + contentRoot = GetTemplatePart<FrameworkElement>(PART_ContentRoot); + hostCanvas = GetTemplatePart<Canvas>(PART_HostCanvas); + modalCanvas = GetTemplatePart<Canvas>(PART_ModalCanvas); + iconBarContainer = GetTemplatePart<FrameworkElement>(PART_IconBarContainer); + overlay = GetTemplatePart<Grid>(PART_Overlay); + iconBar = GetTemplatePart<IconBar>(PART_IconBar); + bottomBar = GetTemplatePart<FrameworkElement>(PART_BottomBar); + bootstrapButton = GetTemplatePart<BootstrapButton>(PART_BootstrapButton); + barContent = GetTemplatePart<ContentControl>(PART_BarContent); + + iconBar.FloatingWindowHost = this; + + SetStyles(); + SubscribeToTemplatePartEvents(); + SubscribeToEvents(); + + templateIsApplied = true; + } + + #region Events + + /// <summary> + /// Occurs when the <see cref="FloatingWindowHost" /> is loaded and its template is applied. + /// </summary> + public event EventHandler Rendered; + + /// <summary> + /// Occurs when the active <see cref="FloatingWindow" /> is changed. + /// </summary> + public event EventHandler<ActiveWindowChangedEventArgs> ActiveWindowChanged; + + #endregion Events + + /// <summary> + /// Adds the specified floating window to the collection of child elements of the host. + /// </summary> + /// <param name="window">The floating window.</param> + /// <exception cref="ArgumentNullException">Floating window is null.</exception> + public void Add(FloatingWindow window) + { + if (window == null) + throw new ArgumentNullException("window"); + + // Guarantee that the visual tree of the control is complete + if (!templateIsApplied) + templateIsApplied = ApplyTemplate(); + + if (!hostCanvas.Children.Contains(window)) + { + hostCanvas.Children.Add(window); + window.FloatingWindowHost = this; + } + } + + /// <summary> + /// Removes the specified floating window from the collection of child elements of the host. + /// </summary> + /// <param name="window">The floating window.</param> + public void Remove(FloatingWindow window) + { + if (window != null) + { + hostCanvas.Children.Remove(window); + modalCanvas.Children.Remove(window); + iconBar.Remove(window); + } + } + + /// <summary> + /// Closes all floating windows. + /// </summary> + public void CloseAllWindows() + { + HideIconBar(); + FloatingWindows.ToList().ForEach(x => x.Close()); + } + + /// <summary> + /// Shows the IconBar. + /// </summary> + public void ShowIconBar() + { + iconBar.Show(); + } + + /// <summary> + /// Hides the IconBar. + /// </summary> + public void HideIconBar() + { + iconBar.Hide(); + } + + /// <summary> + /// Updates the IconBar if it is open. + /// </summary> + public void UpdateIconBar() + { + iconBar.Update(); + } + + /// <summary> + /// Sets the specified floating window topmost, and set Focus on it. + /// </summary> + /// <param name="window">FloatingWindow to set topmost.</param> + /// <exception cref="ArgumentNullException">FloatingWindow is null.</exception> + public void SetTopmostWindow(FloatingWindow window) + { + if (window == null) + throw new ArgumentNullException("window"); + + FloatingWindow topmostWindow = GetTopmostWindow(); + if (topmostWindow == null || window != topmostWindow) + { + SetTopmost(window); + SetFocusToActiveWindow(window); + + if (!window.TopMost && !window.IsModal) + ShowTopmostWindows(); + + ActiveWindowChangedEventArgs e = new ActiveWindowChangedEventArgs(topmostWindow, window); + OnActiveWindowChanged(e); + } + } + + /// <summary> + /// Activates the topmost window and sets focus on it. + /// </summary> + public void ActivateTopmostWindow() + { + // First, try to activate a modal window, if exists + var topmostWindow = this.ModalWindow; + + if (topmostWindow == null) + topmostWindow = GetTopmostWindow(); + + SetFocusToActiveWindow(topmostWindow); + + ActiveWindowChangedEventArgs e = new ActiveWindowChangedEventArgs(null, topmostWindow); + OnActiveWindowChanged(e); + } + + /// <summary> + /// Gets Snap In bounds as bounds of the host and all open windows except the specified one. + /// </summary> + /// <param name="windowToExclude">The window to exclude from the list.</param> + /// <returns>List of bounding rectangles.</returns> + internal IEnumerable<Rect> GetSnapinBounds(FloatingWindow windowToExclude) + { + List<Rect> bounds = new List<Rect>(); + + // Add host bounds + bounds.Add(hostCanvas.GetActualBoundingRectangle()); + + if (!overlay.IsVisible()) + { + foreach (var window in FloatingWindows) + { + if (window != windowToExclude && window.IsOpen) + bounds.Add(window.BoundingRectangle); + } + } + + return bounds; + } + + /// <summary> + /// Shows the overlay, moves the window to the "modal" layer and hides the IconBar. + /// </summary> + /// <param name="modalWindow">The modal window.</param> + internal void ShowWindowAsModal(FloatingWindow modalWindow) + { + HideIconBar(); + VisualStateManager.GoToState(this, VSMSTATE_VisibleOverlay, true); + + MoveWindowToModalLayer(modalWindow); + } + + /// <summary> + /// Removes the overlay under the modal window. + /// </summary> + internal void RemoveOverlay() + { + if (overlay.IsVisible()) + { + FloatingWindow topmostWindow = null; + + if (FloatingWindows.Count(x => x.IsOpen && x.IsModal) == 0) + { + // If there are no more modal windows - remove the overlay + VisualStateManager.GoToState(this, VSMSTATE_HiddenOverlay, true); + + topmostWindow = GetTopmostWindow(); + } + else + { + topmostWindow = GetTopmostModalWindow(); + + if (topmostWindow != null) + topmostWindow.MoveToContainer(modalCanvas); + } + + SetFocusToActiveWindow(topmostWindow); + } + } + + /// <summary> + /// Moves the window to the "modal" layer. + /// </summary> + /// <param name="modalWindow">The modal window.</param> + private void MoveWindowToModalLayer(FloatingWindow modalWindow) + { + FloatingWindow window = this.ModalWindow; + if (window != null && window != modalWindow) + { + // If there is already a modal window - move it to the HostCanvas + window.MoveToContainer(hostCanvas); + SetTopmost(window); + } + + modalWindow.MoveToContainer(modalCanvas); + } + + /// <summary> + /// Raises the <see cref="FloatingWindowHost.ActiveWindowChanged" /> event. + /// </summary> + /// <param name="e">The event data.</param> + protected virtual void OnActiveWindowChanged(ActiveWindowChangedEventArgs e) + { + EventHandler<ActiveWindowChangedEventArgs> handler = ActiveWindowChanged; + + if (handler != null) + handler(this, e); + } + + /// <summary> + /// Shows a floating window when the layout is updated. + /// </summary> + /// <param name="action">A method that displays a window in the specified coordinates.</param> + /// <param name="point">Coordinates of the upper-left corner of the window.</param> + internal void ShowWindow(Action<Point> action, Point point) + { + if (IsLayoutUpdated) + { + action(point); + } + else + { + this.Rendered += (s, e) => { action(point); }; + } + } + + /// <summary> + /// Shows a floating window when the layout is updated. + /// </summary> + /// <param name="action">A method that displays a window taking into account specified margins.</param> + /// <param name="margins">Window margins.</param> + internal void ShowWindow(Action<Thickness> action, Thickness margins) + { + if (IsLayoutUpdated) + { + action(margins); + } + else + { + this.Rendered += (s, e) => { action(margins); }; + + } + } + + /// <summary> + /// Subscribes to the events on the template parts. + /// </summary> + private void SubscribeToTemplatePartEvents() + { + bootstrapButton.Click += new RoutedEventHandler(BootstrapButton_Click); + iconBar.Opened += new EventHandler(IconBarVisibilityChanged); + iconBar.Closed += new EventHandler(IconBarVisibilityChanged); + hostCanvas.SizeChanged += new SizeChangedEventHandler(HostCanvas_SizeChanged); + modalCanvas.SizeChanged += new SizeChangedEventHandler(ModalCanvas_SizeChanged); + iconBarContainer.SizeChanged += new SizeChangedEventHandler(IconBarContainer_SizeChanged); + } + + /// <summary> + /// Unsubscribe from the events that are subscribed on the template part elements. + /// </summary> + private void UnsubscribeFromTemplatePartEvents() + { + if (bootstrapButton != null) + bootstrapButton.Click -= new RoutedEventHandler(BootstrapButton_Click); + + if (iconBar != null) + iconBar.Opened -= new EventHandler(IconBarVisibilityChanged); + + if (iconBar != null) + iconBar.Closed -= new EventHandler(IconBarVisibilityChanged); + + if (hostCanvas != null) + hostCanvas.SizeChanged -= new SizeChangedEventHandler(HostCanvas_SizeChanged); + + if (modalCanvas != null) + modalCanvas.SizeChanged -= new SizeChangedEventHandler(ModalCanvas_SizeChanged); + + if (iconBarContainer != null) + iconBarContainer.SizeChanged -= new SizeChangedEventHandler(IconBarContainer_SizeChanged); + } + + /// <summary> + /// Subscribes to the events the control shall handle. + /// </summary> + private void SubscribeToEvents() + { + this.MouseLeftButtonDown += new MouseButtonEventHandler(FloatingWindowHost_MouseLeftButtonDown); + this.LayoutUpdated += new EventHandler(FloatingWindowHost_LayoutUpdated); + } + + /// <summary> + /// Unsubscribes from the subscribed events. + /// </summary> + private void UnsubscribeFromEvents() + { + this.MouseLeftButtonDown -= new MouseButtonEventHandler(FloatingWindowHost_MouseLeftButtonDown); + this.LayoutUpdated -= new EventHandler(FloatingWindowHost_LayoutUpdated); + } + + /// <summary> + /// Handles the first LayoutUpdated event of the FloatingWindowHost control to raise the OnRendered event. + /// </summary> + /// <param name="sender">The source of the event.</param> + /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param> + private void FloatingWindowHost_LayoutUpdated(object sender, EventArgs e) + { + if (!IsLayoutUpdated) + { + this.LayoutUpdated -= new EventHandler(FloatingWindowHost_LayoutUpdated); + IsLayoutUpdated = true; + OnRendered(EventArgs.Empty); + } + } + + /// <summary> + /// Raises the <see cref="E:Rendered"/> event. + /// Occures when the control template is applied and the control can be rendered. + /// </summary> + /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param> + protected virtual void OnRendered(EventArgs e) + { + EventHandler handler = Rendered; + + if (handler != null) + handler(this, e); + } + + /// <summary> + /// Gets the topmost open FloatingWindow. + /// </summary> + /// <returns>The topmost open FloatingWindow.</returns> + private FloatingWindow GetTopmostWindow() + { + var topmost = (from window in FloatingWindows + where !window.TopMost && window.IsOpen && window.WindowState != WindowState.Minimized + select new + { + Window = window, + ZIndex = Canvas.GetZIndex(window) + } + ).ToList().OrderBy(x => x.ZIndex).LastOrDefault(); + + return topmost != null ? topmost.Window : null; + } + + /// <summary> + /// Gets the topmost modal FloatingWindow on the HostCanvas. + /// </summary> + /// <returns>The topmost modal FloatingWindow.</returns> + private FloatingWindow GetTopmostModalWindow() + { + var topmost = (from window in FloatingWindows + where window.IsModal && window.IsOpen + select new + { + Window = window, + ZIndex = Canvas.GetZIndex(window) + } + ).ToList().OrderBy(x => x.ZIndex).LastOrDefault(); + + return topmost != null ? topmost.Window : null; + } + + /// <summary> + /// Shows the topmost windows in front of other windows. + /// </summary> + private void ShowTopmostWindows() + { + FloatingWindows + .Where(x => x.IsOpen && x.TopMost).ToList() + .ForEach(x => SetTopmost(x)); + } + + /// <summary> + /// Sets the specified UIElement topmost. + /// </summary> + /// <param name="element">UIElement to set topmost.</param> + /// <exception cref="ArgumentNullException">UIElement is null.</exception> + private void SetTopmost(UIElement element) + { + if (element == null) + throw new ArgumentNullException("element"); + + zIndex++; + Canvas.SetZIndex(element, zIndex); + } + + /// <summary> + /// Attempts to set the focus on the FloatingWindow. + /// </summary> + /// <param name="window">The window.</param> + private void SetFocusToActiveWindow(FloatingWindow window) + { + if (window != null && !window.TopMost) + window.Focus(); + } + + /// <summary> + /// Handles the MouseLeftButtonDown event of the FloatingWindowHost control. + /// Closes the IconBar on mouse left click. + /// </summary> + /// <param name="sender">The source of the event.</param> + /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param> + private void FloatingWindowHost_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + e.Handled = false; + HideIconBar(); + } + + /// <summary> + /// Handles the Click event of the BootstrapButton. + /// </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.IsOpen) + ShowIconBar(); + else + HideIconBar(); + } + + /// <summary> + /// Handles the IconBar Visibility Changed event. + /// </summary> + /// <param name="sender">The sender.</param> + /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param> + private void IconBarVisibilityChanged(object sender, EventArgs e) + { + bootstrapButton.IsOpen = iconBar.IsOpen; + } + + /// <summary> + /// Handles the SizeChanged event of the HostCanvas control to set its clipping region. + /// </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 HostCanvas_SizeChanged(object sender, SizeChangedEventArgs e) + { + hostCanvas.Clip = new RectangleGeometry() + { + Rect = hostCanvas.GetActualBoundingRectangle() + }; + } + + /// <summary> + /// Handles the SizeChanged event of the ModalCanvas control to set its clipping region. + /// </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 ModalCanvas_SizeChanged(object sender, SizeChangedEventArgs e) + { + modalCanvas.Clip = new RectangleGeometry() + { + Rect = modalCanvas.GetActualBoundingRectangle() + }; + } + + /// <summary> + /// Handles the SizeChanged event of the IconBarContainer control to set its clipping region. + /// </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 IconBarContainer_SizeChanged(object sender, SizeChangedEventArgs e) + { + iconBarContainer.Clip = new RectangleGeometry() + { + Rect = iconBarContainer.GetActualBoundingRectangle() + }; + } + + /// <summary> + /// Sets styles that are applied for different template parts. + /// </summary> + private void SetStyles() + { + if (bottomBar != null && this.BottomBarStyle != null) + bottomBar.Style = this.BottomBarStyle; + + if (bootstrapButton != null && this.BootstrapButtonStyle != null) + bootstrapButton.Style = this.BootstrapButtonStyle; + + if (iconBar != null && this.WindowIconStyle != null) + iconBar.WindowIconStyle = this.WindowIconStyle; + } + + /// <summary> + /// Gets the FrameworkElement template part with the specified name. + /// </summary> + /// <typeparam name="T">The template part type.</typeparam> + /// <param name="partName">The template part name.</param> + /// <returns>The requested element.</returns> + /// <exception cref="NotImplementedException">The template part not found.</exception> + private T GetTemplatePart<T>(string partName) where T : class + { + T part = this.GetTemplateChild(partName) as T; + + if (part == null) + throw new NotImplementedException(string.Format(CultureInfo.InvariantCulture, "Template Part {0} is required.", partName)); + + return part; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/FloatingWindow/IconBar.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,527 @@ +using System; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media.Animation; +using SilverFlow.Controls.Extensions; +using SilverFlow.Controls.Helpers; + +namespace SilverFlow.Controls +{ + /// <summary> + /// IconBar containing window icons. + /// </summary> + [TemplatePart(Name = PART_LayoutRoot, Type = typeof(FrameworkElement))] + [TemplatePart(Name = PART_FixedBar, Type = typeof(Border))] + [TemplatePart(Name = PART_SlidingBar, Type = typeof(Border))] + [TemplatePart(Name = PART_Carousel, Type = typeof(StackPanel))] + [TemplateVisualState(Name = VSMSTATE_StateOpen, GroupName = VSMGROUP_States)] + [TemplateVisualState(Name = VSMSTATE_StateClosed, GroupName = VSMGROUP_States)] + [StyleTypedProperty(Property = PROPERTY_TitleStyle, StyleTargetType = typeof(Border))] + [StyleTypedProperty(Property = PROPERTY_WindowIconStyle, StyleTargetType = typeof(WindowIcon))] + public class IconBar : ContentControl, INotifyPropertyChanged + { + // Template parts + private const string PART_LayoutRoot = "PART_LayoutRoot"; + private const string PART_FixedBar = "PART_FixedBar"; + private const string PART_SlidingBar = "PART_SlidingBar"; + private const string PART_Carousel = "PART_Carousel"; + + // VSM groups + private const string VSMGROUP_States = "VisualStateGroup"; + + // VSM states + private const string VSMSTATE_StateOpen = "Open"; + private const string VSMSTATE_StateClosed = "Closed"; + + // Style typed properties + private const string PROPERTY_TitleStyle = "IconBarStyle"; + private const string PROPERTY_WindowIconStyle = "WindowIconStyle"; + + // Animation duration in milliseconds + private const double SlidingDurationInMilliseconds = 200; + + #region public Style IconBarStyle + + /// <summary> + /// Gets or sets the style of the IconBar. + /// </summary> + public Style IconBarStyle + { + get { return GetValue(IconBarStyleProperty) as Style; } + set { SetValue(IconBarStyleProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="IconBar.IconBarStyleProperty" /> dependency property. + /// </summary> + public static readonly DependencyProperty IconBarStyleProperty = + DependencyProperty.Register( + "IconBarStyle", + typeof(Style), + typeof(IconBar), + new PropertyMetadata(IconBarStylePropertyChanged)); + + /// <summary> + /// IconBarStyle PropertyChangedCallback call back static function. + /// </summary> + /// <param name="d">IconBar object whose IconBarStyle property is changed.</param> + /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param> + private static void IconBarStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + IconBar iconBar = (IconBar)d; + if (iconBar != null && iconBar.fixedBar != null) + { + iconBar.fixedBar.Style = e.NewValue as Style; + } + } + + #endregion + + #region public Style WindowIconStyle + + /// <summary> + /// Gets or sets the style of the WindowIcon. + /// </summary> + public Style WindowIconStyle + { + get { return GetValue(WindowIconStyleProperty) as Style; } + set { SetValue(WindowIconStyleProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="IconBar.WindowIconStyleProperty" /> dependency property. + /// </summary> + public static readonly DependencyProperty WindowIconStyleProperty = + DependencyProperty.Register( + "WindowIconStyle", + typeof(Style), + typeof(IconBar), + null); + + #endregion + + private FrameworkElement layoutRoot; + private Border fixedBar; + private Border slidingBar; + private StackPanel carousel; + private Storyboard closingStoryboard; + private Storyboard openingStoryboard; + private bool isOpen; + private double slidingBarPosition; + private IVisualHelper visualHelper; + + /// <summary> + /// Gets or sets a value indicating whether the IconBar is open. + /// </summary> + /// <value><c>true</c> if the IconBar is open; otherwise, <c>false</c>.</value> + public bool IsOpen + { + get { return isOpen; } + private set + { + if (value != isOpen) + { + isOpen = value; + OnPropertyChanged(new PropertyChangedEventArgs("IsOpen")); + } + } + } + + /// <summary> + /// Gets or sets the FloatingWindowHost containing the IconBar. + /// </summary> + /// <value>FloatingWindowHost containing the IconBar.</value> + public FloatingWindowHost FloatingWindowHost { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="IconBar"/> class. + /// </summary> + public IconBar() + { + DefaultStyleKey = typeof(IconBar); + visualHelper = new VisualHelper(); + } + + /// <summary> + /// Occurs when the <see cref="IconBar" /> is opened. + /// </summary> + public event EventHandler Opened; + + /// <summary> + /// Occurs when the <see cref="IconBar" /> is closed. + /// </summary> + public event EventHandler Closed; + + #region INotifyPropertyChanged implementation + /// <summary> + /// Occurs when a property changed. + /// </summary> + public event PropertyChangedEventHandler PropertyChanged; + + /// <summary> + /// Raises the <see cref="E:PropertyChanged"/> event. + /// </summary> + /// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param> + public void OnPropertyChanged(PropertyChangedEventArgs e) + { + if (PropertyChanged != null) + PropertyChanged(this, e); + } + #endregion + + /// <summary> + /// Builds the visual tree for the <see cref="IconBar" /> control + /// when a new template is applied. + /// </summary> + public override void OnApplyTemplate() + { + UnsubscribeFromEvents(); + + base.OnApplyTemplate(); + + layoutRoot = GetTemplatePart<FrameworkElement>(PART_LayoutRoot); + fixedBar = GetTemplatePart<Border>(PART_FixedBar); + slidingBar = GetTemplatePart<Border>(PART_SlidingBar); + carousel = GetTemplatePart<StackPanel>(PART_Carousel); + + SetStyles(); + GetStoryboards(); + SubscribeToEvents(); + } + + /// <summary> + /// Shows the IconBar. + /// </summary> + public void Show() + { + if (!IsOpen) + { + FillCarousel(); + SetSlidingBarPosition(0); + VisualStateManager.GoToState(this, VSMSTATE_StateOpen, true); + } + } + + /// <summary> + /// Hides the IconBar. + /// </summary> + public void Hide() + { + if (IsOpen) + { + VisualStateManager.GoToState(this, VSMSTATE_StateClosed, true); + } + } + + /// <summary> + /// Updates the IconBar if it is open. + /// </summary> + public void Update() + { + if (IsOpen) + { + FillCarousel(); + } + } + + /// <summary> + /// Removes the specified window from the IconBar. + /// </summary> + /// <param name="window">The window to remove from the IconBar.</param> + public void Remove(FloatingWindow window) + { + if (window != null) + { + var icon = (from windowIcon in carousel.Children.OfType<WindowIcon>() + where windowIcon.Window == window + select windowIcon).FirstOrDefault(); + + if (icon != null) + { + icon.Click -= new RoutedEventHandler(Icon_Click); + carousel.Children.Remove(icon); + } + } + } + + /// <summary> + /// Gets the storyboards defined in the <see cref="IconBar" /> style. + /// </summary> + private void GetStoryboards() + { + 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) + { + openingStoryboard = (from state in states + where state.Name == IconBar.VSMSTATE_StateOpen + select state.Storyboard).FirstOrDefault(); + + closingStoryboard = (from state in states + where state.Name == IconBar.VSMSTATE_StateClosed + select state.Storyboard).FirstOrDefault(); + } + } + } + + /// <summary> + /// Subscribes to the events after new template is applied. + /// </summary> + private void SubscribeToEvents() + { + if (closingStoryboard != null) + closingStoryboard.Completed += new EventHandler(Closing_Completed); + + if (openingStoryboard != null) + openingStoryboard.Completed += new EventHandler(Opening_Completed); + + if (fixedBar != null) + fixedBar.MouseMove += new MouseEventHandler(Bar_MouseMove); + + if (fixedBar != null) + fixedBar.SizeChanged += new SizeChangedEventHandler(FixedBar_SizeChanged); + } + + /// <summary> + /// Unsubscribe from events. + /// </summary> + private void UnsubscribeFromEvents() + { + if (closingStoryboard != null) + closingStoryboard.Completed -= new EventHandler(Closing_Completed); + + if (openingStoryboard != null) + openingStoryboard.Completed -= new EventHandler(Opening_Completed); + + if (fixedBar != null) + fixedBar.MouseMove += new MouseEventHandler(Bar_MouseMove); + + if (fixedBar != null) + fixedBar.SizeChanged -= new SizeChangedEventHandler(FixedBar_SizeChanged); + } + + /// <summary> + /// Sets styles that are applied for different template parts. + /// </summary> + private void SetStyles() + { + if (fixedBar != null && this.IconBarStyle != null) + fixedBar.Style = this.IconBarStyle; + } + + /// <summary> + /// Executed when the Closing storyboard ends. + /// </summary> + /// <param name="sender">Sender object.</param> + /// <param name="e">Event args.</param> + private void Closing_Completed(object sender, EventArgs e) + { + IsOpen = false; + ClearCarousel(); + OnClosed(EventArgs.Empty); + } + + /// <summary> + /// Executed when the Opening storyboard finishes. + /// </summary> + /// <param name="sender">Sender object.</param> + /// <param name="e">Event args.</param> + private void Opening_Completed(object sender, EventArgs e) + { + IsOpen = true; + OnOpened(EventArgs.Empty); + } + + /// <summary> + /// Raises the <see cref="E:Opened"/> event. + /// </summary> + /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param> + protected virtual void OnOpened(EventArgs e) + { + EventHandler handler = Opened; + + if (handler != null) + handler(this, e); + } + + /// <summary> + /// Raises the <see cref="E:Closed"/> event. + /// </summary> + /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param> + protected virtual void OnClosed(EventArgs e) + { + EventHandler handler = Closed; + + if (handler != null) + handler(this, e); + } + + /// <summary> + /// Add windows icons to the carousel. + /// </summary> + private void FillCarousel() + { + ClearCarousel(); + + Style style = this.WindowIconStyle ?? Application.Current.Resources["WindowIconStyle"] as Style; + foreach (var window in this.FloatingWindowHost.WindowsInIconBar) + { + WindowIcon icon = new WindowIcon() + { + Style = style, + Title = window.IconText, + Thumbnail = window.WindowThumbnail, + FlowDirection = window.FlowDirection, + Window = window, + IconWidth = this.FloatingWindowHost.IconWidth, + IconHeight = this.FloatingWindowHost.IconHeight + }; + + icon.Click += new RoutedEventHandler(Icon_Click); + carousel.Children.Add(icon); + } + } + + /// <summary> + /// Remove Icon Click event handlers and clear the carousel. + /// </summary> + private void ClearCarousel() + { + foreach (var icon in carousel.Children.OfType<WindowIcon>()) + { + icon.Click -= new RoutedEventHandler(Icon_Click); + } + + carousel.Children.Clear(); + } + + /// <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) + { + this.Hide(); + icon.Window.RestoreWindow(); + } + } + + /// <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) + { + // Get absolute mouse position + Point mousePosition = e.GetPosition(null); + + var icon = carousel.Children.OfType<WindowIcon>().FirstOrDefault(); + + // If there is at least one icon on the bar + if (icon != null) + { + double a = e.GetPosition(fixedBar).X; + double b = fixedBar.ActualWidth - fixedBar.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 = Math.Round((a / width) * (b - c)); + + if (x != slidingBarPosition) + { + Storyboard storyboard = slidingBar.AnimateDoubleProperty("(Canvas.Left)", null, x, SlidingDurationInMilliseconds); + storyboard.Completed += (s, args) => + { + // Select an icon on storyboard completion + // That is necessary because MouseOver state won't be proceeded correctly + SlidingBarStoryboardCompleted(mousePosition); + slidingBarPosition = x; + }; + } + } + } + } + } + + /// <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 visualHelper.FindElementsInCoordinates(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; + } + } + + /// <summary> + /// Handles the SizeChanged event of the FixedBar control. + /// Sets the initial position of the sliding bar. + /// </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 FixedBar_SizeChanged(object sender, SizeChangedEventArgs e) + { + if (slidingBarPosition != 0) + SetSlidingBarPosition(0); + } + + /// <summary> + /// Sets the sliding bar position. + /// </summary> + /// <param name="position">X-coordinate of the sliding bar.</param> + private void SetSlidingBarPosition(double x) + { + slidingBarPosition = x; + Canvas.SetLeft(slidingBar, slidingBarPosition); + } + + /// <summary> + /// Gets the FrameworkElement template part with the specified name. + /// </summary> + /// <typeparam name="T">The template part type.</typeparam> + /// <param name="partName">The template part name.</param> + /// <returns>The requested element.</returns> + /// <exception cref="NotImplementedException">The template part not found.</exception> + private T GetTemplatePart<T>(string partName) where T : class + { + T part = this.GetTemplateChild(partName) as T; + + if (part == null) + throw new NotImplementedException(string.Format(CultureInfo.InvariantCulture, "Template Part {0} is required.", partName)); + + return part; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/FloatingWindow/WindowIcon.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,210 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using SilverFlow.Controls.Extensions; + +namespace SilverFlow.Controls +{ + /// <summary> + /// Window icon containing a thumbnail and title of the windows. + /// </summary> + [TemplatePart(Name = PART_Border, Type = typeof(Border))] + [TemplatePart(Name = PART_Title, Type = typeof(TextBlock))] + [TemplatePart(Name = PART_Thumbnail, Type = typeof(Image))] + [StyleTypedProperty(Property = PROPERTY_IconBorderStyle, StyleTargetType = typeof(Border))] + public class WindowIcon : Button + { + // Template parts + private const string PART_Border = "PART_Border"; + private const string PART_Title = "PART_Title"; + private const string PART_Thumbnail = "PART_Thumbnail"; + + // VSM states + private const string VSMSTATE_StateNormal = "Normal"; + private const string VSMSTATE_StateMouseOver = "MouseOver"; + + // Style typed properties + private const string PROPERTY_IconBorderStyle = "IconBorderStyle"; + + #region public Style IconBorderStyle + + /// <summary> + /// Gets or sets the style of the WindowIcon. + /// </summary> + public Style IconBorderStyle + { + get { return GetValue(IconBorderStyleProperty) as Style; } + set { SetValue(IconBorderStyleProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="IconBar.IconBorderStyleProperty" /> dependency property. + /// </summary> + public static readonly DependencyProperty IconBorderStyleProperty = + DependencyProperty.Register( + "IconBorderStyle", + typeof(Style), + typeof(WindowIcon), + new PropertyMetadata(IconBorderStylePropertyChanged)); + + /// <summary> + /// IconBorderStyle PropertyChangedCallback call back static function. + /// </summary> + /// <param name="d">WindowIcon object whose IconBorderStyle property is changed.</param> + /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param> + private static void IconBorderStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + WindowIcon windowIcon = (WindowIcon)d; + if (windowIcon != null && windowIcon.border != null) + { + windowIcon.border.Style = e.NewValue as Style; + } + } + + #endregion + + #region public double IconWidth + + /// <summary> + /// Gets or sets the width of the window's icon. + /// </summary> + /// <value>The width of the window's icon.</value> + public double IconWidth + { + get { return (double)GetValue(IconWidthProperty); } + set { SetValue(IconWidthProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="WindowIcon.IconWidth" /> dependency property. + /// </summary> + /// <value> + /// The identifier for the <see cref="WindowIcon.IconWidth" /> dependency property. + /// </value> + public static readonly DependencyProperty IconWidthProperty = + DependencyProperty.Register( + "IconWidth", + typeof(double), + typeof(WindowIcon), + null); + + #endregion + + #region public double IconHeight + + /// <summary> + /// Gets or sets the height of the window's icon. + /// </summary> + /// <value>The height of the window's icon.</value> + public double IconHeight + { + get { return (double)GetValue(IconHeightProperty); } + set { SetValue(IconHeightProperty, value); } + } + + /// <summary> + /// Identifies the <see cref="WindowIcon.IconHeight" /> dependency property. + /// </summary> + /// <value> + /// The identifier for the <see cref="WindowIcon.IconHeight" /> dependency property. + /// </value> + public static readonly DependencyProperty IconHeightProperty = + DependencyProperty.Register( + "IconHeight", + typeof(double), + typeof(WindowIcon), + null); + + #endregion + + private Border border; + private TextBlock title; + private Image thumbnail; + private bool selected; + + /// <summary> + /// Gets or sets the window title that is displayed on the icon />. + /// </summary> + /// <value>The title displayed on the icon.</value> + public string Title { get; set; } + + /// <summary> + /// Gets or sets the thumbnail. + /// </summary> + /// <value>The thumbnail.</value> + public ImageSource Thumbnail { get; set; } + + /// <summary> + /// Gets or sets the FloatingWindow associated with the icon. + /// </summary> + /// <value>Floating window.</value> + public FloatingWindow Window { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this <see cref="WindowIcon"/> is selected. + /// </summary> + /// <value><c>true</c> if selected; otherwise, <c>false</c>.</value> + public bool Selected + { + get { return selected; } + set + { + if (value != selected) + { + selected = value; + VisualStateManager.GoToState( + this, + value ? VSMSTATE_StateMouseOver : VSMSTATE_StateNormal, + true); + } + } + } + + /// <summary> + /// Builds the visual tree for the <see cref="WindowIcon" /> control + /// when a new template is applied. + /// </summary> + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + border = GetTemplatePart<Border>(PART_Border); + title = GetTemplatePart<TextBlock>(PART_Title); + thumbnail = GetTemplatePart<Image>(PART_Thumbnail); + + SetStyles(); + + title.Text = Title; + title.FlowDirection = this.FlowDirection; + thumbnail.Source = Thumbnail; + } + + /// <summary> + /// Sets styles that are applied for different template parts. + /// </summary> + private void SetStyles() + { + if (border != null && this.IconBorderStyle != null) + border.Style = this.IconBorderStyle; + } + + /// <summary> + /// Gets the FrameworkElement template part with the specified name. + /// </summary> + /// <typeparam name="T">The template part type.</typeparam> + /// <param name="partName">The template part name.</param> + /// <returns>The requested element.</returns> + /// <exception cref="NotImplementedException">The template part not found.</exception> + private T GetTemplatePart<T>(string partName) where T : class + { + T part = this.GetTemplateChild(partName) as T; + + if (part == null) + throw new NotImplementedException(string.Format(CultureInfo.InvariantCulture, "Template Part {0} is required.", partName)); + + return part; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/Geometry/Distance.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,73 @@ +using SilverFlow.Controls.Extensions; + +namespace SilverFlow.Geometry +{ + /// <summary> + /// Defines displacement used for correction of window position or size. + /// </summary> + public class Distance + { + /// <summary> + /// Gets or sets displacement by X coordinate. + /// </summary> + /// <value>Displacement by X coordinate.</value> + public double X { get; set; } + + /// <summary> + /// Gets or sets displacement by Y coordinate. + /// </summary> + /// <value>Displacement by Y coordinate.</value> + public double Y { get; set; } + + /// <summary> + /// Gets a value indicating whether this instance is nonzero. + /// </summary> + /// <value> + /// <c>true</c> if this instance is nonzero; otherwise, <c>false</c>. + /// </value> + public bool IsNonzero + { + get + { + return !double.IsNaN(X) && !double.IsNaN(Y) && (X != 0 || Y != 0); + } + } + + /// <summary> + /// Initializes a new instance of the <see cref="Distance"/> class. + /// </summary> + public Distance() + { + X = double.NaN; + Y = double.NaN; + } + + /// <summary> + /// Initializes a new instance of the <see cref="Distance"/> class. + /// </summary> + /// <param name="x">Displacement by X coordinate.</param> + /// <param name="y">Displacement by Y coordinate.</param> + public Distance(double x, double y) + { + X = x; + Y = y; + } + + /// <summary> + /// Gets the smallest of two distances. + /// </summary> + /// <param name="first">First distance.</param> + /// <param name="second">Second distance.</param> + /// <returns>Smallest distance.</returns> + public static Distance Min(Distance first, Distance second) + { + Distance distance = new Distance + { + X = MathExtensions.AbsMin(first.X, second.X), + Y = MathExtensions.AbsMin(first.Y, second.Y) + }; + + return distance; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/Geometry/Trail.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,41 @@ +using System; +using System.Windows; + +namespace SilverFlow.Geometry +{ + /// <summary> + /// Mouse pointer trail. + /// </summary> + public class Trail + { + /// <summary> + /// Gets or sets mouse position. + /// </summary> + /// <value>The position.</value> + public Point Position { get; set; } + + /// <summary> + /// Gets or sets the timestamp. + /// </summary> + /// <value>The timestamp.</value> + public DateTime Timestamp { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="Trail"/> class. + /// </summary> + public Trail() + { + } + + /// <summary> + /// Initializes a new instance of the <see cref="Trail"/> class. + /// </summary> + /// <param name="point">Point.</param> + /// <param name="time">Time.</param> + public Trail(Point point, DateTime time) + { + this.Position = point; + this.Timestamp = time; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/Geometry/Vector2.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,156 @@ +using System; +using System.Windows; +using SilverFlow.Controls.Extensions; + +namespace SilverFlow.Geometry +{ + /// <summary> + /// Defines two-dimensional vector. + /// </summary> + public class Vector2 + { + /// <summary> + /// Gets or sets the starting point of the vector. + /// </summary> + /// <value>The starting point.</value> + public Point Start { get; set; } + + /// <summary> + /// Gets or sets the ending point of the vector. + /// </summary> + /// <value>The ending point.</value> + public Point End { get; set; } + + /// <summary> + /// Gets a value indicating whether this instance is not set. + /// </summary> + /// <value><c>true</c> if this instance is not set; otherwise, <c>false</c>.</value> + public bool IsNaN + { + get + { + return this.Start.IsNotSet() || this.End.IsNotSet(); + } + } + + /// <summary> + /// Gets a value indicating whether the vector is of zero length. + /// </summary> + /// <value><c>true</c> if the vector is of zero length; otherwise, <c>false</c>.</value> + public bool IsZero + { + get + { + return this.IsNaN ? true : this.Start == this.End; + } + } + + /// <summary> + /// Gets a value indicating whether the vector is vertical. + /// </summary> + /// <value> + /// <c>true</c> if the vector is vertical; otherwise, <c>false</c>. + /// </value> + public bool IsVertical + { + get + { + return !this.IsZero && (this.Start.X == this.End.X); + } + } + + /// <summary> + /// Gets a value indicating whether the vector is horizontal. + /// </summary> + /// <value> + /// <c>true</c> if the vector is horizontal; otherwise, <c>false</c>. + /// </value> + public bool IsHorizontal + { + get + { + return !this.IsZero && (this.Start.Y == this.End.Y); + } + } + + /// <summary> + /// Gets the length of the vector by X-coordinate. + /// </summary> + /// <value>The length by X-coordinate.</value> + public double LengthX + { + get + { + return this.IsNaN ? 0 : (this.End.X - this.Start.X); + } + } + + /// <summary> + /// Gets the length of the vector by Y-coordinate. + /// </summary> + /// <value>The length by Y-coordinate.</value> + public double LengthY + { + get + { + return this.IsNaN ? 0 : (this.End.Y - this.Start.Y); + } + } + + /// <summary> + /// Gets the length of the vector. + /// </summary> + /// <value>The length of the vector.</value> + public double Length + { + get + { + if (this.IsNaN) + return 0; + + return Math.Sqrt(LengthX * LengthX + LengthY * LengthY); + } + } + + /// <summary> + /// Initializes a new instance of the <see cref="Vector2"/> class. + /// </summary> + public Vector2() + { + } + + /// <summary> + /// Initializes a new instance of the <see cref="Vector2"/> class. + /// </summary> + /// <param name="start">The starting point.</param> + /// <param name="end">The ending point.</param> + public Vector2(Point start, Point end) + { + this.Start = start; + this.End = end; + } + + /// <summary> + /// Initializes a new instance of the <see cref="Vector2"/> class. + /// </summary> + /// <param name="start">The starting point.</param> + /// <param name="lengthX">The length by X-coordinate.</param> + /// <param name="lengthY">The length by Y-coordinate.</param> + public Vector2(Point start, double lengthX, double lengthY) + { + this.Start = start; + this.End = start.Add(lengthX, lengthY); + } + + /// <summary> + /// Rounds starting and ending points to the nearest integer coordinates. + /// </summary> + /// <returns>Vector with rounded coordinates.</returns> + public Vector2 Round() + { + this.Start = this.Start.Round(); + this.End = this.End.Round(); + return this; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/Helpers/BitmapHelper.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,57 @@ +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using SilverFlow.Controls.Extensions; + +namespace SilverFlow.Controls.Helpers +{ + /// <summary> + /// Bitmap helper. + /// </summary> + public class BitmapHelper : IBitmapHelper + { + /// <summary> + /// Renders the visual element and returns a bitmap, containing bitmap image of the element. + /// </summary> + /// <param name="element">The visual element.</param> + /// <param name="imageWidth">Image width.</param> + /// <param name="imageHeight">Image height.</param> + /// <returns>Bitmap image of the element.</returns> + public ImageSource RenderVisual(FrameworkElement element, double imageWidth, double imageHeight) + { + int width = element.Width.IsNotSet() ? (int)element.ActualWidth : (int)element.Width; + int height = element.Height.IsNotSet() ? (int)element.ActualHeight : (int)element.Height; + + ScaleTransform transform = null; + + // If the element is an image - do not scale it + if (!(element is Image)) + { + // Scale down the element to fit it into the window's thumbnail + double scaleX = imageWidth / width; + double scaleY = imageHeight / height; + double minScale = Math.Min(scaleX, scaleY); + + if (minScale < 1) + { + transform = new ScaleTransform + { + ScaleX = minScale, + ScaleY = minScale + }; + + width = (int)(width * minScale); + height = (int)(height * minScale); + } + } + + WriteableBitmap bitmap = new WriteableBitmap(width, height); + bitmap.Render(element, transform); + bitmap.Invalidate(); + + return bitmap; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/Helpers/IBitmapHelper.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,20 @@ +using System.Windows; +using System.Windows.Media; + +namespace SilverFlow.Controls.Helpers +{ + /// <summary> + /// This interface defines methods used the create bitmap images. + /// </summary> + public interface IBitmapHelper + { + /// <summary> + /// Renders the visual element and returns a bitmap, containing bitmap image of the element. + /// </summary> + /// <param name="element">The visual element.</param> + /// <param name="imageWidth">Image width.</param> + /// <param name="imageHeight">Image height.</param> + /// <returns>Bitmap image of the element.</returns> + ImageSource RenderVisual(FrameworkElement element, double imageWidth, double imageHeight); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/Helpers/ILocalStorage.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,21 @@ +namespace SilverFlow.Controls.Helpers +{ + /// <summary> + /// This interface defines isolated storage methods and properties. + /// </summary> + public interface ILocalStorage + { + /// <summary> + /// Gets or saves the value associated with the specified key. + /// </summary> + /// <value>The value associated with the specified key.</value> + object this[string key] { get; set; } + + /// <summary> + /// Determines if the local storage contains the specified key. + /// </summary> + /// <param name="key">The key for the entry to be located.</param> + /// <returns>True if the local storage contains the specified key; otherwise, false.</returns> + bool Contains(string key); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/Helpers/IVisualHelper.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using System.Windows; + +namespace SilverFlow.Controls.Helpers +{ + /// <summary> + /// This interface defines methods used to traverse object relationships + /// in the visual tree. + /// </summary> + public interface IVisualHelper + { + /// <summary> + /// Retrieves a set of objects that are located within a specified point of an object's coordinate space. + /// </summary> + /// <param name="intersectingPoint">The point to use as the determination point.</param> + /// <param name="subtree">The object to search within.</param> + /// <returns> + /// An enumerable set of System.Windows.UIElement objects that are determined + /// to be located in the visual tree composition at the specified point and within + /// the specified subtee. + /// </returns> + IEnumerable<UIElement> FindElementsInCoordinates(Point intersectingPoint, UIElement subtree); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/Helpers/LocalStorage.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,38 @@ +using System.IO.IsolatedStorage; + +namespace SilverFlow.Controls.Helpers +{ + /// <summary> + /// Local isolated storage. + /// </summary> + public class LocalStorage : ILocalStorage + { + /// <summary> + /// Gets or saves the value associated with the specified key. + /// </summary> + /// <value>The value associated with the specified key.</value> + public object this[string key] + { + get + { + return IsolatedStorageSettings.ApplicationSettings[key]; + } + set + { + IsolatedStorageSettings.ApplicationSettings[key] = value; + } + } + + /// <summary> + /// Determines if the local storage contains the specified key. + /// </summary> + /// <param name="key">The key for the entry to be located.</param> + /// <returns> + /// True if the local storage contains the specified key; otherwise, false. + /// </returns> + public bool Contains(string key) + { + return IsolatedStorageSettings.ApplicationSettings.Contains(key); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/Helpers/VisualHelper.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using System.Windows; +using System.Windows.Media; + +namespace SilverFlow.Controls.Helpers +{ + public class VisualHelper : IVisualHelper + { + /// <summary> + /// Retrieves a set of objects that are located within a specified point of an object's coordinate space. + /// </summary> + /// <param name="intersectingPoint">The point to use as the determination point.</param> + /// <param name="subtree">The object to search within.</param> + /// <returns> + /// An enumerable set of System.Windows.UIElement objects that are determined + /// to be located in the visual tree composition at the specified point and within + /// the specified subtee. + /// </returns> + public IEnumerable<UIElement> FindElementsInCoordinates(Point intersectingPoint, UIElement subtree) + { + return VisualTreeHelper.FindElementsInHostCoordinates(intersectingPoint, subtree); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/Properties/AssemblyInfo.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,36 @@ +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SilverFlow.Controls")] +[assembly: AssemblyDescription("FloatingWindow Control Library for Silverlight")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Jevgenij Pankov")] +[assembly: AssemblyProduct("SilverFlow.Controls")] +[assembly: AssemblyCopyright("Copyright © 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] +[assembly: CLSCompliant(true)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("00b61972-c1f0-47b6-8507-872060367303")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("2.0.2")] +[assembly: AssemblyFileVersion("2.0.2")]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/SilverFlow.Controls.csproj Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,117 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProductVersion>8.0.50727</ProductVersion> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{3F3D3C8C-9724-4531-A04D-B92049F64686}</ProjectGuid> + <ProjectTypeGuids>{A1591282-1198-4647-A2B1-27E5FF5F6F3B};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>SilverFlow.Controls</RootNamespace> + <AssemblyName>SilverFlow.Controls</AssemblyName> + <TargetFrameworkIdentifier>Silverlight</TargetFrameworkIdentifier> + <TargetFrameworkVersion>v4.0</TargetFrameworkVersion> + <SilverlightVersion>$(TargetFrameworkVersion)</SilverlightVersion> + <SilverlightApplication>false</SilverlightApplication> + <ValidateXaml>true</ValidateXaml> + <ThrowErrorsInValidation>true</ThrowErrorsInValidation> + <Utf8Output>true</Utf8Output> + <ExpressionBlendVersion>4.0.20525.0</ExpressionBlendVersion> + </PropertyGroup> + <!-- This property group is only here to support building this project using the + MSBuild 3.5 toolset. In order to work correctly with this older toolset, it needs + to set the TargetFrameworkVersion to v3.5 --> + <PropertyGroup Condition="'$(MSBuildToolsVersion)' == '3.5'"> + <TargetFrameworkVersion>v3.5</TargetFrameworkVersion> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>Bin\Debug</OutputPath> + <DefineConstants>DEBUG;TRACE;SILVERLIGHT</DefineConstants> + <NoStdLib>true</NoStdLib> + <NoConfig>true</NoConfig> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <RunCodeAnalysis>false</RunCodeAnalysis> + <CodeAnalysisRuleSet>BasicCorrectnessRules.ruleset</CodeAnalysisRuleSet> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>Bin\Release</OutputPath> + <DefineConstants>TRACE;SILVERLIGHT</DefineConstants> + <NoStdLib>true</NoStdLib> + <NoConfig>true</NoConfig> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <ItemGroup> + <Reference Include="mscorlib" /> + <Reference Include="System.Windows" /> + <Reference Include="system" /> + <Reference Include="System.Core" /> + <Reference Include="System.Windows.Controls, Version=2.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" /> + <Reference Include="System.Xml" /> + <Reference Include="System.Net" /> + <Reference Include="System.Windows.Browser" /> + </ItemGroup> + <ItemGroup> + <Compile Include="Converters\BooleanToVisibilityConverter.cs" /> + <Compile Include="Extensions\AnimationExtensions.cs" /> + <Compile Include="Extensions\ControlExtensions.cs" /> + <Compile Include="Extensions\GeometryExtensions.cs" /> + <Compile Include="Extensions\MathExtensions.cs" /> + <Compile Include="FloatingWindow\ActiveWindowChangedEventArgs.cs" /> + <Compile Include="FloatingWindow\BootstrapButton.cs" /> + <Compile Include="FloatingWindow\FloatingWindow.cs" /> + <Compile Include="FloatingWindow\FloatingWindowHost.cs" /> + <Compile Include="Controllers\InertiaController.cs" /> + <Compile Include="Controllers\IResizableElement.cs" /> + <Compile Include="Controllers\ISnapinController.cs" /> + <Compile Include="Controllers\SnapinController.cs" /> + <Compile Include="Controllers\InertialMotion.cs" /> + <Compile Include="Enums\WindowAction.cs" /> + <Compile Include="Geometry\Distance.cs" /> + <Compile Include="Enums\ResizeAnchor.cs" /> + <Compile Include="Controllers\ResizeController.cs"> + <SubType>Code</SubType> + </Compile> + <Compile Include="Geometry\Trail.cs" /> + <Compile Include="Geometry\Vector2.cs" /> + <Compile Include="Helpers\BitmapHelper.cs" /> + <Compile Include="Helpers\IBitmapHelper.cs" /> + <Compile Include="Helpers\ILocalStorage.cs" /> + <Compile Include="Helpers\IVisualHelper.cs" /> + <Compile Include="Helpers\LocalStorage.cs" /> + <Compile Include="Helpers\VisualHelper.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="FloatingWindow\IconBar.cs" /> + <Compile Include="FloatingWindow\WindowIcon.cs" /> + </ItemGroup> + <ItemGroup> + <Resource Include="Themes\generic.xaml"> + <Generator>MSBuild:Compile</Generator> + <SubType>Designer</SubType> + </Resource> + </ItemGroup> + <ItemGroup /> + <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Silverlight\$(SilverlightVersion)\Microsoft.Silverlight.CSharp.targets" /> + <ProjectExtensions> + <VisualStudio> + <FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}"> + <SilverlightProjectProperties /> + </FlavorProperties> + </VisualStudio> + </ProjectExtensions> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/Themes/generic.xaml Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,787 @@ +<ResourceDictionary + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:controls="clr-namespace:SilverFlow.Controls" + xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:System="clr-namespace:System;assembly=mscorlib"> + + <!-- Window Button Brushes --> + <SolidColorBrush x:Key="WindowButtonForeground">#FF9E9E9E</SolidColorBrush> + <SolidColorBrush x:Key="WindowButtonBorderBrush">#FFd1d9e1</SolidColorBrush> + <Color x:Key="WindowButtonOverColor">White</Color> + <Color x:Key="WindowButtonPressedColor">White</Color> + <Color x:Key="WindowButtonBorderOverColor">#FF899199</Color> + <Color x:Key="WindowButtonBorderPressedColor">#FF0667D4</Color> + + <!-- FloatingWindow Brushes --> + <LinearGradientBrush x:Key="FloatingWindowTitleBackgroundBrush" StartPoint="0.5,0" EndPoint="0.5,1"> + <GradientStop Color="White" Offset="0"/> + <GradientStop Color="#FFe7ebed" Offset="1"/> + </LinearGradientBrush> + + <Style x:Key="CloseButtonStyle" TargetType="Button"> + <Setter Property="Foreground" Value="{StaticResource WindowButtonForeground}"/> + <Setter Property="BorderBrush" Value="{StaticResource WindowButtonBorderBrush}"/> + <Setter Property="BorderThickness" Value="1,0,1,1"/> + <Setter Property="Width" Value="25"/> + <Setter Property="Height" Value="15"/> + <Setter Property="IsTabStop" Value="False"/> + <Setter Property="VerticalAlignment" Value="Top"/> + <Setter Property="VerticalContentAlignment" Value="Top"/> + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="Button"> + <Grid x:Name="Root" Background="Transparent" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" > + <VisualStateManager.VisualStateGroups> + <VisualStateGroup x:Name="CommonStates"> + <VisualState x:Name="Normal" /> + <VisualState x:Name="MouseOver"> + <Storyboard> + <ColorAnimation Duration="0" To="{StaticResource WindowButtonOverColor}" Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="ButtonPath" /> + <ColorAnimation Duration="0" To="{StaticResource WindowButtonOverColor}" Storyboard.TargetProperty="(Path.Stroke).(SolidColorBrush.Color)" Storyboard.TargetName="ButtonPath" /> + <ColorAnimation Duration="0" To="#FFf9acaf" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[0].(GradientStop.Color)" Storyboard.TargetName="ButtonBorder" /> + <ColorAnimation Duration="0" To="#FFf64c4c" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="ButtonBorder" /> + </Storyboard> + </VisualState> + <VisualState x:Name="Pressed"> + <Storyboard> + <ColorAnimation Duration="0" To="{StaticResource WindowButtonPressedColor}" Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="ButtonPath" /> + <ColorAnimation Duration="0" To="{StaticResource WindowButtonPressedColor}" Storyboard.TargetProperty="(Path.Stroke).(SolidColorBrush.Color)" Storyboard.TargetName="ButtonPath" /> + <ColorAnimation Duration="0" To="#FFfa8980" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[0].(GradientStop.Color)" Storyboard.TargetName="ButtonBorder" /> + <ColorAnimation Duration="0" To="#FF9a0000" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="ButtonBorder" /> + </Storyboard> + </VisualState> + <VisualState x:Name="Disabled"> + <Storyboard> + <DoubleAnimation Duration="0" Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity" To="0.5"/> + </Storyboard> + </VisualState> + </VisualStateGroup> + </VisualStateManager.VisualStateGroups> + <Border x:Name="ButtonBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" UseLayoutRounding="True"> + <Border.Background> + <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> + <GradientStop Offset="0"/> + <GradientStop Offset="1"/> + </LinearGradientBrush> + </Border.Background> + <Canvas Width="8" Height="8"> + <Path x:Name="ButtonPath" Data="M0,0 L0.9371469,0 L4,3.0628543 L7.0628557,0 L8,0 L8,1.073151 L5.0731506,4 L8,6.9268494 L8,8 L6.9278555,8 L4,5.0721459 L1.0721471,8 L0,8 L0,7.0948515 L3.0948515,4 L0,0.90514863 z" + Fill="{TemplateBinding Foreground}" Stretch="None" StrokeThickness="0.5" Stroke="{TemplateBinding Foreground}" + UseLayoutRounding="True" Width="8" Height="8" VerticalAlignment="Center" HorizontalAlignment="Center" /> + </Canvas> + </Border> + </Grid> + </ControlTemplate> + </Setter.Value> + </Setter> + </Style> + + <Style x:Key="MinimizeButtonStyle" TargetType="Button"> + <Setter Property="Foreground" Value="{StaticResource WindowButtonForeground}"/> + <Setter Property="BorderBrush" Value="{StaticResource WindowButtonBorderBrush}"/> + <Setter Property="BorderThickness" Value="1,0,1,1"/> + <Setter Property="Margin" Value="0,0,-1,0"/> + <Setter Property="Width" Value="21"/> + <Setter Property="Height" Value="15"/> + <Setter Property="IsTabStop" Value="False"/> + <Setter Property="VerticalAlignment" Value="Top"/> + <Setter Property="VerticalContentAlignment" Value="Top"/> + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="Button"> + <Grid x:Name="Root" Background="Transparent" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" > + <VisualStateManager.VisualStateGroups> + <VisualStateGroup x:Name="CommonStates"> + <VisualState x:Name="Normal" /> + <VisualState x:Name="MouseOver"> + <Storyboard> + <ColorAnimation Duration="0" To="{StaticResource WindowButtonOverColor}" Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="ButtonContent" /> + <ColorAnimation Duration="0" To="#FFbfeefd" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[0].(GradientStop.Color)" Storyboard.TargetName="ButtonBorder" /> + <ColorAnimation Duration="0" To="#FF10a4e1" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="ButtonBorder" /> + </Storyboard> + </VisualState> + <VisualState x:Name="Pressed"> + <Storyboard> + <ColorAnimation Duration="0" To="{StaticResource WindowButtonPressedColor}" Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="ButtonContent" /> + <ColorAnimation Duration="0" To="#FF85EBFF" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[0].(GradientStop.Color)" Storyboard.TargetName="ButtonBorder" /> + <ColorAnimation Duration="0" To="#FF003D80" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="ButtonBorder" /> + </Storyboard> + </VisualState> + <VisualState x:Name="Disabled"> + <Storyboard> + <DoubleAnimation Duration="0" Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity" To="0.5"/> + </Storyboard> + </VisualState> + </VisualStateGroup> + </VisualStateManager.VisualStateGroups> + <Border x:Name="ButtonBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" UseLayoutRounding="True"> + <Border.Background> + <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> + <GradientStop Offset="0"/> + <GradientStop Offset="1"/> + </LinearGradientBrush> + </Border.Background> + <Canvas Width="9" Height="8" VerticalAlignment="Center" HorizontalAlignment="Center"> + <Rectangle x:Name="ButtonContent" StrokeThickness="0" Width="9" Height="2" Fill="{TemplateBinding Foreground}" Canvas.Top="6" /> + </Canvas> + </Border> + </Grid> + </ControlTemplate> + </Setter.Value> + </Setter> + </Style> + + <Style x:Key="MaximizeButtonStyle" TargetType="Button"> + <Setter Property="Foreground" Value="{StaticResource WindowButtonForeground}"/> + <Setter Property="BorderBrush" Value="{StaticResource WindowButtonBorderBrush}"/> + <Setter Property="BorderThickness" Value="1,0,1,1"/> + <Setter Property="Margin" Value="0,0,-1,0"/> + <Setter Property="Width" Value="21"/> + <Setter Property="Height" Value="15"/> + <Setter Property="IsTabStop" Value="False"/> + <Setter Property="VerticalAlignment" Value="Top"/> + <Setter Property="VerticalContentAlignment" Value="Top"/> + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="Button"> + <Grid x:Name="Root" Background="Transparent" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" > + <VisualStateManager.VisualStateGroups> + <VisualStateGroup x:Name="CommonStates"> + <VisualState x:Name="Normal" /> + <VisualState x:Name="MouseOver"> + <Storyboard> + <ColorAnimation Duration="0" To="{StaticResource WindowButtonOverColor}" Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="ButtonContent1" /> + <ColorAnimation Duration="0" To="{StaticResource WindowButtonOverColor}" Storyboard.TargetProperty="(Path.Stroke).(SolidColorBrush.Color)" Storyboard.TargetName="ButtonContent2" /> + <ColorAnimation Duration="0" To="#FFbfeefd" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[0].(GradientStop.Color)" Storyboard.TargetName="ButtonBorder" /> + <ColorAnimation Duration="0" To="#FF10a4e1" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="ButtonBorder" /> + </Storyboard> + </VisualState> + <VisualState x:Name="Pressed"> + <Storyboard> + <ColorAnimation Duration="0" To="{StaticResource WindowButtonPressedColor}" Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="ButtonContent1" /> + <ColorAnimation Duration="0" To="{StaticResource WindowButtonPressedColor}" Storyboard.TargetProperty="(Path.Stroke).(SolidColorBrush.Color)" Storyboard.TargetName="ButtonContent2" /> + <ColorAnimation Duration="0" To="#FF85EBFF" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[0].(GradientStop.Color)" Storyboard.TargetName="ButtonBorder" /> + <ColorAnimation Duration="0" To="#FF003D80" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="ButtonBorder" /> + </Storyboard> + </VisualState> + <VisualState x:Name="Disabled"> + <Storyboard> + <DoubleAnimation Duration="0" Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity" To="0.5"/> + </Storyboard> + </VisualState> + </VisualStateGroup> + </VisualStateManager.VisualStateGroups> + <Border x:Name="ButtonBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" UseLayoutRounding="True"> + <Border.Background> + <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> + <GradientStop Offset="0"/> + <GradientStop Offset="1"/> + </LinearGradientBrush> + </Border.Background> + <Canvas Width="9" Height="8"> + <Rectangle x:Name="ButtonContent1" StrokeThickness="0" Width="9" Height="2" Fill="{TemplateBinding Foreground}" /> + <Rectangle x:Name="ButtonContent2" StrokeThickness="1" Width="9" Height="8" Stroke="{TemplateBinding Foreground}" /> + </Canvas> + </Border> + </Grid> + </ControlTemplate> + </Setter.Value> + </Setter> + </Style> + + <Style x:Key="RestoreButtonStyle" TargetType="Button"> + <Setter Property="Foreground" Value="{StaticResource WindowButtonForeground}"/> + <Setter Property="BorderBrush" Value="{StaticResource WindowButtonBorderBrush}"/> + <Setter Property="BorderThickness" Value="1,0,1,1"/> + <Setter Property="Margin" Value="0,0,-1,0"/> + <Setter Property="Width" Value="21"/> + <Setter Property="Height" Value="15"/> + <Setter Property="IsTabStop" Value="False"/> + <Setter Property="VerticalAlignment" Value="Top"/> + <Setter Property="VerticalContentAlignment" Value="Top"/> + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="Button"> + <Grid x:Name="Root" Background="Transparent" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" > + <VisualStateManager.VisualStateGroups> + <VisualStateGroup x:Name="CommonStates"> + <VisualState x:Name="Normal" /> + <VisualState x:Name="MouseOver"> + <Storyboard> + <ColorAnimation Duration="0" To="{StaticResource WindowButtonOverColor}" Storyboard.TargetProperty="(Rectangle.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="Content1" /> + <ColorAnimation Duration="0" To="{StaticResource WindowButtonOverColor}" Storyboard.TargetProperty="(Rectangle.Stroke).(SolidColorBrush.Color)" Storyboard.TargetName="Content2" /> + <ColorAnimation Duration="0" To="{StaticResource WindowButtonOverColor}" Storyboard.TargetProperty="(Rectangle.Stroke).(SolidColorBrush.Color)" Storyboard.TargetName="Content3" /> + <ColorAnimation Duration="0" To="{StaticResource WindowButtonOverColor}" Storyboard.TargetProperty="(Rectangle.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="Content4" /> + <ColorAnimation Duration="0" To="#FFbfeefd" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[0].(GradientStop.Color)" Storyboard.TargetName="ButtonBorder" /> + <ColorAnimation Duration="0" To="#FF10a4e1" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="ButtonBorder" /> + </Storyboard> + </VisualState> + <VisualState x:Name="Pressed"> + <Storyboard> + <ColorAnimation Duration="0" To="{StaticResource WindowButtonPressedColor}" Storyboard.TargetProperty="(Rectangle.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="Content1" /> + <ColorAnimation Duration="0" To="{StaticResource WindowButtonPressedColor}" Storyboard.TargetProperty="(Rectangle.Stroke).(SolidColorBrush.Color)" Storyboard.TargetName="Content2" /> + <ColorAnimation Duration="0" To="{StaticResource WindowButtonPressedColor}" Storyboard.TargetProperty="(Rectangle.Stroke).(SolidColorBrush.Color)" Storyboard.TargetName="Content3" /> + <ColorAnimation Duration="0" To="{StaticResource WindowButtonPressedColor}" Storyboard.TargetProperty="(Rectangle.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="Content4" /> + <ColorAnimation Duration="0" To="#FF85EBFF" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[0].(GradientStop.Color)" Storyboard.TargetName="ButtonBorder" /> + <ColorAnimation Duration="0" To="#FF003D80" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="ButtonBorder" /> + </Storyboard> + </VisualState> + <VisualState x:Name="Disabled"> + <Storyboard> + <DoubleAnimation Duration="0" Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity" To="0.5"/> + </Storyboard> + </VisualState> + </VisualStateGroup> + </VisualStateManager.VisualStateGroups> + <Border x:Name="ButtonBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" UseLayoutRounding="True"> + <Border.Background> + <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> + <GradientStop Offset="0"/> + <GradientStop Offset="1"/> + </LinearGradientBrush> + </Border.Background> + <Canvas Width="9" Height="8"> + <Rectangle x:Name="Content1" StrokeThickness="0" Width="7" Height="1" Fill="{TemplateBinding Foreground}" Canvas.Top="3" /> + <Rectangle x:Name="Content2" StrokeThickness="1" Width="7" Height="5" Stroke="{TemplateBinding Foreground}" Canvas.Top="3" /> + <Path x:Name="Content3" Data="M0.35,0.345 L6.65,0.35 L6.65,5.65 L4.65,5.65 L4.65,3.65625 L0.35,3.65625 z" Width="7" Height="6" + Stretch="Fill" Stroke="{TemplateBinding Foreground}" StrokeThickness="1" Canvas.Left="2" /> + <Rectangle x:Name="Content4" StrokeThickness="0" Width="7" Height="2" Fill="{TemplateBinding Foreground}" Canvas.Left="2" /> + </Canvas> + </Border> + </Grid> + </ControlTemplate> + </Setter.Value> + </Setter> + </Style> + + <Style x:Key="WindowButtonToolTipStyle" TargetType="ToolTip"> + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="ToolTip"> + <Border Background="#FFf7f8fa" BorderThickness="1" CornerRadius="2" Padding="7,1"> + <Border.BorderBrush> + <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1"> + <GradientStop Color="#F09EBFCE" Offset="0" /> + <GradientStop Color="#F0698795" Offset="1" /> + </LinearGradientBrush> + </Border.BorderBrush> + <Border.Effect> + <DropShadowEffect ShadowDepth="0.5" Opacity="0.3" BlurRadius="3" /> + </Border.Effect> + <ContentPresenter Content="{TemplateBinding Content}" + ContentTemplate="{TemplateBinding ContentTemplate}" /> + </Border> + </ControlTemplate> + </Setter.Value> + </Setter> + </Style> + + <Style x:Key="FloatingWindowTitleStyle" TargetType="ContentControl"> + <Setter Property="IsTabStop" Value="False"/> + <Setter Property="HorizontalAlignment" Value="Stretch"/> + <Setter Property="VerticalAlignment" Value="Center"/> + <Setter Property="Margin" Value="6,3,6,3"/> + <Setter Property="UseLayoutRounding" Value="True"/> + <Setter Property="FontWeight" Value="Bold"/> + <Setter Property="Foreground" Value="#FF67676C"/> + <Setter Property="FontSize" Value="10"/> + </Style> + + <Style x:Key="BottomBarStyle" TargetType="Border"> + <Setter Property="Height" Value="24" /> + <Setter Property="Background" Value="#FFE7EBED" /> + <Setter Property="BorderThickness" Value="0,1,0,0" /> + <Setter Property="BorderBrush" Value="#FFFEFEFE" /> + </Style> + + <Style x:Key="IconBarStyle" TargetType="Border"> + <Setter Property="VerticalAlignment" Value="Top" /> + <Setter Property="Height" Value="104" /> + <Setter Property="Padding" Value="2,2" /> + <Setter Property="Background" Value="#FF202F50" /> + </Style> + + <Style x:Key="IconBorderStyle" TargetType="Border"> + <Setter Property="Width" Value="140" /> + <Setter Property="Height" Value="100" /> + <Setter Property="Margin" Value="0,0,1,0" /> + <Setter Property="Padding" Value="2" /> + <Setter Property="VerticalAlignment" Value="Bottom" /> + <Setter Property="CornerRadius" Value="2" /> + <Setter Property="BorderThickness" Value="0.7" /> + <Setter Property="BorderBrush" Value="Transparent" /> + <Setter Property="Background"> + <Setter.Value> + <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> + <GradientStop Color="Transparent" Offset="0"/> + <GradientStop Color="Transparent" Offset="1"/> + </LinearGradientBrush> + </Setter.Value> + </Setter> + </Style> + + <Style TargetType="controls:FloatingWindow"> + <Setter Property="Width" Value="300"/> + <Setter Property="Height" Value="200"/> + <Setter Property="IsTabStop" Value="False"/> + <Setter Property="TabNavigation" Value="Cycle"/> + <Setter Property="HorizontalAlignment" Value="Center"/> + <Setter Property="VerticalAlignment" Value="Center"/> + <Setter Property="HorizontalContentAlignment" Value="Stretch"/> + <Setter Property="VerticalContentAlignment" Value="Stretch"/> + <Setter Property="BorderThickness" Value="1"/> + <Setter Property="BorderBrush" Value="#FFA2ADB8"/> + <Setter Property="Background" Value="#FFE7EBED" /> + <Setter Property="TitleBackground" Value="{StaticResource FloatingWindowTitleBackgroundBrush}" /> + + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="controls:FloatingWindow"> + <Grid x:Name="Root" RenderTransformOrigin="0.5,0.5"> + <Grid.RenderTransform> + <CompositeTransform/> + </Grid.RenderTransform> + <VisualStateManager.VisualStateGroups> + <VisualStateGroup x:Name="WindowStates"> + <VisualState x:Name="Open"> + <Storyboard> + <DoubleAnimationUsingKeyFrames BeginTime="0" Storyboard.TargetName="ContentRoot" Storyboard.TargetProperty="Opacity"> + <SplineDoubleKeyFrame KeyTime="0" Value="0"/> + <SplineDoubleKeyFrame KeyTime="00:00:00.3" Value="1"/> + </DoubleAnimationUsingKeyFrames> + <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="ContentRoot" Storyboard.TargetProperty="Visibility"> + <DiscreteObjectKeyFrame KeyTime="0"> + <DiscreteObjectKeyFrame.Value> + <Visibility>Visible</Visibility> + </DiscreteObjectKeyFrame.Value> + </DiscreteObjectKeyFrame> + </ObjectAnimationUsingKeyFrames> + </Storyboard> + </VisualState> + <VisualState x:Name="Closed"> + <Storyboard> + <DoubleAnimationUsingKeyFrames BeginTime="0" Storyboard.TargetName="ContentRoot" Storyboard.TargetProperty="Opacity"> + <SplineDoubleKeyFrame KeyTime="0" Value="1"/> + <SplineDoubleKeyFrame KeyTime="00:00:00.2" Value="0"/> + </DoubleAnimationUsingKeyFrames> + </Storyboard> + </VisualState> + <VisualState x:Name="Minimized"> + <Storyboard> + <PointAnimation Duration="0" To="0,1" Storyboard.TargetProperty="(UIElement.RenderTransformOrigin)" Storyboard.TargetName="ContentRoot" /> + <DoubleAnimationUsingKeyFrames BeginTime="0" Storyboard.TargetName="ContentRoot" Storyboard.TargetProperty="(UIElement.RenderTransform).(Children)[0].ScaleX"> + <SplineDoubleKeyFrame KeyTime="0" Value="1"/> + <SplineDoubleKeyFrame KeyTime="00:00:0.2" Value="0"/> + </DoubleAnimationUsingKeyFrames> + <DoubleAnimationUsingKeyFrames BeginTime="0" Storyboard.TargetName="ContentRoot" Storyboard.TargetProperty="(UIElement.RenderTransform).(Children)[0].ScaleY"> + <SplineDoubleKeyFrame KeyTime="00:00:00" Value="1"/> + <SplineDoubleKeyFrame KeyTime="00:00:0.2" Value="0"/> + </DoubleAnimationUsingKeyFrames> + <DoubleAnimationUsingKeyFrames Storyboard.TargetName="ContentRoot" Storyboard.TargetProperty="Opacity"> + <EasingDoubleKeyFrame KeyTime="0" Value="1"/> + <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="0"/> + </DoubleAnimationUsingKeyFrames> + <ObjectAnimationUsingKeyFrames BeginTime="0" Duration="00:00:0.2" Storyboard.TargetName="ContentRoot" Storyboard.TargetProperty="Visibility"> + <DiscreteObjectKeyFrame KeyTime="00:00:0.2"> + <DiscreteObjectKeyFrame.Value> + <Visibility>Collapsed</Visibility> + </DiscreteObjectKeyFrame.Value> + </DiscreteObjectKeyFrame> + </ObjectAnimationUsingKeyFrames> + </Storyboard> + </VisualState> + <VisualState x:Name="Restored"> + <Storyboard> + <PointAnimation Duration="0" To="0,1" Storyboard.TargetProperty="(UIElement.RenderTransformOrigin)" Storyboard.TargetName="ContentRoot" /> + <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="ContentRoot"> + <EasingDoubleKeyFrame KeyTime="0" Value="0"/> + <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="1"/> + </DoubleAnimationUsingKeyFrames> + <ObjectAnimationUsingKeyFrames BeginTime="0" Duration="0" Storyboard.TargetName="ContentRoot" Storyboard.TargetProperty="Visibility"> + <DiscreteObjectKeyFrame KeyTime="00:00:00"> + <DiscreteObjectKeyFrame.Value> + <Visibility>Visible</Visibility> + </DiscreteObjectKeyFrame.Value> + </DiscreteObjectKeyFrame> + </ObjectAnimationUsingKeyFrames> + <DoubleAnimationUsingKeyFrames BeginTime="0" Storyboard.TargetName="ContentRoot" Storyboard.TargetProperty="(UIElement.RenderTransform).(Children)[0].ScaleX"> + <SplineDoubleKeyFrame KeyTime="0" Value="0"/> + <SplineDoubleKeyFrame KeyTime="00:00:0.3" Value="1" KeySpline="0,0.86,0.19,1"/> + </DoubleAnimationUsingKeyFrames> + <DoubleAnimationUsingKeyFrames BeginTime="0" Storyboard.TargetName="ContentRoot" Storyboard.TargetProperty="(UIElement.RenderTransform).(Children)[0].ScaleY"> + <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/> + <SplineDoubleKeyFrame KeyTime="00:00:0.3" Value="1" KeySpline="0,0.86,0.19,1"/> + </DoubleAnimationUsingKeyFrames> + </Storyboard> + </VisualState> + </VisualStateGroup> + </VisualStateManager.VisualStateGroups> + <Grid x:Name="ContentRoot" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}" + RenderTransformOrigin="0,0" Height="{TemplateBinding Height}" Width="{TemplateBinding Width}"> + <Grid.Effect> + <DropShadowEffect ShadowDepth="0.5" Opacity="0.35" BlurRadius="12" /> + </Grid.Effect> + <Grid.RenderTransform> + <TransformGroup> + <ScaleTransform /> + <SkewTransform /> + <RotateTransform /> + <TranslateTransform /> + </TransformGroup> + </Grid.RenderTransform> + + <Border x:Name="ContentBorder" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" + BorderBrush="{TemplateBinding BorderBrush}" CornerRadius="3"> + <Grid> + <Grid.RowDefinitions> + <RowDefinition Height="Auto"/> + <RowDefinition/> + </Grid.RowDefinitions> + + <Border x:Name="Chrome" Width="Auto" Background="{TemplateBinding TitleBackground}" UseLayoutRounding="False" CornerRadius="2,2,0,0"> + <Grid Height="Auto" Width="Auto"> + <Grid.ColumnDefinitions> + <ColumnDefinition/> + <ColumnDefinition Width="Auto"/> + <ColumnDefinition Width="Auto"/> + <ColumnDefinition Width="6"/> + </Grid.ColumnDefinitions> + + <ContentControl x:Name="TitleContent" Content="{TemplateBinding Title}" Style="{StaticResource FloatingWindowTitleStyle}" /> + + <StackPanel x:Name="MenuPanel" Orientation="Horizontal" Grid.Column="1" VerticalAlignment="Top"> + <Button x:Name="MinimizeButton" Style="{StaticResource MinimizeButtonStyle}"> + <ToolTipService.ToolTip> + <ToolTip Style="{StaticResource WindowButtonToolTipStyle}" Content="Minimize" /> + </ToolTipService.ToolTip> + </Button> + <Button x:Name="MaximizeButton" Style="{StaticResource MaximizeButtonStyle}"> + <ToolTipService.ToolTip> + <ToolTip Style="{StaticResource WindowButtonToolTipStyle}" Content="Maximize" /> + </ToolTipService.ToolTip> + </Button> + <Button x:Name="RestoreButton" Style="{StaticResource RestoreButtonStyle}" Visibility="Collapsed"> + <ToolTipService.ToolTip> + <ToolTip Style="{StaticResource WindowButtonToolTipStyle}" Content="Restore" /> + </ToolTipService.ToolTip> + </Button> + </StackPanel> + <Button x:Name="CloseButton" Style="{StaticResource CloseButtonStyle}" Grid.Column="2"> + <ToolTipService.ToolTip> + <ToolTip Style="{StaticResource WindowButtonToolTipStyle}" Content="Close" /> + </ToolTipService.ToolTip> + </Button> + </Grid> + </Border> + <Border Grid.Row="1"> + <ContentPresenter x:Name="ContentPresenter" Content="{TemplateBinding Content}" + ContentTemplate="{TemplateBinding ContentTemplate}" + HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" + VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> + </Border> + </Grid> + </Border> + </Grid> + </Grid> + </ControlTemplate> + </Setter.Value> + </Setter> + </Style> + + <Style x:Key="BootstrapButtonStyle" TargetType="controls:BootstrapButton"> + <Setter Property="Width" Value="24"/> + <Setter Property="Height" Value="24"/> + <Setter Property="BorderBrush" Value="#FFA2ADB8"/> + <Setter Property="Background" Value="#FF4E4E4E"/> + <Setter Property="VerticalAlignment" Value="Top"/> + <Setter Property="IsTabStop" Value="false"/> + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="controls:BootstrapButton"> + <Grid Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" RenderTransformOrigin="0.5,0.5"> + <Grid.RenderTransform> + <CompositeTransform/> + </Grid.RenderTransform> + <VisualStateManager.VisualStateGroups> + <VisualStateGroup x:Name="CommonStates"> + <VisualStateGroup.Transitions> + <VisualTransition GeneratedDuration="0"/> + </VisualStateGroup.Transitions> + <VisualState x:Name="Disabled"> + <Storyboard> + <ColorAnimation Duration="0" To="LightGray" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="PART_Arrow" /> + </Storyboard> + </VisualState> + <VisualState x:Name="Normal"/> + <VisualState x:Name="MouseOver"> + <Storyboard> + <ColorAnimation Duration="0" To="Black" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="PART_Arrow" /> + </Storyboard> + </VisualState> + <VisualState x:Name="Pressed"> + <Storyboard> + <ColorAnimation Duration="0" To="Black" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="PART_Arrow" /> + <DoubleAnimation Duration="0" By="1" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateY)" Storyboard.TargetName="PART_Arrow"/> + </Storyboard> + </VisualState> + </VisualStateGroup> + <VisualStateGroup x:Name="ButtonStates"> + <VisualState x:Name="Open"> + <Storyboard> + <DoubleAnimation Duration="0:0:0.4" To="0" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.Rotation)" Storyboard.TargetName="PART_Arrow"> + <DoubleAnimation.EasingFunction> + <CubicEase EasingMode="EaseOut"/> + </DoubleAnimation.EasingFunction> + </DoubleAnimation> + </Storyboard> + </VisualState> + <VisualState x:Name="Close"> + <Storyboard> + <DoubleAnimation Duration="0:0:0.4" To="180" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.Rotation)" Storyboard.TargetName="PART_Arrow"> + <DoubleAnimation.EasingFunction> + <CubicEase EasingMode="EaseOut"/> + </DoubleAnimation.EasingFunction> + </DoubleAnimation> + <DoubleAnimation Duration="0:0:0.4" To="0" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateY)" Storyboard.TargetName="PART_Arrow"/> + </Storyboard> + </VisualState> + </VisualStateGroup> + </VisualStateManager.VisualStateGroups> + + <Ellipse> + <Ellipse.Fill> + <LinearGradientBrush EndPoint="0.691,0.972" StartPoint="0.307,0.037"> + <GradientStop Color="#FFA2ADB8"/> + <GradientStop Color="White" Offset="1"/> + </LinearGradientBrush> + </Ellipse.Fill> + </Ellipse> + <Ellipse Margin="1"> + <Ellipse.Fill> + <LinearGradientBrush EndPoint="0.631,1" MappingMode="RelativeToBoundingBox" StartPoint="0.29,0.045"> + <GradientStop Color="#FFB8C3C8" Offset="1"/> + <GradientStop Color="#FFE7EBED" Offset="0.27"/> + </LinearGradientBrush> + </Ellipse.Fill> + </Ellipse> + <Path x:Name="PART_Arrow" Data="M27.671843,6 L31.333334,2 L35.02343,6 z" + HorizontalAlignment="Center" Width="8" Height="4" Stretch="Fill" UseLayoutRounding="False" + VerticalAlignment="Center" Fill="#FF686D76" RenderTransformOrigin="0.5,0.5"> + <Path.RenderTransform> + <CompositeTransform TranslateY="-1"/> + </Path.RenderTransform> + </Path> + </Grid> + </ControlTemplate> + </Setter.Value> + </Setter> + </Style> + + <Style TargetType="controls:FloatingWindowHost"> + <Setter Property="Width" Value="Auto" /> + <Setter Property="Height" Value="Auto" /> + <Setter Property="SnapinEnabled" Value="True" /> + <Setter Property="SnapinDistance" Value="5" /> + <Setter Property="SnapinMargin" Value="0" /> + <Setter Property="ShowMinimizedOnlyInIconbar" Value="False" /> + <Setter Property="HorizontalAlignment" Value="Stretch" /> + <Setter Property="VerticalAlignment" Value="Stretch" /> + <Setter Property="OverlayBrush" Value="#90202030" /> + <Setter Property="Background" Value="#FF7ED2E9" /> + + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="controls:FloatingWindowHost"> + <Grid x:Name="PART_Root" Background="{TemplateBinding Background}" + Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"> + <VisualStateManager.VisualStateGroups> + <VisualStateGroup x:Name="OverlayStates"> + <VisualState x:Name="VisibleOverlay"> + <Storyboard> + <DoubleAnimationUsingKeyFrames BeginTime="0" Storyboard.TargetName="PART_Overlay" Storyboard.TargetProperty="Opacity"> + <SplineDoubleKeyFrame KeyTime="0:0:0.3" Value="1"/> + </DoubleAnimationUsingKeyFrames> + <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="PART_Overlay" Storyboard.TargetProperty="Visibility"> + <DiscreteObjectKeyFrame KeyTime="0"> + <DiscreteObjectKeyFrame.Value> + <Visibility>Visible</Visibility> + </DiscreteObjectKeyFrame.Value> + </DiscreteObjectKeyFrame> + </ObjectAnimationUsingKeyFrames> + </Storyboard> + </VisualState> + <VisualState x:Name="HiddenOverlay"> + <Storyboard> + <DoubleAnimationUsingKeyFrames BeginTime="0" Storyboard.TargetName="PART_Overlay" Storyboard.TargetProperty="Opacity"> + <SplineDoubleKeyFrame KeyTime="0:0:0.3" Value="0"/> + </DoubleAnimationUsingKeyFrames> + <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_Overlay" Storyboard.TargetProperty="Visibility"> + <DiscreteObjectKeyFrame KeyTime="0:0:0.3"> + <DiscreteObjectKeyFrame.Value> + <Visibility>Collapsed</Visibility> + </DiscreteObjectKeyFrame.Value> + </DiscreteObjectKeyFrame> + </ObjectAnimationUsingKeyFrames> + </Storyboard> + </VisualState> + </VisualStateGroup> + </VisualStateManager.VisualStateGroups> + + <Grid x:Name="PART_ContentRoot"> + <Grid.RowDefinitions> + <RowDefinition Height="*" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + + <Canvas x:Name="PART_HostCanvas" /> + + <Grid x:Name="PART_IconBarContainer" VerticalAlignment="Bottom"> + <controls:IconBar x:Name="PART_IconBar"/> + </Grid> + + <Border x:Name="PART_BottomBar" Grid.Row="1" Style="{StaticResource BottomBarStyle}"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + + <controls:BootstrapButton x:Name="PART_BootstrapButton" Style="{StaticResource BootstrapButtonStyle}" + Margin="10,-4,10,0" IsOpen="{Binding IsOpen, ElementName=PART_IconBar}" /> + + <Border Grid.Column="1"> + <ContentControl x:Name="PART_BarContent" Content="{TemplateBinding Bar}" + VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" /> + </Border> + </Grid> + </Border> + </Grid> + + <Grid x:Name="PART_Overlay" Background="{TemplateBinding OverlayBrush}" Opacity="0" Visibility="Collapsed" /> + <Canvas x:Name="PART_ModalCanvas" /> + </Grid> + </ControlTemplate> + </Setter.Value> + </Setter> + </Style> + + <Style TargetType="controls:IconBar"> + <Setter Property="IsTabStop" Value="false"/> + <Setter Property="HorizontalAlignment" Value="Stretch"/> + <Setter Property="VerticalAlignment" Value="Bottom"/> + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="controls:IconBar"> + <Grid x:Name="PART_LayoutRoot"> + <VisualStateManager.VisualStateGroups> + <VisualStateGroup x:Name="VisualStateGroup"> + <VisualState x:Name="Open"> + <Storyboard> + <DoubleAnimation Duration="0:0:0.2" To="0" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateY)" Storyboard.TargetName="PART_FixedBar"> + <DoubleAnimation.EasingFunction> + <CubicEase EasingMode="EaseOut"/> + </DoubleAnimation.EasingFunction> + </DoubleAnimation> + <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="Visibility" Storyboard.TargetName="PART_FixedBar"> + <DiscreteObjectKeyFrame KeyTime="0"> + <DiscreteObjectKeyFrame.Value> + <Visibility>Visible</Visibility> + </DiscreteObjectKeyFrame.Value> + </DiscreteObjectKeyFrame> + </ObjectAnimationUsingKeyFrames> + </Storyboard> + </VisualState> + <VisualState x:Name="Closed"> + <Storyboard> + <DoubleAnimation Duration="0:0:0.2" To="104" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateY)" Storyboard.TargetName="PART_FixedBar"> + <DoubleAnimation.EasingFunction> + <CubicEase EasingMode="EaseOut"/> + </DoubleAnimation.EasingFunction> + </DoubleAnimation> + <ObjectAnimationUsingKeyFrames Duration="0:0:0.2" Storyboard.TargetProperty="Visibility" Storyboard.TargetName="PART_FixedBar"> + <DiscreteObjectKeyFrame KeyTime="0:0:0.2"> + <DiscreteObjectKeyFrame.Value> + <Visibility>Collapsed</Visibility> + </DiscreteObjectKeyFrame.Value> + </DiscreteObjectKeyFrame> + </ObjectAnimationUsingKeyFrames> + </Storyboard> + </VisualState> + </VisualStateGroup> + </VisualStateManager.VisualStateGroups> + + <Border x:Name="PART_FixedBar" RenderTransformOrigin="0.5,1" Style="{StaticResource IconBarStyle}"> + <Border.RenderTransform> + <!-- Hide the IconBar --> + <CompositeTransform TranslateY="104" /> + </Border.RenderTransform> + + <Canvas> + <Border x:Name="PART_SlidingBar" Canvas.Left="0" VerticalAlignment="Bottom"> + <StackPanel x:Name="PART_Carousel" RenderTransformOrigin="0.5,1" Orientation="Horizontal" + HorizontalAlignment="Left" VerticalAlignment="Bottom"/> + </Border> + </Canvas> + </Border> + </Grid> + </ControlTemplate> + </Setter.Value> + </Setter> + </Style> + + <Style x:Key="WindowIconStyle" TargetType="controls:WindowIcon"> + <Setter Property="IsTabStop" Value="false"/> + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="controls:WindowIcon"> + <Border x:Name="PART_Border" Style="{StaticResource IconBorderStyle}"> + <VisualStateManager.VisualStateGroups> + <VisualStateGroup x:Name="CommonStates"> + <VisualStateGroup.Transitions> + <VisualTransition From="MouseOver" GeneratedDuration="0:0:0.2" To="Normal"> + <VisualTransition.GeneratedEasingFunction> + <QuadraticEase EasingMode="EaseOut"/> + </VisualTransition.GeneratedEasingFunction> + </VisualTransition> + </VisualStateGroup.Transitions> + <VisualState x:Name="Disabled"/> + <VisualState x:Name="Normal" /> + <VisualState x:Name="MouseOver"> + <Storyboard> + <ColorAnimation Duration="0" To="White" Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)" Storyboard.TargetName="PART_Title" /> + <ColorAnimation Duration="0" To="#40F0F0FF" Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="PART_Border" /> + <ColorAnimation Duration="0" To="#40F0F0FF" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[0].(GradientStop.Color)" Storyboard.TargetName="PART_Border" /> + <ColorAnimation Duration="0" To="#40F0F0FF" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="PART_Border" /> + </Storyboard> + </VisualState> + <VisualState x:Name="Pressed"> + <Storyboard> + <ColorAnimation Duration="0" To="White" Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)" Storyboard.TargetName="PART_Title" /> + <ColorAnimation Duration="0" To="#A0FFFFFF" Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="PART_Border" /> + <ColorAnimation Duration="0" To="#90D0D0E0" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[0].(GradientStop.Color)" Storyboard.TargetName="PART_Border" /> + <ColorAnimation Duration="0" To="#80B0B0F0" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="PART_Border" /> + </Storyboard> + </VisualState> + </VisualStateGroup> + </VisualStateManager.VisualStateGroups> + <Grid> + <Grid.RowDefinitions> + <RowDefinition Height="*" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <Border Grid.Row="0" Padding="2"> + <Image x:Name="PART_Thumbnail" Stretch="None" /> + </Border> + <Border Grid.Row="1" HorizontalAlignment="Center"> + <TextBlock x:Name="PART_Title" Foreground="LightGray" VerticalAlignment="Center" Margin="5,2" TextTrimming="WordEllipsis" FontSize="10.667" /> + </Border> + </Grid> + </Border> + </ControlTemplate> + </Setter.Value> + </Setter> + </Style> + +</ResourceDictionary>
--- a/SilverlightGlimpse/SilverlightGlimpse.Test/App.xaml.cs Sun Apr 22 09:01:20 2012 +0100 +++ b/SilverlightGlimpse/SilverlightGlimpse.Test/App.xaml.cs Sun Apr 22 13:33:42 2012 +0100 @@ -1,39 +1,28 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; using System.Windows; -using System.Windows.Controls; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Animation; -using System.Windows.Shapes; using SilverlightGlimpse.Services; //using Glimpse; +using System.Reflection; namespace SilverlightGlimpse.Test { public partial class App : Application { - public App() { - this.Startup += this.Application_Startup; - this.Exit += this.Application_Exit; - this.UnhandledException += this.Application_UnhandledException; + Startup += Application_Startup; + Exit += Application_Exit; + UnhandledException += Application_UnhandledException; InitializeComponent(); } private void Application_Startup(object sender, StartupEventArgs e) { - string appName = "SilverlightGlimpse"; - try { - this.RootVisual = new MainPage(); - GlimpseService.CreateInstance.Load(this, appName); + RootVisual = new MainPage(); + GlimpseService.CreateInstance.Load(this, Assembly.GetCallingAssembly().FullName); } catch (Exception ex) { @@ -74,6 +63,7 @@ } catch (Exception) { + //TODO: log here } } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverlightGlimpse.Test/BuggyControl.xaml Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,42 @@ +<UserControl x:Class="SilverlightGlimpse.Test.BuggyControl" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:c="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + d:DesignHeight="300" + d:DesignWidth="400" + mc:Ignorable="d"> + + <Grid x:Name="LayoutRoot" Background="White"> + <ListBox x:Name="listbox1" ScrollViewer.HorizontalScrollBarVisibility="Disabled"> + <ListBox.ItemsPanel> + <ItemsPanelTemplate> + <c:WrapPanel ItemHeight="100" + ItemWidth="100" + Orientation="Horizontal" /> + </ItemsPanelTemplate> + </ListBox.ItemsPanel> + <ListBox.ItemTemplate> + <DataTemplate> + <StackPanel Margin="20" HorizontalAlignment="Center"> + <Viewbox> + <Grid x:Name="backgroundGrid" + Width="48" + Height="48"> + <Rectangle x:Name="Rect" Fill="Orange" /> + <TextBlock x:Name="LabelForIdProperty" + HorizontalAlignment="Center" + VerticalAlignment="Center" + FontFamily="Segoe UI" + FontSize="24" + Foreground="White" + Text="{Binding INCORRECTPATH}" /> + </Grid> + </Viewbox> + </StackPanel> + </DataTemplate> + </ListBox.ItemTemplate> + </ListBox> + </Grid> +</UserControl>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverlightGlimpse.Test/BuggyControl.xaml.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,29 @@ +using System.Collections.Generic; + +namespace SilverlightGlimpse.Test +{ + public partial class BuggyControl + { + public BuggyControl() + { + InitializeComponent(); + + var list = new List<object>(5) + { + new Person { Id = 1 , Name = "Steve"}, + new Person { Id = 2 , Name = "Dave"}, + new Person { Id = 3 , Name = "Bob"}, + new Person { Id = 4 , Name = "Rich"}, + new Person { Id = 5 , Name = "Clare"} + }; + + listbox1.ItemsSource = list; + } + } + + public class Person + { + public int Id { get; set; } + public string Name { get; set; } + } +}
--- a/SilverlightGlimpse/SilverlightGlimpse.Test/MainPage.xaml Sun Apr 22 09:01:20 2012 +0100 +++ b/SilverlightGlimpse/SilverlightGlimpse.Test/MainPage.xaml Sun Apr 22 13:33:42 2012 +0100 @@ -2,6 +2,7 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:local="clr-namespace:SilverlightGlimpse.Test" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" d:DesignHeight="300" d:DesignWidth="400" @@ -9,22 +10,10 @@ <Grid x:Name="LayoutRoot" Background="White"> <Grid.RowDefinitions> - <RowDefinition Height="100" /> + <RowDefinition Height="800" /> </Grid.RowDefinitions> - <Button Width="150" - Height="60" - Click="Button_Click" - Content="Open form" /> - <ListBox x:Name="Listbox1" Grid.Row="1"> - <ListBox.ItemTemplate> - <DataTemplate> - <StackPanel> - <TextBlock Text="{Binding Id }" /> - <TextBlock Text="{Binding FAIL}" /> - </StackPanel> - </DataTemplate> - </ListBox.ItemTemplate> - </ListBox> + <local:BuggyControl /> + </Grid> </UserControl>
--- a/SilverlightGlimpse/SilverlightGlimpse.Test/MainPage.xaml.cs Sun Apr 22 09:01:20 2012 +0100 +++ b/SilverlightGlimpse/SilverlightGlimpse.Test/MainPage.xaml.cs Sun Apr 22 13:33:42 2012 +0100 @@ -1,37 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Animation; -using System.Windows.Shapes; -using System.Dynamic; - -namespace SilverlightGlimpse.Test +namespace SilverlightGlimpse.Test { - public partial class MainPage : UserControl + public partial class MainPage { public MainPage() { InitializeComponent(); - throw new Exception("Test"); - } - private void Button_Click(object sender, RoutedEventArgs e) - { - var list = new List<object>(5) - { - new { Id = 1 , Name = "Steve"}, - new { Id = 2 , Name = "Dave"}, - new { Id = 3 , Name = "Bob"}, - new { Id = 4 , Name = "Rich"}, - new { Id = 5 , Name = "Clare"} - }; - this.DataContext = list; + // uncomment this exception to view exception on startup + //throw new Exception("This exception has been thrown to demonstrate an exception during startup", new Exception("This is an inner exception")); } } }
--- a/SilverlightGlimpse/SilverlightGlimpse.Test/SilverlightGlimpse.Test.csproj Sun Apr 22 09:01:20 2012 +0100 +++ b/SilverlightGlimpse/SilverlightGlimpse.Test/SilverlightGlimpse.Test.csproj Sun Apr 22 13:33:42 2012 +0100 @@ -31,6 +31,8 @@ <ThrowErrorsInValidation>true</ThrowErrorsInValidation> <LinkedServerProject> </LinkedServerProject> + <Utf8Output>true</Utf8Output> + <ExpressionBlendVersion>4.1.20402.0</ExpressionBlendVersion> </PropertyGroup> <!-- This property group is only here to support building this project using the MSBuild 3.5 toolset. In order to work correctly with this older toolset, it needs @@ -64,10 +66,8 @@ <Reference Include="System.Windows" /> <Reference Include="system" /> <Reference Include="System.Core" /> - <Reference Include="System.Net" /> - <Reference Include="System.Windows.Controls, Version=5.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> - <HintPath>..\Libs\System.Windows.Controls.dll</HintPath> - </Reference> + <Reference Include="System.Windows.Controls, Version=5.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" /> + <Reference Include="System.Windows.Controls.Toolkit, Version=5.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" /> <Reference Include="System.Xml" /> <Reference Include="System.Windows.Browser" /> </ItemGroup> @@ -75,6 +75,9 @@ <Compile Include="App.xaml.cs"> <DependentUpon>App.xaml</DependentUpon> </Compile> + <Compile Include="BuggyControl.xaml.cs"> + <DependentUpon>BuggyControl.xaml</DependentUpon> + </Compile> <Compile Include="MainPage.xaml.cs"> <DependentUpon>MainPage.xaml</DependentUpon> </Compile> @@ -85,6 +88,10 @@ <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </ApplicationDefinition> + <Page Include="BuggyControl.xaml"> + <SubType>Designer</SubType> + <Generator>MSBuild:Compile</Generator> + </Page> <Page Include="MainPage.xaml"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> @@ -99,6 +106,9 @@ <Name>SilverlightGlimpse</Name> </ProjectReference> </ItemGroup> + <ItemGroup> + <WCFMetadata Include="Service References\" /> + </ItemGroup> <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Silverlight\$(SilverlightVersion)\Microsoft.Silverlight.CSharp.targets" /> <!-- To modify your build process, add your task inside one of the targets below and uncomment it. Other similar extension points exist, see Microsoft.Common.targets. @@ -114,4 +124,5 @@ </FlavorProperties> </VisualStudio> </ProjectExtensions> + <Import Project="$(MSBuildExtensionsPath)\Microsoft\Expression\Blend\Silverlight\v5.0\Microsoft.Expression.Blend.Silverlight.targets" /> </Project> \ No newline at end of file
--- a/SilverlightGlimpse/SilverlightGlimpse/Controls/BrokenBindingsViewer.xaml Sun Apr 22 09:01:20 2012 +0100 +++ b/SilverlightGlimpse/SilverlightGlimpse/Controls/BrokenBindingsViewer.xaml Sun Apr 22 13:33:42 2012 +0100 @@ -1,6 +1,7 @@ <UserControl x:Class="SilverlightGlimpse.Controls.BrokenBindingsViewer" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + Loaded="BrokenBindings_Loaded"> <Grid x:Name="LayoutRoot" Background="White"> <ScrollViewer> <ItemsControl x:Name="icBrokenBindings" />
--- a/SilverlightGlimpse/SilverlightGlimpse/Controls/BrokenBindingsViewer.xaml.cs Sun Apr 22 09:01:20 2012 +0100 +++ b/SilverlightGlimpse/SilverlightGlimpse/Controls/BrokenBindingsViewer.xaml.cs Sun Apr 22 13:33:42 2012 +0100 @@ -1,9 +1,8 @@ -using System.Reflection; -using System.Windows.Data; +using System.Diagnostics; +using System.Reflection; +using System.Windows; +using System.Windows.Media; using SilverlightGlimpse.Models; -using System.Windows; -using System.Diagnostics; -using System.Windows.Media; using SilverlightGlimpse.Services; namespace SilverlightGlimpse.Controls @@ -17,74 +16,71 @@ private void BrokenBindings_Loaded(object sender, RoutedEventArgs e) { - this.icBrokenBindings.Items.Clear(); + icBrokenBindings.Items.Clear(); LoadBrokenBindings(GlimpseService.CreateInstance.RootVisual); } private void LoadBrokenBindings(UIElement uiElement) { var frameworkElement = uiElement as FrameworkElement; + if (frameworkElement == null) return; - if (frameworkElement != null) + foreach (var fieldInfo in frameworkElement.GetType().GetFields(BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Static)) { - foreach (var fieldInfo in frameworkElement.GetType().GetFields(BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Static)) - { - if (object.ReferenceEquals(fieldInfo.FieldType, typeof(DependencyProperty))) - { - var bindingExpression = frameworkElement.GetBindingExpression((DependencyProperty)fieldInfo.GetValue(null)); + if (!ReferenceEquals(fieldInfo.FieldType, typeof (DependencyProperty))) continue; + + var bindingExpression = frameworkElement.GetBindingExpression((DependencyProperty)fieldInfo.GetValue(null)); - if (bindingExpression != null && bindingExpression.ParentBinding.Source == null && bindingExpression.ParentBinding.RelativeSource == null) - { - var isInherited = false; + if (bindingExpression == null || bindingExpression.ParentBinding.Source != null || + bindingExpression.ParentBinding.RelativeSource != null) continue; - if (frameworkElement.DataContext != null && !string.IsNullOrEmpty(bindingExpression.ParentBinding.Path.Path)) - { - foreach (var propertyInfo in frameworkElement.DataContext.GetType().GetProperties(BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Instance)) - { - if (string.Compare(propertyInfo.Name, bindingExpression.ParentBinding.Path.Path) == 0) - { - isInherited = true; - break; // TODO: might not be correct. Was : Exit For - } - } - } - - if (isInherited) - { - break; // TODO: might not be correct. Was : Exit For - } + var isInherited = false; - //this code handles empty bindings on the Button controls - //I'll have to look into why the Button has an empty or unresolved binding - if (string.IsNullOrEmpty(frameworkElement.Name) - && frameworkElement.GetType().Name == "TextBlock" - && fieldInfo.Name == "TextProperty" - && string.IsNullOrEmpty(bindingExpression.ParentBinding.Path.Path)) - { - break; // TODO: might not be correct. Was : Exit For - } - - BrokenBinding objBrokenBinding = new BrokenBinding( - frameworkElement.Name, - frameworkElement.GetType().Name, - fieldInfo.Name, - bindingExpression.ParentBinding.Path.Path); - this.icBrokenBindings.Items.Add(objBrokenBinding); - Debug.WriteLine("Broken Binding - ", objBrokenBinding.ToString()); + if (frameworkElement.DataContext != null && !string.IsNullOrEmpty(bindingExpression.ParentBinding.Path.Path)) + { + foreach (var propertyInfo in frameworkElement.DataContext.GetType().GetProperties(BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Instance)) + { + if (string.Compare(propertyInfo.Name, bindingExpression.ParentBinding.Path.Path) == 0) + { + isInherited = true; + break; // TODO: might not be correct. Was : Exit For } } } - int children = VisualTreeHelper.GetChildrenCount(frameworkElement); + if (isInherited) + { + break; // TODO: might not be correct. Was : Exit For + } - for (int j = 0; j <= children - 1; j++) + //this code handles empty bindings on the Button controls + //I'll have to look into why the Button has an empty or unresolved binding + if (string.IsNullOrEmpty(frameworkElement.Name) + && frameworkElement.GetType().Name == "TextBlock" + && fieldInfo.Name == "TextProperty" + && string.IsNullOrEmpty(bindingExpression.ParentBinding.Path.Path)) { - FrameworkElement child = VisualTreeHelper.GetChild(frameworkElement, j) as FrameworkElement; + break; // TODO: might not be correct. Was : Exit For + } - if (child != null) - { - LoadBrokenBindings(child); - } + var brokenBinding = new BrokenBinding( + frameworkElement.Name, + frameworkElement.GetType().Name, + fieldInfo.Name, + bindingExpression.ParentBinding.Path.Path); + icBrokenBindings.Items.Add(brokenBinding); + Debug.WriteLine("Broken Binding: {0}", brokenBinding); + } + + int children = VisualTreeHelper.GetChildrenCount(frameworkElement); + + for (int j = 0; j <= children - 1; j++) + { + var child = VisualTreeHelper.GetChild(frameworkElement, j) as FrameworkElement; + + if (child != null) + { + LoadBrokenBindings(child); } } }
--- a/SilverlightGlimpse/SilverlightGlimpse/Controls/ExceptionsViewer.xaml Sun Apr 22 09:01:20 2012 +0100 +++ b/SilverlightGlimpse/SilverlightGlimpse/Controls/ExceptionsViewer.xaml Sun Apr 22 13:33:42 2012 +0100 @@ -1,30 +1,37 @@ <UserControl x:Class="SilverlightGlimpse.Controls.ExceptionsViewer" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> - <Grid x:Name="LayoutRoot" Background="White"> + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + Loaded="ExceptionsViewer_Loaded"> + <Grid x:Name="LayoutRoot"> + <Grid.ColumnDefinitions> - <ColumnDefinition Width="250" /> + <ColumnDefinition Width="150" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> - <RowDefinition Height="Auto" /> </Grid.RowDefinitions> - <TextBlock Grid.ColumnSpan="2" - Margin="3.5" - VerticalAlignment="Center" - FontSize="18" - Foreground="Red" - Text="Exceptions Viewer" /> + <Grid Grid.Row="1"> + <Grid.RowDefinitions> + <RowDefinition Height="*" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> - <ListBox x:Name="lbExceptions" - Grid.Row="1" - Margin="3.5" - ItemsSource="{Binding}" - SelectionChanged="lbExceptions_SelectionChanged" /> + <ListBox x:Name="lbExceptions" + Margin="3.5" + ItemsSource="{Binding}" + SelectionChanged="lbExceptions_SelectionChanged" /> + <Button Grid.Row="1" + Width="135" + Margin="7" + HorizontalAlignment="Center" + VerticalAlignment="Center" + Click="ClearExceptions_Click" + Content="Clear Exceptions" /> + </Grid> <ScrollViewer Grid.Row="1" Grid.Column="1" Margin="3.5" @@ -42,9 +49,7 @@ <RowDefinition Height="Auto" /> </Grid.RowDefinitions> - <Rectangle Fill="BlanchedAlmond" /> <TextBlock x:Name="tbAction" - FontSize="14" Text="Action" TextDecorations="Underline" /> <TextBlock Grid.Row="1" @@ -54,10 +59,8 @@ Visibility="{Binding ElementName=tbAction, Path=Visibility}" /> - <Rectangle Grid.Row="2" Fill="BlanchedAlmond" /> <TextBlock Grid.Row="2" Margin="0,7,0,0" - FontSize="14" Text="Control Name" TextDecorations="Underline" Visibility="{Binding ElementName=tbAction, @@ -69,21 +72,18 @@ Visibility="{Binding ElementName=tbAction, Path=Visibility}" /> - <Rectangle Grid.Row="4" Fill="BlanchedAlmond" /> <TextBlock Grid.Row="4" Margin="0,7,0,0" - FontSize="14" Text="Message" TextDecorations="Underline" /> + <TextBlock Grid.Row="5" FontSize="11" Text="{Binding Path=Exception.Message}" TextWrapping="Wrap" /> - <Rectangle Grid.Row="6" Fill="BlanchedAlmond" /> <TextBlock Grid.Row="6" Margin="0,7,0,0" - FontSize="14" Text="Stack Trace" TextDecorations="Underline" /> <TextBlock Grid.Row="7" @@ -93,13 +93,5 @@ </Grid> </ScrollViewer> - <Button Grid.Row="2" - Grid.Column="1" - Margin="11" - HorizontalAlignment="Right" - VerticalAlignment="Center" - Click="ClearExceptions_Click" - Content="Clear Exceptions" - Padding="7" /> </Grid> </UserControl>
--- a/SilverlightGlimpse/SilverlightGlimpse/Controls/ExceptionsViewer.xaml.cs Sun Apr 22 09:01:20 2012 +0100 +++ b/SilverlightGlimpse/SilverlightGlimpse/Controls/ExceptionsViewer.xaml.cs Sun Apr 22 13:33:42 2012 +0100 @@ -1,7 +1,6 @@ -using System; -using System.Windows; +using System.Windows; +using System.Windows.Controls; using SilverlightGlimpse.Models; -using System.Windows.Controls; using SilverlightGlimpse.Services; namespace SilverlightGlimpse.Controls @@ -20,20 +19,19 @@ private void ExceptionsViewer_Loaded(object sender, RoutedEventArgs e) { - this.DataContext = GlimpseService.CreateInstance.HostExceptions; + DataContext = GlimpseService.CreateInstance.HostExceptions; if (GlimpseService.CreateInstance.HostExceptions.Count > 0) - this.lbExceptions.SelectedIndex = 0; + lbExceptions.SelectedIndex = 0; } private void lbExceptions_SelectionChanged(object sender, SelectionChangedEventArgs e) { - if (this.lbExceptions.SelectedItem != null && this.lbExceptions.SelectedItem is ExceptionWrapper) - { - if (((ExceptionWrapper)this.lbExceptions.SelectedItem).IsValidationException) - this.tbAction.Visibility = Visibility.Visible; - else - this.tbAction.Visibility = Visibility.Collapsed; - } + if (lbExceptions.SelectedItem == null || !(lbExceptions.SelectedItem is ExceptionWrapper)) + return; + + tbAction.Visibility = ((ExceptionWrapper)lbExceptions.SelectedItem).IsValidationException + ? Visibility.Visible + : Visibility.Collapsed; } } } \ No newline at end of file
--- a/SilverlightGlimpse/SilverlightGlimpse/Controls/GlimpseViewer.xaml Sun Apr 22 09:01:20 2012 +0100 +++ b/SilverlightGlimpse/SilverlightGlimpse/Controls/GlimpseViewer.xaml Sun Apr 22 13:33:42 2012 +0100 @@ -3,25 +3,28 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:c="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls" xmlns:localc="clr-namespace:SilverlightGlimpse.Controls"> - <Grid x:Name="LayoutRoot" Background="Khaki"> + <Grid x:Name="LayoutRoot"> <Grid.Resources> - <SolidColorBrush x:Name="noExceptionsBrush" Color="LightGreen" /> + <SolidColorBrush x:Name="noExceptionsBrush" Color="SteelBlue" /> <SolidColorBrush x:Name="hasExceptionsBrush" Color="Red" /> </Grid.Resources> + <Grid x:Name="layoutInstrumentPanel"> <StackPanel Orientation="Horizontal"> <Grid Margin="7"> <Ellipse x:Name="elpValidationExceptions" Width="40" Height="40" - Fill="LightGreen" - Stroke="Brown" - StrokeThickness="2" /> + Fill="{StaticResource noExceptionsBrush}" + Stroke="LightBlue" + StrokeThickness="3" /> + <TextBlock x:Name="tbValidationExceptions" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="12" FontWeight="Bold" + Foreground="White" Text="0" ToolTipService.ToolTip="Binding Exception Count" /> </Grid> @@ -29,20 +32,22 @@ <Ellipse x:Name="elpUnhandledExceptions" Width="40" Height="40" - Fill="LightGreen" - Stroke="Brown" - StrokeThickness="2" /> + Fill="{StaticResource noExceptionsBrush}" + Stroke="LightBlue" + StrokeThickness="3" /> <TextBlock x:Name="tbUnhandledExceptions" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="12" FontWeight="Bold" + Foreground="White" Text="0" ToolTipService.ToolTip="Unhandled Exception Count" /> </Grid> <Button x:Name="btnExpand" Margin="7" VerticalAlignment="Center" + Click="btnExpand_Click" Content="Expand" /> </StackPanel> </Grid> @@ -51,28 +56,34 @@ <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> - <TextBlock Margin="3.5" + + <TextBlock Margin="3.5,0,0,0" VerticalAlignment="Center" + FontFamily="Lucida Sans Unicode" FontSize="18" - Foreground="DarkGreen" - Text="Glimpse Viewer" /> + Foreground="SteelBlue" + Text="Silverlight Glimpse" /> + <Button x:Name="btnContract" Margin="7" HorizontalAlignment="Right" VerticalAlignment="Center" + Click="btnContract_Click" Content="Contract" /> + <c:TabControl Grid.Row="1" - Width="690" - Height="390" - Background="Khaki" - SelectedIndex="2"> + Width="500" + Height="250"> + <c:TabItem Header="Exceptions"> <localc:ExceptionsViewer /> </c:TabItem> + <c:TabItem Header="Bindings with no Source"> <localc:BrokenBindingsViewer /> </c:TabItem> </c:TabControl> + </Grid> </Grid>
--- a/SilverlightGlimpse/SilverlightGlimpse/Controls/GlimpseViewer.xaml.cs Sun Apr 22 09:01:20 2012 +0100 +++ b/SilverlightGlimpse/SilverlightGlimpse/Controls/GlimpseViewer.xaml.cs Sun Apr 22 13:33:42 2012 +0100 @@ -10,18 +10,18 @@ public GlimpseViewer() { InitializeComponent(); - this.DataContext = GlimpseService.CreateInstance; + DataContext = GlimpseService.CreateInstance; GlimpseService.CreateInstance.HostExceptions.CollectionChanged += HostExceptions_CollectionChanged; } - private void btnContract_Click(object sender, System.Windows.RoutedEventArgs e) + private void btnContract_Click(object sender, RoutedEventArgs e) { - this.layoutViewer.Visibility = Visibility.Collapsed; + layoutViewer.Visibility = Visibility.Collapsed; } - private void btnExpand_Click(object sender, System.Windows.RoutedEventArgs e) + private void btnExpand_Click(object sender, RoutedEventArgs e) { - this.layoutViewer.Visibility = Visibility.Visible; + layoutViewer.Visibility = Visibility.Visible; } private void HostExceptions_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) @@ -37,17 +37,17 @@ unhandledExceptionCount++; } - this.tbValidationExceptions.Text = validationExceptionCount.ToString(); + tbValidationExceptions.Text = validationExceptionCount.ToString(); - this.elpValidationExceptions.Fill = validationExceptionCount == 0 - ? this.noExceptionsBrush - : this.hasExceptionsBrush; + elpValidationExceptions.Fill = validationExceptionCount == 0 + ? noExceptionsBrush + : hasExceptionsBrush; - this.tbUnhandledExceptions.Text = unhandledExceptionCount.ToString(); + tbUnhandledExceptions.Text = unhandledExceptionCount.ToString(); - this.elpUnhandledExceptions.Fill = unhandledExceptionCount == 0 - ? this.noExceptionsBrush - : this.hasExceptionsBrush; + elpUnhandledExceptions.Fill = unhandledExceptionCount == 0 + ? noExceptionsBrush + : hasExceptionsBrush; } } } \ No newline at end of file
--- a/SilverlightGlimpse/SilverlightGlimpse/Controls/LoadExceptionViewer.xaml Sun Apr 22 09:01:20 2012 +0100 +++ b/SilverlightGlimpse/SilverlightGlimpse/Controls/LoadExceptionViewer.xaml Sun Apr 22 13:33:42 2012 +0100 @@ -8,30 +8,26 @@ BorderThickness="6" CornerRadius="5"> <Grid x:Name="LayoutRoot"> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="200" /> - <ColumnDefinition Width="*" /> - </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> + <RowDefinition Height="100" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> - <Rectangle Grid.ColumnSpan="2" Fill="SteelBlue" /> + <Rectangle Fill="SteelBlue" /> + <TextBlock x:Name="txtTitle" - Grid.ColumnSpan="2" Margin="3.5" VerticalAlignment="Center" Foreground="White" - Text="Exception" /> + Text="Exception source goes here from code behind" /> <ListBox x:Name="lbExceptions" Grid.Row="1" Margin="3.5" DisplayMemberPath="Message" /> - <ScrollViewer Grid.Row="1" - Grid.Column="1" + <ScrollViewer Grid.Row="2" Margin="3.5" Background="White" DataContext="{Binding ElementName=lbExceptions, @@ -46,24 +42,19 @@ <RowDefinition Height="Auto" /> </Grid.RowDefinitions> - <TextBlock FontSize="14" - Text="Message" - TextDecorations="Underline" /> + <TextBlock Text="Message" TextDecorations="Underline" /> <TextBlock Grid.Row="1" - FontSize="11" Text="{Binding Path=Message}" TextWrapping="Wrap" /> <TextBlock Grid.Row="2" Margin="0,11,0,0" - FontSize="14" Text="Stack Trace" TextDecorations="Underline" /> <TextBlock Grid.Row="3" FontSize="11" Text="{Binding Path=StackTrace}" TextWrapping="Wrap" /> - </Grid> </ScrollViewer> </Grid>
--- a/SilverlightGlimpse/SilverlightGlimpse/Controls/LoadExceptionViewer.xaml.cs Sun Apr 22 09:01:20 2012 +0100 +++ b/SilverlightGlimpse/SilverlightGlimpse/Controls/LoadExceptionViewer.xaml.cs Sun Apr 22 13:33:42 2012 +0100 @@ -1,9 +1,8 @@ using System; -using System.Windows.Controls; namespace SilverlightGlimpse.Controls { - public partial class LoadExceptionViewer : UserControl + public partial class LoadExceptionViewer { public LoadExceptionViewer() { @@ -12,19 +11,19 @@ public LoadExceptionViewer(Exception e, string sourceLocation) : this() { - this.txtTitle.Text = string.Concat("Exception ", sourceLocation.TrimStart()); + txtTitle.Text = string.Concat("Exception ", sourceLocation.TrimStart()); - Exception ex = e; + var ex = e; while (ex != null) { - this.lbExceptions.Items.Add(ex); + lbExceptions.Items.Add(ex); ex = ex.InnerException; } - if (this.lbExceptions.Items.Count > 0) + if (lbExceptions.Items.Count > 0) { - this.lbExceptions.SelectedIndex = 0; + lbExceptions.SelectedIndex = 0; } } }
--- a/SilverlightGlimpse/SilverlightGlimpse/Services/GlimpseService.cs Sun Apr 22 09:01:20 2012 +0100 +++ b/SilverlightGlimpse/SilverlightGlimpse/Services/GlimpseService.cs Sun Apr 22 13:33:42 2012 +0100 @@ -30,7 +30,7 @@ public static GlimpseService CreateInstance { - get { return (_instance == null) ? _instance = new GlimpseService() : _instance; } + get { return _instance ?? (_instance = new GlimpseService()); } } internal Application App { get; private set; } @@ -45,30 +45,32 @@ public void DisplayLoadFailure(Application app, Exception ex) { - string source = ex.StackTrace.Substring(0, ex.StackTrace.IndexOf(Environment.NewLine)); - Debug.WriteLine("{0} had exception. {1}", this.HostApplicationName, ex.ToString()); + var source = ex.StackTrace.Substring(0, ex.StackTrace.IndexOf(Environment.NewLine, StringComparison.Ordinal)); + Debug.WriteLine("{0} had exception. {1}", HostApplicationName, ex); App = app; App.RootVisual = new LoadExceptionViewer(ex, source); } public void Load(Application app, string hostApplicationName) { - this.App = app; - this.RootVisual = App.RootVisual as FrameworkElement; - this.HostApplicationName = hostApplicationName; + App = app; + RootVisual = App.RootVisual as FrameworkElement; + HostApplicationName = hostApplicationName; RootVisual.BindingValidationError += HostRootVisual_BindingValidationError; App.UnhandledException += Application_UnhandledException; - FloatableWindow window = new FloatableWindow(); - window.Title = this.HostApplicationName; - window.Content = new GlimpseViewer(); - window.ParentLayoutRoot = (Panel)VisualTreeHelper.GetChild(RootVisual, 0); ; - + GlimpseWindow = new FloatableWindow + { + Title = this.HostApplicationName, + Content = new GlimpseViewer(), + ParentLayoutRoot = (Panel) VisualTreeHelper.GetChild(RootVisual, 0), + ResizeMode = ResizeMode.NoResize + }; if (Double.IsNaN(RootVisual.Width)) { //if the host control is autosized (consumes the browser window) then locate Glimpse in the top, left - window.Show(); + GlimpseWindow.Show(10,10); } else { @@ -78,7 +80,7 @@ if (dblLeft < 0) dblLeft = 0; - window.Show(dblLeft,10); + GlimpseWindow.Show(dblLeft, 10); } } #endregion
--- a/SilverlightGlimpse/SilverlightGlimpse/SilverlightGlimpse.csproj Sun Apr 22 09:01:20 2012 +0100 +++ b/SilverlightGlimpse/SilverlightGlimpse/SilverlightGlimpse.csproj Sun Apr 22 13:33:42 2012 +0100 @@ -64,12 +64,10 @@ <Reference Include="System.Windows" /> <Reference Include="system" /> <Reference Include="System.Core" /> - <Reference Include="System.Net" /> <Reference Include="System.Windows.Controls, Version=5.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <HintPath>..\Libs\System.Windows.Controls.dll</HintPath> </Reference> <Reference Include="System.Xml" /> - <Reference Include="System.Windows.Browser" /> </ItemGroup> <ItemGroup> <Compile Include="Controls\BrokenBindingsViewer.xaml.cs">