1360 lines
57 KiB
C#
1360 lines
57 KiB
C#
|
//---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Copyright (C) Microsoft Corporation. All rights reserved.
|
||
|
//
|
||
|
//---------------------------------------------------------------------------
|
||
|
|
||
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.ComponentModel;
|
||
|
using System.Diagnostics;
|
||
|
using System.Windows;
|
||
|
using System.Windows.Controls;
|
||
|
using System.Windows.Media;
|
||
|
using EGC = ExtendedGrid.Microsoft.Windows.Controls;
|
||
|
|
||
|
namespace ExtendedGrid.Microsoft.Windows.Controls
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// A Border used to provide the default look of headers.
|
||
|
/// When Background or BorderBrush are set, the rendering will
|
||
|
/// revert back to the default Border implementation.
|
||
|
/// </summary>
|
||
|
public class DataGridHeaderBorder : Border
|
||
|
{
|
||
|
static DataGridHeaderBorder()
|
||
|
{
|
||
|
// This allows the control to re-render correctly when there is a theme change. It relies
|
||
|
// on a string resource in the theme-specific resource dictionaries.
|
||
|
EGC.DataGridHelper.HookThemeChange(typeof(EGC.DataGridHeaderBorder), new PropertyChangedCallback(OnThemeChange));
|
||
|
|
||
|
// We always set this to true on these borders, so just default it to true here.
|
||
|
SnapsToDevicePixelsProperty.OverrideMetadata(typeof(EGC.DataGridHeaderBorder), new FrameworkPropertyMetadata(true));
|
||
|
}
|
||
|
|
||
|
#region Header Appearance Properties
|
||
|
|
||
|
/// <summary>
|
||
|
/// Whether the hover look should be applied.
|
||
|
/// </summary>
|
||
|
public bool IsHovered
|
||
|
{
|
||
|
get { return (bool)GetValue(IsHoveredProperty); }
|
||
|
set { SetValue(IsHoveredProperty, value); }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// DependencyProperty for IsHovered.
|
||
|
/// </summary>
|
||
|
public static readonly DependencyProperty IsHoveredProperty =
|
||
|
DependencyProperty.Register("IsHovered", typeof(bool), typeof(EGC.DataGridHeaderBorder), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender));
|
||
|
|
||
|
/// <summary>
|
||
|
/// Whether the pressed look should be applied.
|
||
|
/// </summary>
|
||
|
public bool IsPressed
|
||
|
{
|
||
|
get { return (bool)GetValue(IsPressedProperty); }
|
||
|
set { SetValue(IsPressedProperty, value); }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// DependencyProperty for IsPressed.
|
||
|
/// </summary>
|
||
|
public static readonly DependencyProperty IsPressedProperty =
|
||
|
DependencyProperty.Register("IsPressed", typeof(bool), typeof(EGC.DataGridHeaderBorder), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsArrange));
|
||
|
|
||
|
/// <summary>
|
||
|
/// When false, will not apply the hover look even when IsHovered is true.
|
||
|
/// </summary>
|
||
|
public bool IsClickable
|
||
|
{
|
||
|
get { return (bool)GetValue(IsClickableProperty); }
|
||
|
set { SetValue(IsClickableProperty, value); }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// DependencyProperty for IsClickable.
|
||
|
/// </summary>
|
||
|
public static readonly DependencyProperty IsClickableProperty =
|
||
|
DependencyProperty.Register("IsClickable", typeof(bool), typeof(EGC.DataGridHeaderBorder), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsArrange));
|
||
|
|
||
|
/// <summary>
|
||
|
/// Whether to appear sorted.
|
||
|
/// </summary>
|
||
|
public ListSortDirection? SortDirection
|
||
|
{
|
||
|
get { return (ListSortDirection?)GetValue(SortDirectionProperty); }
|
||
|
set { SetValue(SortDirectionProperty, value); }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// DependencyProperty for SortDirection.
|
||
|
/// </summary>
|
||
|
public static readonly DependencyProperty SortDirectionProperty =
|
||
|
DependencyProperty.Register("SortDirection", typeof(ListSortDirection?), typeof(EGC.DataGridHeaderBorder), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
|
||
|
|
||
|
/// <summary>
|
||
|
/// Whether to appear selected.
|
||
|
/// </summary>
|
||
|
public bool IsSelected
|
||
|
{
|
||
|
get { return (bool)GetValue(IsSelectedProperty); }
|
||
|
set { SetValue(IsSelectedProperty, value); }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// DependencyProperty for IsSelected.
|
||
|
/// </summary>
|
||
|
public static readonly DependencyProperty IsSelectedProperty =
|
||
|
DependencyProperty.Register("IsSelected", typeof(bool), typeof(EGC.DataGridHeaderBorder), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender));
|
||
|
|
||
|
/// <summary>
|
||
|
/// Vertical = column header
|
||
|
/// Horizontal = row header
|
||
|
/// </summary>
|
||
|
public Orientation Orientation
|
||
|
{
|
||
|
get { return (Orientation)GetValue(OrientationProperty); }
|
||
|
set { SetValue(OrientationProperty, value); }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// DependencyProperty for Orientation.
|
||
|
/// </summary>
|
||
|
public static readonly DependencyProperty OrientationProperty =
|
||
|
DependencyProperty.Register("Orientation", typeof(Orientation), typeof(EGC.DataGridHeaderBorder), new FrameworkPropertyMetadata(Orientation.Vertical, FrameworkPropertyMetadataOptions.AffectsRender));
|
||
|
|
||
|
/// <summary>
|
||
|
/// When there is a Background or BorderBrush, revert to the Border implementation.
|
||
|
/// </summary>
|
||
|
private bool UsingBorderImplementation
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return (Background != null) || (BorderBrush != null);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Property that indicates the brush to use when drawing seperators between headers.
|
||
|
/// </summary>
|
||
|
public Brush SeparatorBrush
|
||
|
{
|
||
|
get { return (Brush)GetValue(SeparatorBrushProperty); }
|
||
|
set { SetValue(SeparatorBrushProperty, value); }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// DependencyProperty for SeparatorBrush.
|
||
|
/// </summary>
|
||
|
public static readonly DependencyProperty SeparatorBrushProperty =
|
||
|
DependencyProperty.Register("SeparatorBrush", typeof(Brush), typeof(EGC.DataGridHeaderBorder), new FrameworkPropertyMetadata(null));
|
||
|
|
||
|
/// <summary>
|
||
|
/// Property that indicates the Visibility for the header seperators.
|
||
|
/// </summary>
|
||
|
public Visibility SeparatorVisibility
|
||
|
{
|
||
|
get { return (Visibility)GetValue(SeparatorVisibilityProperty); }
|
||
|
set { SetValue(SeparatorVisibilityProperty, value); }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// DependencyProperty for SeperatorBrush.
|
||
|
/// </summary>
|
||
|
public static readonly DependencyProperty SeparatorVisibilityProperty =
|
||
|
DependencyProperty.Register("SeparatorVisibility", typeof(Visibility), typeof(EGC.DataGridHeaderBorder), new FrameworkPropertyMetadata(Visibility.Visible));
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region Theme Information
|
||
|
|
||
|
// Theme names
|
||
|
private const string ClassicThemeName = "Classic";
|
||
|
private const string AeroNormalColorName = "Aero.NormalColor";
|
||
|
private const string LunaNormalColorName = "Luna.NormalColor";
|
||
|
private const string LunaHomeSteadName = "Luna.HomeStead";
|
||
|
private const string LunaMetallicName = "Luna.Metallic";
|
||
|
private const string RoyaleNormalColorName = "Royale.NormalColor";
|
||
|
|
||
|
/// <summary>
|
||
|
/// The name of the current theme.
|
||
|
/// Accessing this property finishes hooking up
|
||
|
/// the theme change notification.
|
||
|
/// </summary>
|
||
|
private string Theme
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
// Get the current theme string. Also finishes hooking up the theme change notification.
|
||
|
string theme = EGC.DataGridHelper.GetTheme(this);
|
||
|
if (String.IsNullOrEmpty(theme))
|
||
|
{
|
||
|
// Use Classic as the default if an unknown theme is encountered
|
||
|
theme = ClassicThemeName;
|
||
|
}
|
||
|
|
||
|
return theme;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Called when the theme changes.
|
||
|
/// </summary>
|
||
|
private static void OnThemeChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||
|
{
|
||
|
// Release all the old resources.
|
||
|
ReleaseCache();
|
||
|
|
||
|
EGC.DataGridHeaderBorder border = (EGC.DataGridHeaderBorder)d;
|
||
|
|
||
|
// Everything needs to be re-done.
|
||
|
border.InvalidateMeasure();
|
||
|
border.InvalidateArrange();
|
||
|
border.InvalidateVisual();
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region Layout
|
||
|
|
||
|
/// <summary>
|
||
|
/// Calculates the desired size of the element given the constraint.
|
||
|
/// </summary>
|
||
|
protected override Size MeasureOverride(Size constraint)
|
||
|
{
|
||
|
if (UsingBorderImplementation)
|
||
|
{
|
||
|
// Revert to the Border implementation
|
||
|
return base.MeasureOverride(constraint);
|
||
|
}
|
||
|
|
||
|
UIElement child = Child;
|
||
|
if (child != null)
|
||
|
{
|
||
|
// Use the public Padding property if it's set
|
||
|
Thickness padding = Padding;
|
||
|
if (padding.Equals(new Thickness()))
|
||
|
{
|
||
|
padding = DefaultPadding;
|
||
|
}
|
||
|
|
||
|
double childWidth = constraint.Width;
|
||
|
double childHeight = constraint.Height;
|
||
|
|
||
|
// If there is an actual constraint, then reserve space for the chrome
|
||
|
if (!Double.IsInfinity(childWidth))
|
||
|
{
|
||
|
childWidth = Math.Max(0.0, childWidth - padding.Left - padding.Right);
|
||
|
}
|
||
|
|
||
|
if (!Double.IsInfinity(childHeight))
|
||
|
{
|
||
|
childHeight = Math.Max(0.0, childHeight - padding.Top - padding.Bottom);
|
||
|
}
|
||
|
|
||
|
child.Measure(new Size(childWidth, childHeight));
|
||
|
Size desiredSize = child.DesiredSize;
|
||
|
|
||
|
// Add on the reserved space for the chrome
|
||
|
return new Size(desiredSize.Width + padding.Left + padding.Right, desiredSize.Height + padding.Top + padding.Bottom);
|
||
|
}
|
||
|
|
||
|
return new Size();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Positions children and returns the final size of the element.
|
||
|
/// </summary>
|
||
|
protected override Size ArrangeOverride(Size arrangeSize)
|
||
|
{
|
||
|
if (UsingBorderImplementation)
|
||
|
{
|
||
|
// Revert to the Border implementation
|
||
|
return base.ArrangeOverride(arrangeSize);
|
||
|
}
|
||
|
|
||
|
UIElement child = Child;
|
||
|
if (child != null)
|
||
|
{
|
||
|
// Use the public Padding property if it's set
|
||
|
Thickness padding = Padding;
|
||
|
if (padding.Equals(new Thickness()))
|
||
|
{
|
||
|
padding = DefaultPadding;
|
||
|
}
|
||
|
|
||
|
// Reserve space for the chrome
|
||
|
double childWidth = Math.Max(0.0, arrangeSize.Width - padding.Left - padding.Right);
|
||
|
double childHeight = Math.Max(0.0, arrangeSize.Height - padding.Top - padding.Bottom);
|
||
|
|
||
|
child.Arrange(new Rect(padding.Left, padding.Top, childWidth, childHeight));
|
||
|
}
|
||
|
|
||
|
return arrangeSize;
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region Rendering
|
||
|
|
||
|
/// <summary>
|
||
|
/// Called when this element should re-render.
|
||
|
/// </summary>
|
||
|
protected override void OnRender(DrawingContext dc)
|
||
|
{
|
||
|
if (UsingBorderImplementation)
|
||
|
{
|
||
|
// Revert to the Border implementation
|
||
|
base.OnRender(dc);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Choose the appropriate rendering based on the current theme
|
||
|
switch (Theme)
|
||
|
{
|
||
|
case ClassicThemeName:
|
||
|
RenderClassic(dc);
|
||
|
break;
|
||
|
|
||
|
case LunaNormalColorName:
|
||
|
RenderLuna(dc, Luna.NormalColor);
|
||
|
break;
|
||
|
|
||
|
case LunaHomeSteadName:
|
||
|
RenderLuna(dc, Luna.HomeStead);
|
||
|
break;
|
||
|
|
||
|
case LunaMetallicName:
|
||
|
RenderLuna(dc, Luna.Metallic);
|
||
|
break;
|
||
|
|
||
|
case RoyaleNormalColorName:
|
||
|
RenderLuna(dc, Luna.Metallic);
|
||
|
break;
|
||
|
|
||
|
case AeroNormalColorName:
|
||
|
RenderAeroNormalColor(dc);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns a default padding for the various themes for use
|
||
|
/// by measure and arrange.
|
||
|
/// </summary>
|
||
|
private Thickness DefaultPadding
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
Thickness padding = new Thickness(3.0); // The default padding
|
||
|
if (Orientation == Orientation.Vertical)
|
||
|
{
|
||
|
if (Theme == AeroNormalColorName)
|
||
|
{
|
||
|
// Reserve space above for the arrow
|
||
|
padding = new Thickness(5.0, 4.0, 5.0, 4.0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Reserve space to the right for the arrow
|
||
|
padding.Right = 15.0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// When pressed, offset the child
|
||
|
if (IsPressed && IsClickable)
|
||
|
{
|
||
|
padding.Left += 1.0;
|
||
|
padding.Top += 1.0;
|
||
|
padding.Right -= 1.0;
|
||
|
padding.Bottom -= 1.0;
|
||
|
}
|
||
|
|
||
|
return padding;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static double Max0(double d)
|
||
|
{
|
||
|
return Math.Max(0.0, d);
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region Aero
|
||
|
|
||
|
private void RenderAeroNormalColor(DrawingContext dc)
|
||
|
{
|
||
|
Size size = RenderSize;
|
||
|
bool horizontal = Orientation == Orientation.Horizontal;
|
||
|
bool isClickable = IsClickable && IsEnabled;
|
||
|
bool isHovered = isClickable && IsHovered;
|
||
|
bool isPressed = isClickable && IsPressed;
|
||
|
ListSortDirection? sortDirection = SortDirection;
|
||
|
bool isSorted = sortDirection != null;
|
||
|
bool isSelected = IsSelected;
|
||
|
bool hasBevel = (!isHovered && !isPressed && !isSorted && !isSelected);
|
||
|
|
||
|
EnsureCache((int)AeroFreezables.NumFreezables);
|
||
|
|
||
|
if (horizontal)
|
||
|
{
|
||
|
// When horizontal, rotate the rendering by -90 degrees
|
||
|
Matrix m1 = new Matrix();
|
||
|
m1.RotateAt(-90.0, 0.0, 0.0);
|
||
|
Matrix m2 = new Matrix();
|
||
|
m2.Translate(0.0, size.Height);
|
||
|
|
||
|
MatrixTransform horizontalRotate = new MatrixTransform(m1 * m2);
|
||
|
horizontalRotate.Freeze();
|
||
|
dc.PushTransform(horizontalRotate);
|
||
|
|
||
|
double temp = size.Width;
|
||
|
size.Width = size.Height;
|
||
|
size.Height = temp;
|
||
|
}
|
||
|
|
||
|
if (hasBevel)
|
||
|
{
|
||
|
// This is a highlight that can be drawn by just filling the background with the color.
|
||
|
// It will be seen through the gab between the border and the background.
|
||
|
LinearGradientBrush bevel = (LinearGradientBrush)GetCachedFreezable((int)AeroFreezables.NormalBevel);
|
||
|
if (bevel == null)
|
||
|
{
|
||
|
bevel = new LinearGradientBrush();
|
||
|
bevel.StartPoint = new Point();
|
||
|
bevel.EndPoint = new Point(0.0, 1.0);
|
||
|
bevel.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF), 0.0));
|
||
|
bevel.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF), 0.4));
|
||
|
bevel.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xFC, 0xFC, 0xFD), 0.4));
|
||
|
bevel.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xFB, 0xFC, 0xFC), 1.0));
|
||
|
bevel.Freeze();
|
||
|
|
||
|
CacheFreezable(bevel, (int)AeroFreezables.NormalBevel);
|
||
|
}
|
||
|
|
||
|
dc.DrawRectangle(bevel, null, new Rect(0.0, 0.0, size.Width, size.Height));
|
||
|
}
|
||
|
|
||
|
// Fill the background
|
||
|
AeroFreezables backgroundType = AeroFreezables.NormalBackground;
|
||
|
if (isPressed)
|
||
|
{
|
||
|
backgroundType = AeroFreezables.PressedBackground;
|
||
|
}
|
||
|
else if (isHovered)
|
||
|
{
|
||
|
backgroundType = AeroFreezables.HoveredBackground;
|
||
|
}
|
||
|
else if (isSorted || isSelected)
|
||
|
{
|
||
|
backgroundType = AeroFreezables.SortedBackground;
|
||
|
}
|
||
|
|
||
|
LinearGradientBrush background = (LinearGradientBrush)GetCachedFreezable((int)backgroundType);
|
||
|
if (background == null)
|
||
|
{
|
||
|
background = new LinearGradientBrush();
|
||
|
background.StartPoint = new Point();
|
||
|
background.EndPoint = new Point(0.0, 1.0);
|
||
|
|
||
|
switch (backgroundType)
|
||
|
{
|
||
|
case AeroFreezables.NormalBackground:
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF), 0.0));
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF), 0.4));
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xF7, 0xF8, 0xFA), 0.4));
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xF1, 0xF2, 0xF4), 1.0));
|
||
|
break;
|
||
|
|
||
|
case AeroFreezables.PressedBackground:
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xBC, 0xE4, 0xF9), 0.0));
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xBC, 0xE4, 0xF9), 0.4));
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0x8D, 0xD6, 0xF7), 0.4));
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0x8A, 0xD1, 0xF5), 1.0));
|
||
|
break;
|
||
|
|
||
|
case AeroFreezables.HoveredBackground:
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xE3, 0xF7, 0xFF), 0.0));
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xE3, 0xF7, 0xFF), 0.4));
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xBD, 0xED, 0xFF), 0.4));
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xB7, 0xE7, 0xFB), 1.0));
|
||
|
break;
|
||
|
|
||
|
case AeroFreezables.SortedBackground:
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xF2, 0xF9, 0xFC), 0.0));
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xF2, 0xF9, 0xFC), 0.4));
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xE1, 0xF1, 0xF9), 0.4));
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xD8, 0xEC, 0xF6), 1.0));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
background.Freeze();
|
||
|
|
||
|
CacheFreezable(background, (int)backgroundType);
|
||
|
}
|
||
|
|
||
|
dc.DrawRectangle(background, null, new Rect(0.0, 0.0, size.Width, size.Height));
|
||
|
|
||
|
if (size.Width >= 2.0)
|
||
|
{
|
||
|
// Draw the borders on the sides
|
||
|
AeroFreezables sideType = AeroFreezables.NormalSides;
|
||
|
if (isPressed)
|
||
|
{
|
||
|
sideType = AeroFreezables.PressedSides;
|
||
|
}
|
||
|
else if (isHovered)
|
||
|
{
|
||
|
sideType = AeroFreezables.HoveredSides;
|
||
|
}
|
||
|
else if (isSorted || isSelected)
|
||
|
{
|
||
|
sideType = AeroFreezables.SortedSides;
|
||
|
}
|
||
|
|
||
|
if (SeparatorVisibility == Visibility.Visible)
|
||
|
{
|
||
|
Brush sideBrush;
|
||
|
if (SeparatorBrush != null)
|
||
|
{
|
||
|
sideBrush = SeparatorBrush;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sideBrush = (Brush)GetCachedFreezable((int)sideType);
|
||
|
if (sideBrush == null)
|
||
|
{
|
||
|
LinearGradientBrush lgBrush = null;
|
||
|
if (sideType != AeroFreezables.SortedSides)
|
||
|
{
|
||
|
lgBrush = new LinearGradientBrush();
|
||
|
lgBrush.StartPoint = new Point();
|
||
|
lgBrush.EndPoint = new Point(0.0, 1.0);
|
||
|
sideBrush = lgBrush;
|
||
|
}
|
||
|
|
||
|
switch (sideType)
|
||
|
{
|
||
|
case AeroFreezables.NormalSides:
|
||
|
lgBrush.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xF2, 0xF2, 0xF2), 0.0));
|
||
|
lgBrush.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xEF, 0xEF, 0xEF), 0.4));
|
||
|
lgBrush.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xE7, 0xE8, 0xEA), 0.4));
|
||
|
lgBrush.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xDE, 0xDF, 0xE1), 1.0));
|
||
|
break;
|
||
|
|
||
|
case AeroFreezables.PressedSides:
|
||
|
lgBrush.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0x7A, 0x9E, 0xB1), 0.0));
|
||
|
lgBrush.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0x7A, 0x9E, 0xB1), 0.4));
|
||
|
lgBrush.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0x50, 0x91, 0xAF), 0.4));
|
||
|
lgBrush.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0x4D, 0x8D, 0xAD), 1.0));
|
||
|
break;
|
||
|
|
||
|
case AeroFreezables.HoveredSides:
|
||
|
lgBrush.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0x88, 0xCB, 0xEB), 0.0));
|
||
|
lgBrush.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0x88, 0xCB, 0xEB), 0.4));
|
||
|
lgBrush.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0x69, 0xBB, 0xE3), 0.4));
|
||
|
lgBrush.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0x69, 0xBB, 0xE3), 1.0));
|
||
|
break;
|
||
|
|
||
|
case AeroFreezables.SortedSides:
|
||
|
sideBrush = new SolidColorBrush(Color.FromArgb(0xFF, 0x96, 0xD9, 0xF9));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
sideBrush.Freeze();
|
||
|
|
||
|
CacheFreezable(sideBrush, (int)sideType);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
dc.DrawRectangle(sideBrush, null, new Rect(0.0, 0.0, 1.0, Max0(size.Height - 0.95)));
|
||
|
dc.DrawRectangle(sideBrush, null, new Rect(size.Width - 1.0, 0.0, 1.0, Max0(size.Height - 0.95)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (isPressed && (size.Width >= 4.0) && (size.Height >= 4.0))
|
||
|
{
|
||
|
// When pressed, there are added borders on the left and top
|
||
|
LinearGradientBrush topBrush = (LinearGradientBrush)GetCachedFreezable((int)AeroFreezables.PressedTop);
|
||
|
if (topBrush == null)
|
||
|
{
|
||
|
topBrush = new LinearGradientBrush();
|
||
|
topBrush.StartPoint = new Point();
|
||
|
topBrush.EndPoint = new Point(0.0, 1.0);
|
||
|
topBrush.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0x86, 0xA3, 0xB2), 0.0));
|
||
|
topBrush.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0x86, 0xA3, 0xB2), 0.1));
|
||
|
topBrush.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xAA, 0xCE, 0xE1), 0.9));
|
||
|
topBrush.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xAA, 0xCE, 0xE1), 1.0));
|
||
|
topBrush.Freeze();
|
||
|
|
||
|
CacheFreezable(topBrush, (int)AeroFreezables.PressedTop);
|
||
|
}
|
||
|
|
||
|
dc.DrawRectangle(topBrush, null, new Rect(0.0, 0.0, size.Width, 2.0));
|
||
|
|
||
|
LinearGradientBrush pressedBevel = (LinearGradientBrush)GetCachedFreezable((int)AeroFreezables.PressedBevel);
|
||
|
if (pressedBevel == null)
|
||
|
{
|
||
|
pressedBevel = new LinearGradientBrush();
|
||
|
pressedBevel.StartPoint = new Point();
|
||
|
pressedBevel.EndPoint = new Point(0.0, 1.0);
|
||
|
pressedBevel.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xA2, 0xCB, 0xE0), 0.0));
|
||
|
pressedBevel.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xA2, 0xCB, 0xE0), 0.4));
|
||
|
pressedBevel.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0x72, 0xBC, 0xDF), 0.4));
|
||
|
pressedBevel.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0x6E, 0xB8, 0xDC), 1.0));
|
||
|
pressedBevel.Freeze();
|
||
|
|
||
|
CacheFreezable(pressedBevel, (int)AeroFreezables.PressedBevel);
|
||
|
}
|
||
|
|
||
|
dc.DrawRectangle(pressedBevel, null, new Rect(1.0, 0.0, 1.0, size.Height - 0.95));
|
||
|
dc.DrawRectangle(pressedBevel, null, new Rect(size.Width - 2.0, 0.0, 1.0, size.Height - 0.95));
|
||
|
}
|
||
|
|
||
|
if (size.Height >= 2.0)
|
||
|
{
|
||
|
// Draw the bottom border
|
||
|
AeroFreezables bottomType = AeroFreezables.NormalBottom;
|
||
|
if (isPressed)
|
||
|
{
|
||
|
bottomType = AeroFreezables.PressedOrHoveredBottom;
|
||
|
}
|
||
|
else if (isHovered)
|
||
|
{
|
||
|
bottomType = AeroFreezables.PressedOrHoveredBottom;
|
||
|
}
|
||
|
else if (isSorted || isSelected)
|
||
|
{
|
||
|
bottomType = AeroFreezables.SortedBottom;
|
||
|
}
|
||
|
|
||
|
SolidColorBrush bottomBrush = (SolidColorBrush)GetCachedFreezable((int)bottomType);
|
||
|
if (bottomBrush == null)
|
||
|
{
|
||
|
switch (bottomType)
|
||
|
{
|
||
|
case AeroFreezables.NormalBottom:
|
||
|
bottomBrush = new SolidColorBrush(Color.FromArgb(0xFF, 0xD5, 0xD5, 0xD5));
|
||
|
break;
|
||
|
|
||
|
case AeroFreezables.PressedOrHoveredBottom:
|
||
|
bottomBrush = new SolidColorBrush(Color.FromArgb(0xFF, 0x93, 0xC9, 0xE3));
|
||
|
break;
|
||
|
|
||
|
case AeroFreezables.SortedBottom:
|
||
|
bottomBrush = new SolidColorBrush(Color.FromArgb(0xFF, 0x96, 0xD9, 0xF9));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
bottomBrush.Freeze();
|
||
|
|
||
|
CacheFreezable(bottomBrush, (int)bottomType);
|
||
|
}
|
||
|
|
||
|
dc.DrawRectangle(bottomBrush, null, new Rect(0.0, size.Height - 1.0, size.Width, 1.0));
|
||
|
}
|
||
|
|
||
|
if (isSorted && (size.Width > 14.0) && (size.Height > 10.0))
|
||
|
{
|
||
|
// Draw the sort arrow
|
||
|
TranslateTransform positionTransform = new TranslateTransform((size.Width - 8.0) * 0.5, 1.0);
|
||
|
positionTransform.Freeze();
|
||
|
dc.PushTransform(positionTransform);
|
||
|
|
||
|
bool ascending = (sortDirection == ListSortDirection.Ascending);
|
||
|
PathGeometry arrowGeometry = (PathGeometry)GetCachedFreezable(ascending ? (int)AeroFreezables.ArrowUpGeometry : (int)AeroFreezables.ArrowDownGeometry);
|
||
|
if (arrowGeometry == null)
|
||
|
{
|
||
|
arrowGeometry = new PathGeometry();
|
||
|
PathFigure arrowFigure = new PathFigure();
|
||
|
|
||
|
if (ascending)
|
||
|
{
|
||
|
arrowFigure.StartPoint = new Point(0.0, 4.0);
|
||
|
|
||
|
LineSegment line = new LineSegment(new Point(4.0, 0.0), false);
|
||
|
line.Freeze();
|
||
|
arrowFigure.Segments.Add(line);
|
||
|
|
||
|
line = new LineSegment(new Point(8.0, 4.0), false);
|
||
|
line.Freeze();
|
||
|
arrowFigure.Segments.Add(line);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
arrowFigure.StartPoint = new Point(0.0, 0.0);
|
||
|
|
||
|
LineSegment line = new LineSegment(new Point(8.0, 0.0), false);
|
||
|
line.Freeze();
|
||
|
arrowFigure.Segments.Add(line);
|
||
|
|
||
|
line = new LineSegment(new Point(4.0, 4.0), false);
|
||
|
line.Freeze();
|
||
|
arrowFigure.Segments.Add(line);
|
||
|
}
|
||
|
|
||
|
arrowFigure.IsClosed = true;
|
||
|
arrowFigure.Freeze();
|
||
|
|
||
|
arrowGeometry.Figures.Add(arrowFigure);
|
||
|
arrowGeometry.Freeze();
|
||
|
|
||
|
CacheFreezable(arrowGeometry, ascending ? (int)AeroFreezables.ArrowUpGeometry : (int)AeroFreezables.ArrowDownGeometry);
|
||
|
}
|
||
|
|
||
|
// Draw two arrows, one inset in the other. This is to achieve a double gradient over both the border and the fill.
|
||
|
LinearGradientBrush arrowBorder = (LinearGradientBrush)GetCachedFreezable((int)AeroFreezables.ArrowBorder);
|
||
|
if (arrowBorder == null)
|
||
|
{
|
||
|
arrowBorder = new LinearGradientBrush();
|
||
|
arrowBorder.StartPoint = new Point();
|
||
|
arrowBorder.EndPoint = new Point(1.0, 1.0);
|
||
|
arrowBorder.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0x3C, 0x5E, 0x72), 0.0));
|
||
|
arrowBorder.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0x3C, 0x5E, 0x72), 0.1));
|
||
|
arrowBorder.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xC3, 0xE4, 0xF5), 1.0));
|
||
|
arrowBorder.Freeze();
|
||
|
CacheFreezable(arrowBorder, (int)AeroFreezables.ArrowBorder);
|
||
|
}
|
||
|
|
||
|
dc.DrawGeometry(arrowBorder, null, arrowGeometry);
|
||
|
|
||
|
LinearGradientBrush arrowFill = (LinearGradientBrush)GetCachedFreezable((int)AeroFreezables.ArrowFill);
|
||
|
if (arrowFill == null)
|
||
|
{
|
||
|
arrowFill = new LinearGradientBrush();
|
||
|
arrowFill.StartPoint = new Point();
|
||
|
arrowFill.EndPoint = new Point(1.0, 1.0);
|
||
|
arrowFill.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0x61, 0x96, 0xB6), 0.0));
|
||
|
arrowFill.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0x61, 0x96, 0xB6), 0.1));
|
||
|
arrowFill.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xCA, 0xE6, 0xF5), 1.0));
|
||
|
arrowFill.Freeze();
|
||
|
CacheFreezable(arrowFill, (int)AeroFreezables.ArrowFill);
|
||
|
}
|
||
|
|
||
|
// Inset the fill arrow inside the border arrow
|
||
|
ScaleTransform arrowScale = (ScaleTransform)GetCachedFreezable((int)AeroFreezables.ArrowFillScale);
|
||
|
if (arrowScale == null)
|
||
|
{
|
||
|
arrowScale = new ScaleTransform(0.75, 0.75, 3.5, 4.0);
|
||
|
arrowScale.Freeze();
|
||
|
CacheFreezable(arrowScale, (int)AeroFreezables.ArrowFillScale);
|
||
|
}
|
||
|
|
||
|
dc.PushTransform(arrowScale);
|
||
|
|
||
|
dc.DrawGeometry(arrowFill, null, arrowGeometry);
|
||
|
|
||
|
dc.Pop(); // Scale Transform
|
||
|
dc.Pop(); // Position Transform
|
||
|
}
|
||
|
|
||
|
if (horizontal)
|
||
|
{
|
||
|
dc.Pop(); // Horizontal Rotate
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private enum AeroFreezables : int
|
||
|
{
|
||
|
NormalBevel,
|
||
|
NormalBackground,
|
||
|
PressedBackground,
|
||
|
HoveredBackground,
|
||
|
SortedBackground,
|
||
|
PressedTop,
|
||
|
NormalSides,
|
||
|
PressedSides,
|
||
|
HoveredSides,
|
||
|
SortedSides,
|
||
|
PressedBevel,
|
||
|
NormalBottom,
|
||
|
PressedOrHoveredBottom,
|
||
|
SortedBottom,
|
||
|
ArrowBorder,
|
||
|
ArrowFill,
|
||
|
ArrowFillScale,
|
||
|
ArrowUpGeometry,
|
||
|
ArrowDownGeometry,
|
||
|
NumFreezables
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region Luna
|
||
|
|
||
|
private void RenderLuna(DrawingContext dc, Luna colorVariant)
|
||
|
{
|
||
|
Size size = RenderSize;
|
||
|
bool horizontal = Orientation == Orientation.Horizontal;
|
||
|
bool isClickable = IsClickable && IsEnabled;
|
||
|
bool isHovered = isClickable && IsHovered;
|
||
|
bool isPressed = isClickable && IsPressed;
|
||
|
ListSortDirection? sortDirection = SortDirection;
|
||
|
bool isSorted = sortDirection != null;
|
||
|
bool isSelected = IsSelected;
|
||
|
|
||
|
EnsureCache((int)LunaFreezables.NumFreezables);
|
||
|
|
||
|
if (horizontal)
|
||
|
{
|
||
|
// When horizontal, rotate the rendering by -90 degrees
|
||
|
Matrix m1 = new Matrix();
|
||
|
m1.RotateAt(-90.0, 0.0, 0.0);
|
||
|
Matrix m2 = new Matrix();
|
||
|
m2.Translate(0.0, size.Height);
|
||
|
|
||
|
MatrixTransform horizontalRotate = new MatrixTransform(m1 * m2);
|
||
|
horizontalRotate.Freeze();
|
||
|
dc.PushTransform(horizontalRotate);
|
||
|
|
||
|
double temp = size.Width;
|
||
|
size.Width = size.Height;
|
||
|
size.Height = temp;
|
||
|
}
|
||
|
|
||
|
// Draw the background
|
||
|
LunaFreezables backgroundType = isPressed ? LunaFreezables.PressedBackground : isHovered ? LunaFreezables.HoveredBackground : LunaFreezables.NormalBackground;
|
||
|
LinearGradientBrush background = (LinearGradientBrush)GetCachedFreezable((int)backgroundType);
|
||
|
if (background == null)
|
||
|
{
|
||
|
background = new LinearGradientBrush();
|
||
|
background.StartPoint = new Point();
|
||
|
background.EndPoint = new Point(0.0, 1.0);
|
||
|
|
||
|
if (isPressed)
|
||
|
{
|
||
|
if (colorVariant == Luna.Metallic)
|
||
|
{
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xB9, 0xB9, 0xC8), 0.0));
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xEC, 0xEC, 0xF3), 0.1));
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xEC, 0xEC, 0xF3), 1.0));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xC1, 0xC2, 0xB8), 0.0));
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xDE, 0xDF, 0xD8), 0.1));
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xDE, 0xDF, 0xD8), 1.0));
|
||
|
}
|
||
|
}
|
||
|
else if (isHovered || isSelected)
|
||
|
{
|
||
|
if (colorVariant == Luna.Metallic)
|
||
|
{
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xFE, 0xFE, 0xFE), 0.0));
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xFE, 0xFE, 0xFE), 0.85));
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xBD, 0xBE, 0xCE), 1.0));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xFA, 0xF9, 0xF4), 0.0));
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xFA, 0xF9, 0xF4), 0.85));
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xEC, 0xE9, 0xD8), 1.0));
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (colorVariant == Luna.Metallic)
|
||
|
{
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xF9, 0xFA, 0xFD), 0.0));
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xF9, 0xFA, 0xFD), 0.85));
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xBD, 0xBE, 0xCE), 1.0));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xEB, 0xEA, 0xDB), 0.0));
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xEB, 0xEA, 0xDB), 0.85));
|
||
|
background.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xCB, 0xC7, 0xB8), 1.0));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
background.Freeze();
|
||
|
CacheFreezable(background, (int)backgroundType);
|
||
|
}
|
||
|
|
||
|
dc.DrawRectangle(background, null, new Rect(0.0, 0.0, size.Width, size.Height));
|
||
|
|
||
|
if (isHovered && !isPressed && (size.Width >= 6.0) && (size.Height >= 4.0))
|
||
|
{
|
||
|
// When hovered, there is a colored tab at the bottom
|
||
|
TranslateTransform positionTransform = new TranslateTransform(0.0, size.Height - 3.0);
|
||
|
positionTransform.Freeze();
|
||
|
dc.PushTransform(positionTransform);
|
||
|
|
||
|
PathGeometry tabGeometry = new PathGeometry();
|
||
|
PathFigure tabFigure = new PathFigure();
|
||
|
|
||
|
tabFigure.StartPoint = new Point(0.5, 0.5);
|
||
|
|
||
|
LineSegment line = new LineSegment(new Point(size.Width - 0.5, 0.5), true);
|
||
|
line.Freeze();
|
||
|
tabFigure.Segments.Add(line);
|
||
|
|
||
|
ArcSegment arc = new ArcSegment(new Point(size.Width - 2.5, 2.5), new Size(2.0, 2.0), 90.0, false, SweepDirection.Clockwise, true);
|
||
|
arc.Freeze();
|
||
|
tabFigure.Segments.Add(arc);
|
||
|
|
||
|
line = new LineSegment(new Point(2.5, 2.5), true);
|
||
|
line.Freeze();
|
||
|
tabFigure.Segments.Add(line);
|
||
|
|
||
|
arc = new ArcSegment(new Point(0.5, 0.5), new Size(2.0, 2.0), 90.0, false, SweepDirection.Clockwise, true);
|
||
|
arc.Freeze();
|
||
|
tabFigure.Segments.Add(arc);
|
||
|
|
||
|
tabFigure.IsClosed = true;
|
||
|
tabFigure.Freeze();
|
||
|
|
||
|
tabGeometry.Figures.Add(tabFigure);
|
||
|
tabGeometry.Freeze();
|
||
|
|
||
|
Pen tabStroke = (Pen)GetCachedFreezable((int)LunaFreezables.TabStroke);
|
||
|
if (tabStroke == null)
|
||
|
{
|
||
|
SolidColorBrush tabStrokeBrush = new SolidColorBrush((colorVariant == Luna.HomeStead) ? Color.FromArgb(0xFF, 0xCF, 0x72, 0x25) : Color.FromArgb(0xFF, 0xF8, 0xA9, 0x00));
|
||
|
tabStrokeBrush.Freeze();
|
||
|
|
||
|
tabStroke = new Pen(tabStrokeBrush, 1.0);
|
||
|
tabStroke.Freeze();
|
||
|
|
||
|
CacheFreezable(tabStroke, (int)LunaFreezables.TabStroke);
|
||
|
}
|
||
|
|
||
|
LinearGradientBrush tabFill = (LinearGradientBrush)GetCachedFreezable((int)LunaFreezables.TabFill);
|
||
|
if (tabFill == null)
|
||
|
{
|
||
|
tabFill = new LinearGradientBrush();
|
||
|
tabFill.StartPoint = new Point();
|
||
|
tabFill.EndPoint = new Point(1.0, 0.0);
|
||
|
if (colorVariant == Luna.HomeStead)
|
||
|
{
|
||
|
tabFill.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xE3, 0x91, 0x4F), 0.0));
|
||
|
tabFill.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xE3, 0x91, 0x4F), 1.0));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
tabFill.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xFC, 0xE0, 0xA6), 0.0));
|
||
|
tabFill.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xF6, 0xC4, 0x56), 0.1));
|
||
|
tabFill.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xF6, 0xC4, 0x56), 0.9));
|
||
|
tabFill.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, 0xDF, 0x97, 0x00), 1.0));
|
||
|
}
|
||
|
|
||
|
tabFill.Freeze();
|
||
|
CacheFreezable(tabFill, (int)LunaFreezables.TabFill);
|
||
|
}
|
||
|
|
||
|
dc.DrawGeometry(tabFill, tabStroke, tabGeometry);
|
||
|
|
||
|
dc.Pop(); // Translate Transform
|
||
|
}
|
||
|
|
||
|
if (isPressed && (size.Width >= 2.0) && (size.Height >= 2.0))
|
||
|
{
|
||
|
// When pressed, there is a border on the left and bottom
|
||
|
SolidColorBrush border = (SolidColorBrush)GetCachedFreezable((int)LunaFreezables.PressedBorder);
|
||
|
if (border == null)
|
||
|
{
|
||
|
border = new SolidColorBrush((colorVariant == Luna.Metallic) ? Color.FromArgb(0xFF, 0x80, 0x80, 0x99) : Color.FromArgb(0xFF, 0xA5, 0xA5, 0x97));
|
||
|
border.Freeze();
|
||
|
CacheFreezable(border, (int)LunaFreezables.PressedBorder);
|
||
|
}
|
||
|
|
||
|
dc.DrawRectangle(border, null, new Rect(0.0, 0.0, 1.0, size.Height));
|
||
|
dc.DrawRectangle(border, null, new Rect(0.0, Max0(size.Height - 1.0), size.Width, 1.0));
|
||
|
}
|
||
|
|
||
|
if (!isPressed && !isHovered && (size.Width >= 4.0))
|
||
|
{
|
||
|
if (SeparatorVisibility == Visibility.Visible)
|
||
|
{
|
||
|
Brush sideBrush;
|
||
|
if (SeparatorBrush != null)
|
||
|
{
|
||
|
sideBrush = SeparatorBrush;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// When not pressed or hovered, draw the resize gripper
|
||
|
LinearGradientBrush gripper = (LinearGradientBrush)GetCachedFreezable((int)(horizontal ? LunaFreezables.HorizontalGripper : LunaFreezables.VerticalGripper));
|
||
|
if (gripper == null)
|
||
|
{
|
||
|
gripper = new LinearGradientBrush();
|
||
|
gripper.StartPoint = new Point();
|
||
|
gripper.EndPoint = new Point(1.0, 0.0);
|
||
|
|
||
|
Color highlight = Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF);
|
||
|
Color shadow = Color.FromArgb(0xFF, 0xC7, 0xC5, 0xB2);
|
||
|
|
||
|
if (horizontal)
|
||
|
{
|
||
|
gripper.GradientStops.Add(new GradientStop(highlight, 0.0));
|
||
|
gripper.GradientStops.Add(new GradientStop(highlight, 0.25));
|
||
|
gripper.GradientStops.Add(new GradientStop(shadow, 0.75));
|
||
|
gripper.GradientStops.Add(new GradientStop(shadow, 1.0));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
gripper.GradientStops.Add(new GradientStop(shadow, 0.0));
|
||
|
gripper.GradientStops.Add(new GradientStop(shadow, 0.25));
|
||
|
gripper.GradientStops.Add(new GradientStop(highlight, 0.75));
|
||
|
gripper.GradientStops.Add(new GradientStop(highlight, 1.0));
|
||
|
}
|
||
|
|
||
|
gripper.Freeze();
|
||
|
CacheFreezable(gripper, (int)(horizontal ? LunaFreezables.HorizontalGripper : LunaFreezables.VerticalGripper));
|
||
|
}
|
||
|
|
||
|
sideBrush = gripper;
|
||
|
}
|
||
|
|
||
|
dc.DrawRectangle(sideBrush, null, new Rect(horizontal ? 0.0 : Max0(size.Width - 2.0), 4.0, 2.0, Max0(size.Height - 8.0)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (isSorted && (size.Width > 14.0) && (size.Height > 10.0))
|
||
|
{
|
||
|
// When sorted, draw an arrow on the right
|
||
|
TranslateTransform positionTransform = new TranslateTransform(size.Width - 15.0, (size.Height - 5.0) * 0.5);
|
||
|
positionTransform.Freeze();
|
||
|
dc.PushTransform(positionTransform);
|
||
|
|
||
|
bool ascending = (sortDirection == ListSortDirection.Ascending);
|
||
|
PathGeometry arrowGeometry = (PathGeometry)GetCachedFreezable(ascending ? (int)LunaFreezables.ArrowUpGeometry : (int)LunaFreezables.ArrowDownGeometry);
|
||
|
if (arrowGeometry == null)
|
||
|
{
|
||
|
arrowGeometry = new PathGeometry();
|
||
|
PathFigure arrowFigure = new PathFigure();
|
||
|
|
||
|
if (ascending)
|
||
|
{
|
||
|
arrowFigure.StartPoint = new Point(0.0, 5.0);
|
||
|
|
||
|
LineSegment line = new LineSegment(new Point(5.0, 0.0), false);
|
||
|
line.Freeze();
|
||
|
arrowFigure.Segments.Add(line);
|
||
|
|
||
|
line = new LineSegment(new Point(10.0, 5.0), false);
|
||
|
line.Freeze();
|
||
|
arrowFigure.Segments.Add(line);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
arrowFigure.StartPoint = new Point(0.0, 0.0);
|
||
|
|
||
|
LineSegment line = new LineSegment(new Point(10.0, 0.0), false);
|
||
|
line.Freeze();
|
||
|
arrowFigure.Segments.Add(line);
|
||
|
|
||
|
line = new LineSegment(new Point(5.0, 5.0), false);
|
||
|
line.Freeze();
|
||
|
arrowFigure.Segments.Add(line);
|
||
|
}
|
||
|
|
||
|
arrowFigure.IsClosed = true;
|
||
|
arrowFigure.Freeze();
|
||
|
|
||
|
arrowGeometry.Figures.Add(arrowFigure);
|
||
|
arrowGeometry.Freeze();
|
||
|
|
||
|
CacheFreezable(arrowGeometry, ascending ? (int)LunaFreezables.ArrowUpGeometry : (int)LunaFreezables.ArrowDownGeometry);
|
||
|
}
|
||
|
|
||
|
SolidColorBrush arrowFill = (SolidColorBrush)GetCachedFreezable((int)LunaFreezables.ArrowFill);
|
||
|
if (arrowFill == null)
|
||
|
{
|
||
|
arrowFill = new SolidColorBrush(Color.FromArgb(0xFF, 0xAC, 0xA8, 0x99));
|
||
|
arrowFill.Freeze();
|
||
|
CacheFreezable(arrowFill, (int)LunaFreezables.ArrowFill);
|
||
|
}
|
||
|
|
||
|
dc.DrawGeometry(arrowFill, null, arrowGeometry);
|
||
|
|
||
|
dc.Pop(); // Position Transform
|
||
|
}
|
||
|
|
||
|
if (horizontal)
|
||
|
{
|
||
|
dc.Pop(); // Horizontal Rotate
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private enum Luna
|
||
|
{
|
||
|
NormalColor,
|
||
|
HomeStead,
|
||
|
Metallic,
|
||
|
}
|
||
|
|
||
|
private enum LunaFreezables
|
||
|
{
|
||
|
NormalBackground,
|
||
|
HoveredBackground,
|
||
|
PressedBackground,
|
||
|
HorizontalGripper,
|
||
|
VerticalGripper,
|
||
|
PressedBorder,
|
||
|
TabGeometry,
|
||
|
TabStroke,
|
||
|
TabFill,
|
||
|
ArrowFill,
|
||
|
ArrowUpGeometry,
|
||
|
ArrowDownGeometry,
|
||
|
NumFreezables
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region Classic
|
||
|
|
||
|
private static readonly DependencyProperty ControlBrushProperty =
|
||
|
DependencyProperty.Register("ControlBrush", typeof(Brush), typeof(EGC.DataGridHeaderBorder), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
|
||
|
|
||
|
private Brush EnsureControlBrush()
|
||
|
{
|
||
|
if (ReadLocalValue(ControlBrushProperty) == DependencyProperty.UnsetValue)
|
||
|
{
|
||
|
SetResourceReference(ControlBrushProperty, SystemColors.ControlBrushKey);
|
||
|
}
|
||
|
|
||
|
return (Brush)GetValue(ControlBrushProperty);
|
||
|
}
|
||
|
|
||
|
private void RenderClassic(DrawingContext dc)
|
||
|
{
|
||
|
Size size = RenderSize;
|
||
|
bool isClickable = IsClickable && IsEnabled;
|
||
|
bool isPressed = isClickable && IsPressed;
|
||
|
ListSortDirection? sortDirection = SortDirection;
|
||
|
bool isSorted = sortDirection != null;
|
||
|
bool horizontal = Orientation == Orientation.Horizontal;
|
||
|
Brush background = EnsureControlBrush();
|
||
|
Brush light = SystemColors.ControlLightBrush;
|
||
|
Brush dark = SystemColors.ControlDarkBrush;
|
||
|
bool shouldDrawRight = true;
|
||
|
bool shouldDrawBottom = true;
|
||
|
bool usingSeparatorBrush = false;
|
||
|
|
||
|
Brush darkDarkRight = null;
|
||
|
if (!horizontal)
|
||
|
{
|
||
|
if (SeparatorVisibility == Visibility.Visible && SeparatorBrush != null)
|
||
|
{
|
||
|
darkDarkRight = SeparatorBrush;
|
||
|
usingSeparatorBrush = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
shouldDrawRight = false;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
darkDarkRight = SystemColors.ControlDarkDarkBrush;
|
||
|
}
|
||
|
|
||
|
Brush darkDarkBottom = null;
|
||
|
if (horizontal)
|
||
|
{
|
||
|
if (SeparatorVisibility == Visibility.Visible && SeparatorBrush != null)
|
||
|
{
|
||
|
darkDarkBottom = SeparatorBrush;
|
||
|
usingSeparatorBrush = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
shouldDrawBottom = false;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
darkDarkBottom = SystemColors.ControlDarkDarkBrush;
|
||
|
}
|
||
|
|
||
|
EnsureCache((int)ClassicFreezables.NumFreezables);
|
||
|
|
||
|
// Draw the background
|
||
|
dc.DrawRectangle(background, null, new Rect(0.0, 0.0, size.Width, size.Height));
|
||
|
|
||
|
if ((size.Width > 3.0) && (size.Height > 3.0))
|
||
|
{
|
||
|
// Draw the border
|
||
|
if (isPressed)
|
||
|
{
|
||
|
dc.DrawRectangle(dark, null, new Rect(0.0, 0.0, size.Width, 1.0));
|
||
|
dc.DrawRectangle(dark, null, new Rect(0.0, 0.0, 1.0, size.Height));
|
||
|
dc.DrawRectangle(dark, null, new Rect(0.0, Max0(size.Height - 1.0), size.Width, 1.0));
|
||
|
dc.DrawRectangle(dark, null, new Rect(Max0(size.Width - 1.0), 0.0, 1.0, size.Height));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
dc.DrawRectangle(light, null, new Rect(0.0, 0.0, 1.0, Max0(size.Height - 1.0)));
|
||
|
dc.DrawRectangle(light, null, new Rect(0.0, 0.0, Max0(size.Width - 1.0), 1.0));
|
||
|
|
||
|
if (shouldDrawRight)
|
||
|
{
|
||
|
if (!usingSeparatorBrush)
|
||
|
{
|
||
|
dc.DrawRectangle(dark, null, new Rect(Max0(size.Width - 2.0), 1.0, 1.0, Max0(size.Height - 2.0)));
|
||
|
}
|
||
|
|
||
|
dc.DrawRectangle(darkDarkRight, null, new Rect(Max0(size.Width - 1.0), 0.0, 1.0, size.Height));
|
||
|
}
|
||
|
|
||
|
if (shouldDrawBottom)
|
||
|
{
|
||
|
if (!usingSeparatorBrush)
|
||
|
{
|
||
|
dc.DrawRectangle(dark, null, new Rect(1.0, Max0(size.Height - 2.0), Max0(size.Width - 2.0), 1.0));
|
||
|
}
|
||
|
|
||
|
dc.DrawRectangle(darkDarkBottom, null, new Rect(0.0, Max0(size.Height - 1.0), size.Width, 1.0));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (isSorted && (size.Width > 14.0) && (size.Height > 10.0))
|
||
|
{
|
||
|
// If sorted, draw an arrow on the right
|
||
|
TranslateTransform positionTransform = new TranslateTransform(size.Width - 15.0, (size.Height - 5.0) * 0.5);
|
||
|
positionTransform.Freeze();
|
||
|
dc.PushTransform(positionTransform);
|
||
|
|
||
|
bool ascending = (sortDirection == ListSortDirection.Ascending);
|
||
|
PathGeometry arrowGeometry = (PathGeometry)GetCachedFreezable(ascending ? (int)ClassicFreezables.ArrowUpGeometry : (int)ClassicFreezables.ArrowDownGeometry);
|
||
|
if (arrowGeometry == null)
|
||
|
{
|
||
|
arrowGeometry = new PathGeometry();
|
||
|
PathFigure arrowFigure = new PathFigure();
|
||
|
|
||
|
if (ascending)
|
||
|
{
|
||
|
arrowFigure.StartPoint = new Point(0.0, 5.0);
|
||
|
|
||
|
LineSegment line = new LineSegment(new Point(5.0, 0.0), false);
|
||
|
line.Freeze();
|
||
|
arrowFigure.Segments.Add(line);
|
||
|
|
||
|
line = new LineSegment(new Point(10.0, 5.0), false);
|
||
|
line.Freeze();
|
||
|
arrowFigure.Segments.Add(line);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
arrowFigure.StartPoint = new Point(0.0, 0.0);
|
||
|
|
||
|
LineSegment line = new LineSegment(new Point(10.0, 0.0), false);
|
||
|
line.Freeze();
|
||
|
arrowFigure.Segments.Add(line);
|
||
|
|
||
|
line = new LineSegment(new Point(5.0, 5.0), false);
|
||
|
line.Freeze();
|
||
|
arrowFigure.Segments.Add(line);
|
||
|
}
|
||
|
|
||
|
arrowFigure.IsClosed = true;
|
||
|
arrowFigure.Freeze();
|
||
|
|
||
|
arrowGeometry.Figures.Add(arrowFigure);
|
||
|
arrowGeometry.Freeze();
|
||
|
|
||
|
CacheFreezable(arrowGeometry, ascending ? (int)ClassicFreezables.ArrowUpGeometry : (int)ClassicFreezables.ArrowDownGeometry);
|
||
|
}
|
||
|
|
||
|
dc.DrawGeometry(SystemColors.GrayTextBrush, null, arrowGeometry);
|
||
|
|
||
|
dc.Pop(); // Position Transform
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private enum ClassicFreezables
|
||
|
{
|
||
|
ArrowUpGeometry,
|
||
|
ArrowDownGeometry,
|
||
|
NumFreezables
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region Freezable Cache
|
||
|
|
||
|
/// <summary>
|
||
|
/// Creates a cache of frozen Freezable resources for use
|
||
|
/// across all instances of the border.
|
||
|
/// </summary>
|
||
|
private static void EnsureCache(int size)
|
||
|
{
|
||
|
// Quick check to avoid locking
|
||
|
if (_freezableCache == null)
|
||
|
{
|
||
|
lock (_cacheAccess)
|
||
|
{
|
||
|
// Re-check in case another thread created the cache
|
||
|
if (_freezableCache == null)
|
||
|
{
|
||
|
_freezableCache = new List<Freezable>(size);
|
||
|
for (int i = 0; i < size; i++)
|
||
|
{
|
||
|
_freezableCache.Add(null);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Debug.Assert(_freezableCache.Count == size, "The cache size does not match the requested amount.");
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Releases all resources in the cache.
|
||
|
/// </summary>
|
||
|
private static void ReleaseCache()
|
||
|
{
|
||
|
// Avoid locking if necessary
|
||
|
if (_freezableCache != null)
|
||
|
{
|
||
|
lock (_cacheAccess)
|
||
|
{
|
||
|
// No need to re-check if non-null since it's OK to set it to null multiple times
|
||
|
_freezableCache = null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Retrieves a cached resource.
|
||
|
/// </summary>
|
||
|
private static Freezable GetCachedFreezable(int index)
|
||
|
{
|
||
|
lock (_cacheAccess)
|
||
|
{
|
||
|
Freezable freezable = _freezableCache[index];
|
||
|
Debug.Assert((freezable == null) || freezable.IsFrozen, "Cached Freezables should have been frozen.");
|
||
|
return freezable;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Caches a resources.
|
||
|
/// </summary>
|
||
|
private static void CacheFreezable(Freezable freezable, int index)
|
||
|
{
|
||
|
Debug.Assert(freezable.IsFrozen, "Cached Freezables should be frozen.");
|
||
|
|
||
|
lock (_cacheAccess)
|
||
|
{
|
||
|
if (_freezableCache[index] != null)
|
||
|
{
|
||
|
_freezableCache[index] = freezable;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static List<Freezable> _freezableCache;
|
||
|
private static object _cacheAccess = new object();
|
||
|
|
||
|
#endregion
|
||
|
}
|
||
|
}
|