This repository has been archived on 2024-01-02. You can view files and clone it, but cannot push or open issues or pull requests.
Sic06/FrameworkLocal/UIClient/DataGridTransform/DataGrid/Microsoft/Windows/Controls/DataGridHeaderBorder.cs

1360 lines
57 KiB
C#
Raw Normal View History

2023-01-13 10:57:37 +08:00
//---------------------------------------------------------------------------
//
// 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
}
}