|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#if RIBBON_IN_FRAMEWORK
namespace System.Windows.Shell
#else
namespace Microsoft.Windows.Shell
#endif
{
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Windows;
using System.Windows.Data;
using Standard;
public enum ResizeGripDirection
{
None,
TopLeft,
Top,
TopRight,
Right,
BottomRight,
Bottom,
BottomLeft,
Left,
}
[Flags]
public enum NonClientFrameEdges
{
None = 0,
Left = 1,
Top = 2,
Right = 4,
Bottom = 8,
}
public class WindowChrome : Freezable
{
private struct _SystemParameterBoundProperty
{
public string SystemParameterPropertyName { get; set; }
public DependencyProperty DependencyProperty { get; set; }
}
// Named property available for fully extending the glass frame.
public static Thickness GlassFrameCompleteThickness { get { return new Thickness(-1); } }
#region Attached Properties
public static readonly DependencyProperty WindowChromeProperty = DependencyProperty.RegisterAttached(
"WindowChrome",
typeof(WindowChrome),
typeof(WindowChrome),
new PropertyMetadata(null, _OnChromeChanged));
private static void _OnChromeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// The different design tools handle drawing outside their custom window objects differently.
// Rather than try to support this concept in the design surface let the designer draw its own
// chrome anyways.
// There's certainly room for improvement here.
if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(d))
{
return;
}
var window = (Window)d;
var newChrome = (WindowChrome)e.NewValue;
Assert.IsNotNull(window);
// Update the ChromeWorker with this new object.
// If there isn't currently a worker associated with the Window then assign a new one.
// There can be a many:1 relationship of to Window to WindowChrome objects, but a 1:1 for a Window and a WindowChromeWorker.
WindowChromeWorker chromeWorker = WindowChromeWorker.GetWindowChromeWorker(window);
if (chromeWorker == null)
{
chromeWorker = new WindowChromeWorker();
WindowChromeWorker.SetWindowChromeWorker(window, chromeWorker);
}
chromeWorker.SetWindowChrome(newChrome);
}
[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0")]
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
public static WindowChrome GetWindowChrome(Window window)
{
Verify.IsNotNull(window, "window");
return (WindowChrome)window.GetValue(WindowChromeProperty);
}
[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0")]
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
public static void SetWindowChrome(Window window, WindowChrome chrome)
{
Verify.IsNotNull(window, "window");
window.SetValue(WindowChromeProperty, chrome);
}
public static readonly DependencyProperty IsHitTestVisibleInChromeProperty = DependencyProperty.RegisterAttached(
"IsHitTestVisibleInChrome",
typeof(bool),
typeof(WindowChrome),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits));
[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0")]
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
public static bool GetIsHitTestVisibleInChrome(IInputElement inputElement)
{
Verify.IsNotNull(inputElement, "inputElement");
var dobj = inputElement as DependencyObject;
if (dobj == null)
{
throw new ArgumentException("The element must be a DependencyObject", "inputElement");
}
return (bool)dobj.GetValue(IsHitTestVisibleInChromeProperty);
}
[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0")]
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
public static void SetIsHitTestVisibleInChrome(IInputElement inputElement, bool hitTestVisible)
{
Verify.IsNotNull(inputElement, "inputElement");
var dobj = inputElement as DependencyObject;
if (dobj == null)
{
throw new ArgumentException("The element must be a DependencyObject", "inputElement");
}
dobj.SetValue(IsHitTestVisibleInChromeProperty, hitTestVisible);
}
public static readonly DependencyProperty ResizeGripDirectionProperty = DependencyProperty.RegisterAttached(
"ResizeGripDirection",
typeof(ResizeGripDirection),
typeof(WindowChrome),
new FrameworkPropertyMetadata(ResizeGripDirection.None, FrameworkPropertyMetadataOptions.Inherits));
[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0")]
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
public static ResizeGripDirection GetResizeGripDirection(IInputElement inputElement)
{
Verify.IsNotNull(inputElement, "inputElement");
var dobj = inputElement as DependencyObject;
if (dobj == null)
{
throw new ArgumentException("The element must be a DependencyObject", "inputElement");
}
return (ResizeGripDirection)dobj.GetValue(ResizeGripDirectionProperty);
}
[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0")]
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
public static void SetResizeGripDirection(IInputElement inputElement, ResizeGripDirection direction)
{
Verify.IsNotNull(inputElement, "inputElement");
var dobj = inputElement as DependencyObject;
if (dobj == null)
{
throw new ArgumentException("The element must be a DependencyObject", "inputElement");
}
dobj.SetValue(ResizeGripDirectionProperty, direction);
}
#endregion
#region Dependency Properties
public static readonly DependencyProperty CaptionHeightProperty = DependencyProperty.Register(
"CaptionHeight",
typeof(double),
typeof(WindowChrome),
new PropertyMetadata(
0d,
(d, e) => ((WindowChrome)d)._OnPropertyChangedThatRequiresRepaint()),
value => (double)value >= 0d);
/// <summary>The extent of the top of the window to treat as the caption.</summary>
public double CaptionHeight
{
get { return (double)GetValue(CaptionHeightProperty); }
set { SetValue(CaptionHeightProperty, value); }
}
public static readonly DependencyProperty ResizeBorderThicknessProperty = DependencyProperty.Register(
"ResizeBorderThickness",
typeof(Thickness),
typeof(WindowChrome),
new PropertyMetadata(default(Thickness)),
(value) => Utility.IsThicknessNonNegative((Thickness)value));
public Thickness ResizeBorderThickness
{
get { return (Thickness)GetValue(ResizeBorderThicknessProperty); }
set { SetValue(ResizeBorderThicknessProperty, value); }
}
public static readonly DependencyProperty GlassFrameThicknessProperty = DependencyProperty.Register(
"GlassFrameThickness",
typeof(Thickness),
typeof(WindowChrome),
new PropertyMetadata(
default(Thickness),
(d, e) => ((WindowChrome)d)._OnPropertyChangedThatRequiresRepaint(),
(d, o) => _CoerceGlassFrameThickness((Thickness)o)));
private static object _CoerceGlassFrameThickness(Thickness thickness)
{
// If it's explicitly set, but set to a thickness with at least one negative side then
// coerce the value to the stock GlassFrameCompleteThickness.
if (!Utility.IsThicknessNonNegative(thickness))
{
return GlassFrameCompleteThickness;
}
return thickness;
}
public Thickness GlassFrameThickness
{
get { return (Thickness)GetValue(GlassFrameThicknessProperty); }
set { SetValue(GlassFrameThicknessProperty, value); }
}
public static readonly DependencyProperty UseAeroCaptionButtonsProperty = DependencyProperty.Register(
"UseAeroCaptionButtons",
typeof(bool),
typeof(WindowChrome),
new FrameworkPropertyMetadata(true));
public bool UseAeroCaptionButtons
{
get { return (bool)GetValue(UseAeroCaptionButtonsProperty); }
set { SetValue(UseAeroCaptionButtonsProperty, value); }
}
public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register(
"CornerRadius",
typeof(CornerRadius),
typeof(WindowChrome),
new PropertyMetadata(
default(CornerRadius),
(d, e) => ((WindowChrome)d)._OnPropertyChangedThatRequiresRepaint()),
(value) => Utility.IsCornerRadiusValid((CornerRadius)value));
public CornerRadius CornerRadius
{
get { return (CornerRadius)GetValue(CornerRadiusProperty); }
set { SetValue(CornerRadiusProperty, value); }
}
public static readonly DependencyProperty NonClientFrameEdgesProperty = DependencyProperty.Register(
"NonClientFrameEdges",
typeof(NonClientFrameEdges),
typeof(WindowChrome),
new PropertyMetadata(
NonClientFrameEdges.None,
(d, e) => ((WindowChrome)d)._OnPropertyChangedThatRequiresRepaint()),
_NonClientFrameEdgesAreValid);
private static readonly NonClientFrameEdges NonClientFrameEdges_All = NonClientFrameEdges.Left | NonClientFrameEdges.Top | NonClientFrameEdges.Right | NonClientFrameEdges.Bottom;
private static bool _NonClientFrameEdgesAreValid(object value)
{
NonClientFrameEdges ncEdges = NonClientFrameEdges.None;
try
{
ncEdges = (NonClientFrameEdges)value;
}
catch (InvalidCastException)
{
return false;
}
if (ncEdges == NonClientFrameEdges.None)
{
return true;
}
// Does this only contain valid bits?
if ((ncEdges | NonClientFrameEdges_All) != NonClientFrameEdges_All)
{
return false;
}
// It can't sacrifice all 4 edges. Weird things happen.
if (ncEdges == NonClientFrameEdges_All)
{
return false;
}
return true;
}
public NonClientFrameEdges NonClientFrameEdges
{
get { return (NonClientFrameEdges)GetValue(NonClientFrameEdgesProperty); }
set { SetValue(NonClientFrameEdgesProperty, value); }
}
#endregion
protected override Freezable CreateInstanceCore()
{
return new WindowChrome();
}
private static readonly List<_SystemParameterBoundProperty> _BoundProperties = new List<_SystemParameterBoundProperty>
{
new _SystemParameterBoundProperty { DependencyProperty = CornerRadiusProperty, SystemParameterPropertyName = "WindowCornerRadius" },
new _SystemParameterBoundProperty { DependencyProperty = CaptionHeightProperty, SystemParameterPropertyName = "WindowCaptionHeight" },
new _SystemParameterBoundProperty { DependencyProperty = ResizeBorderThicknessProperty, SystemParameterPropertyName = "WindowResizeBorderThickness" },
new _SystemParameterBoundProperty { DependencyProperty = GlassFrameThicknessProperty, SystemParameterPropertyName = "WindowNonClientFrameThickness" },
};
public WindowChrome()
{
// Effective default values for some of these properties are set to be bindings
// that set them to system defaults.
// A more correct way to do this would be to Coerce the value iff the source of the DP was the default value.
// Unfortunately with the current property system we can't detect whether the value being applied at the time
// of the coersion is the default.
foreach (var bp in _BoundProperties)
{
// This list must be declared after the DP's are assigned.
Assert.IsNotNull(bp.DependencyProperty);
BindingOperations.SetBinding(
this,
bp.DependencyProperty,
new Binding
{
#if RIBBON_IN_FRAMEWORK
Path = new PropertyPath($"(SystemParameters.{bp.SystemParameterPropertyName})"),
#else
Source = SystemParameters2.Current,
Path = new PropertyPath(bp.SystemParameterPropertyName),
#endif
Mode = BindingMode.OneWay,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
});
}
}
private void _OnPropertyChangedThatRequiresRepaint()
{
var handler = PropertyChangedThatRequiresRepaint;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
internal event EventHandler PropertyChangedThatRequiresRepaint;
}
}
|