Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Framework / System / Windows / Controls / GridViewColumnHeader.cs / 1 / GridViewColumnHeader.cs
//----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//---------------------------------------------------------------------------
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices; // SafeHandle
using System.Security; // [SecurityCritical]
using System.Windows.Automation.Peers; // AutomationPeer
using System.Windows.Controls.Primitives; // ButtonBase
using System.Windows.Input; // MouseButtonEventArgs
using System.Windows.Media; // VisualBrush
using MS.Internal; // DoubleUtil
using MS.Internal.KnownBoxes; // BooleanBoxes
using MS.Win32; // SafeNativeMethods
namespace System.Windows.Controls
{
///
/// Defines the different roles of GridViewColumnHeaders
///
public enum GridViewColumnHeaderRole
{
///
/// The normal header
///
Normal,
///
/// The floating header (when dragging a header)
///
Floating,
///
/// The padding header (the very last header in header bar)
///
Padding
}
///
/// column header of GridView
///
#if OLD_AUTOMATION
[Automation(AccessibilityControlType = "Button")]
#endif
[TemplatePart(Name = "PART_HeaderGripper", Type = typeof(Thumb))]
[TemplatePart(Name = "PART_FloatingHeaderCanvas", Type = typeof(Canvas))]
public class GridViewColumnHeader : ButtonBase
#if OLD_AUTOMATION
, IInvokeProvider
#endif
{
//-------------------------------------------------------------------
//
// Constructors
//
//-------------------------------------------------------------------
#region Constructor
static GridViewColumnHeader()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(GridViewColumnHeader), new FrameworkPropertyMetadata(typeof(GridViewColumnHeader)));
_dType = DependencyObjectType.FromSystemTypeInternal(typeof(GridViewColumnHeader));
FocusableProperty.OverrideMetadata(typeof(GridViewColumnHeader), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox));
// hookup property change event.
StyleProperty.OverrideMetadata(typeof(GridViewColumnHeader), new FrameworkPropertyMetadata(new PropertyChangedCallback(PropertyChanged)));
ContentTemplateProperty.OverrideMetadata(typeof(GridViewColumnHeader), new FrameworkPropertyMetadata(new PropertyChangedCallback(PropertyChanged)));
ContentTemplateSelectorProperty.OverrideMetadata(typeof(GridViewColumnHeader), new FrameworkPropertyMetadata(new PropertyChangedCallback(PropertyChanged)));
ContextMenuProperty.OverrideMetadata(typeof(GridViewColumnHeader), new FrameworkPropertyMetadata(new PropertyChangedCallback(PropertyChanged)));
ToolTipProperty.OverrideMetadata(typeof(GridViewColumnHeader), new FrameworkPropertyMetadata(new PropertyChangedCallback(PropertyChanged)));
}
#endregion
//--------------------------------------------------------------------
//
// Public Methods
//
//-------------------------------------------------------------------
#region Public Methods
///
/// Called when the Template's tree has been generated
///
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
GridViewColumnHeaderRole role = Role;
if (role == GridViewColumnHeaderRole.Normal)
{
HookupGripperEvents();
}
else if (role == GridViewColumnHeaderRole.Floating)
{
// if this is a floating header, try to find the FloatingHeaderCanvas,
// and copy source header's visual to it
_floatingHeaderCanvas = GetTemplateChild(FloatingHeaderCanvasTemplateName) as Canvas;
UpdateFloatingHeaderCanvas();
}
}
#endregion
//--------------------------------------------------------------------
//
// Public Properties
//
//--------------------------------------------------------------------
#region Public Properties
///
/// The key for Column (read-only property)
///
internal static readonly DependencyPropertyKey ColumnPropertyKey =
DependencyProperty.RegisterReadOnly(
"Column",
typeof(GridViewColumn),
typeof(GridViewColumnHeader),
null);
///
/// The DependencyProperty for the Column property.
///
public static readonly DependencyProperty ColumnProperty =
ColumnPropertyKey.DependencyProperty;
///
/// Column associated with this header
///
public GridViewColumn Column
{
get { return (GridViewColumn)GetValue(ColumnProperty); }
}
///
/// The key for Role (read-only property)
///
internal static readonly DependencyPropertyKey RolePropertyKey =
DependencyProperty.RegisterReadOnly(
"Role",
typeof(GridViewColumnHeaderRole),
typeof(GridViewColumnHeader),
new FrameworkPropertyMetadata(GridViewColumnHeaderRole.Normal));
///
/// The DependencyProperty for the Role property.
///
public static readonly DependencyProperty RoleProperty =
RolePropertyKey.DependencyProperty;
///
/// What the role of the header is: Normal, Floating, Padding.
///
[Category("Behavior")]
public GridViewColumnHeaderRole Role
{
get { return (GridViewColumnHeaderRole)GetValue(RoleProperty); }
}
#endregion Public Properties
#if OLD_AUTOMATION
//-------------------------------------------------------------------
//
// IInvodeProvider
//
//--------------------------------------------------------------------
void IInvokeProvider.Invoke()
{
IsAccessKeyOrAutomation = true;
OnClick();
}
#endif
//-------------------------------------------------------------------
//
// Protected Methods
//
//-------------------------------------------------------------------
#region Protected Methods
///
/// This is the method that responds to the MouseButtonEvent event.
///
/// Event arguments
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonUp(e);
// give parent a chance to handle MouseButtonEvent (for GridViewHeaderRowPresenter by default)
e.Handled = false;
if (ClickMode == ClickMode.Hover && IsMouseCaptured)
{
ReleaseMouseCapture();
}
}
///
/// This is the method that responds to the MouseButtonEvent event.
///
/// Event arguments
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
// give parent a chance to handle MouseButtonEvent (for GridViewHeaderRowPresenter by default)
e.Handled = false;
//If ClickMode is Hover, we must capture mouse in order to let column reorder work correctly (Bug#1496673)
if (ClickMode == ClickMode.Hover && e.ButtonState == MouseButtonState.Pressed)
{
CaptureMouse();
}
}
///
/// This is the method that responds to the MouseMoveEvent event.
///
/// Event arguments
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
// Override base method: if left mouse is pressed, always set IsPressed as true
if ((ClickMode != ClickMode.Hover) &&
(IsMouseCaptured && (Mouse.PrimaryDevice.LeftButton == MouseButtonState.Pressed)))
{
SetValue(ButtonBase.IsPressedPropertyKey, BooleanBoxes.TrueBox);
}
e.Handled = false;
}
///
/// Override for
///
protected internal override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
// when render size is changed, check to hide the previous header's right half gripper
CheckWidthForPreviousHeaderGripper();
}
///
/// Override base method: raises the Click event only when not re-ordering
///
protected override void OnClick()
{
// if not suppress click event
if (!SuppressClickEvent)
{
// if is clicked by access key or automation,
// otherwise should be clicked by mouse
if (IsAccessKeyOrAutomation || !IsMouseOutside())
{
IsAccessKeyOrAutomation = false;
ClickImplement();
MakeParentGotFocus();
}
}
}
///
/// The Access key for this control was invoked.
///
protected override void OnAccessKey(AccessKeyEventArgs e)
{
IsAccessKeyOrAutomation = true;
base.OnAccessKey(e);
}
///
/// Stop Style, ContentTemplate , ContentTemplateSelector, ContextMenu and ToolTip properties
/// from been serialized in case the value are pushed from GridView or GridViewHeaderRowPresenter.
///
protected internal override bool ShouldSerializeProperty(DependencyProperty dp)
{
if (IsInternalGenerated)
{
// we should never reach here since this header is instantiated by HeaderRowPresenter.
Debug.Assert(false, "Method ShouldSerializeProperty is called on an internally generated GridViewColumnHeader.");
// nothing should be serialized from this object.
return false;
}
Flags flag, ignoreFlag;
PropertyToFlags(dp, out flag, out ignoreFlag); //ignoreFlag is never used in this method.
return ((flag == Flags.None) || GetFlag(flag))
&& base.ShouldSerializeProperty(dp);
}
///
/// An event reporting the mouse entered this element.
///
/// Event arguments
//Override OnMouseEnter/Leave to process the ClickMode == Hover case
protected override void OnMouseEnter(MouseEventArgs e)
{
if (HandleIsMouseOverChanged())
{
e.Handled = true;
}
}
///
/// An event reporting the mouse left this element.
///
/// Event arguments
protected override void OnMouseLeave(MouseEventArgs e)
{
if (HandleIsMouseOverChanged())
{
e.Handled = true;
}
}
///
/// An event announcing that the keyboard is no longer focused
///
/// Event arguments
protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
base.OnLostKeyboardFocus(e);
if (ClickMode == ClickMode.Hover && IsMouseCaptured)
{
ReleaseMouseCapture();
}
}
#endregion Protected Methods
//-------------------------------------------------------------------
//
// Internal Methods
//
//--------------------------------------------------------------------
#region Internal Methods
///
/// This method is called when column header is clicked via IInvokeProvider.
///
internal void AutomationClick()
{
IsAccessKeyOrAutomation = true;
OnClick();
}
// cancel resizing if Escape key down.
internal void OnColumnHeaderKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape && _headerGripper != null && _headerGripper.IsDragging)
{
// NOTE: this will cause Thumb to complete the dragging and fire drag
// complete event with the Canceled property as 'True'. Handler
// OnColumnHeaderGripperDragCompleted will restore the width.
_headerGripper.CancelDrag();
e.Handled = true;
}
}
// Check to see if hide previous header's right half gripper
internal void CheckWidthForPreviousHeaderGripper()
{
bool hideGripperRightHalf = false;
if (_headerGripper != null)
{
// when header's width is less than gripper's width,
// hide the right half of the left header's gripper
hideGripperRightHalf = DoubleUtil.LessThan(ActualWidth, _headerGripper.Width);
}
if (_previousHeader != null)
{
_previousHeader.HideGripperRightHalf(hideGripperRightHalf);
}
UpdateGripperCursor();
}
// Fix for bug 1269757 in Windows OS Bugs. Reset the background visual brush ref to
// avoid keeping it alive. Keeping a VisualBrush alive causes us to assume that the
// entire Visual tree is a graph, preventing an optimized render walk of only
// the dirty subtree. We would end up rendering all of our realizations on each
// frame, causing high CPU consumption when a large realization tree is present.
internal void ResetFloatingHeaderCanvasBackground()
{
if (_floatingHeaderCanvas != null)
{
_floatingHeaderCanvas.Background = null;
}
}
///
/// This method is called iff related properties are passed from GirdView/GridViewColumn to header.
/// And must use this method to update property from GirdView/GridViewColumn to header.
///
/// If this header is instantiated by user, before actually update the property,
/// this method will turn on the IgnoreXXX flag. And the PropertyChangeCallBack
/// will check this flag, and know that this update is an internal operation. By
/// doing this, we can distinguish {the property change by user} from {the change
/// by HeaderRowPresenter}.
///
/// the property you want to update
/// a null value will result in ClearValue operation
internal void UpdateProperty(DependencyProperty dp, object value)
{
Flags ignoreFlag = Flags.None;
if (!IsInternalGenerated)
{
Flags flag;
PropertyToFlags(dp, out flag, out ignoreFlag);
Debug.Assert(flag != Flags.None && ignoreFlag != Flags.None, "Invalid parameter dp.");
if (GetFlag(flag)) /* user has provided value for the property */
{
return;
}
else
{
SetFlag(ignoreFlag, true);
}
}
if (value != null)
{
SetValue(dp, value);
}
else
{
ClearValue(dp);
}
SetFlag(ignoreFlag, false);
}
#endregion Internal Methods
//-------------------------------------------------------------------
//
// Internal Properties
//
//--------------------------------------------------------------------
#region Internal Properties
#region DTypeThemeStyleKey
// Returns the DependencyObjectType for the registered ThemeStyleKey's default
// value. Controls will override this method to return approriate types.
internal override DependencyObjectType DTypeThemeStyleKey
{
get { return _dType; }
}
private static DependencyObjectType _dType;
#endregion DTypeThemeStyleKey
#region PreviousVisualHeader
// Link to the previous visual column header, the value is filled by GridViewHeaderRowPresenter
internal GridViewColumnHeader PreviousVisualHeader
{
get { return _previousHeader; }
set { _previousHeader = value; }
}
private GridViewColumnHeader _previousHeader;
#endregion PreviousVisualHeader
#region SuppressClickEvent
// indicating whether to fire click event
internal bool SuppressClickEvent
{
get { return GetFlag(Flags.SuppressClickEvent); }
set { SetFlag(Flags.SuppressClickEvent, value); }
}
#endregion SuppressClickEvent
// the source header for floating
// This property is only used to create VisualBrush for floating header,
// and will be set to null when VisualBrush is created. Set to null for GC.
internal GridViewColumnHeader FloatSourceHeader
{
get { return _srcHeader; }
set { _srcHeader = value; }
}
// whether this header is generated by GVHeaderRowPresenter or user
internal bool IsInternalGenerated
{
get { return GetFlag(Flags.IsInternalGenerated); }
set { SetFlag(Flags.IsInternalGenerated, value); }
}
#endregion Internal Properties
//--------------------------------------------------------------------
//
// Accessibility
//
//-------------------------------------------------------------------
#region Accessibility
///
/// Creates AutomationPeer ( )
///
protected override AutomationPeer OnCreateAutomationPeer()
{
return new GridViewColumnHeaderAutomationPeer(this);
}
#endregion
//--------------------------------------------------------------------
//
// Private Methods
//
//-------------------------------------------------------------------
#region Private Methods
private static void PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
GridViewColumnHeader header = (GridViewColumnHeader)d;
if (!header.IsInternalGenerated)
{
Flags flag, ignoreFlag;
PropertyToFlags(e.Property, out flag, out ignoreFlag);
if (!header.GetFlag(ignoreFlag)) // value is updated by user
{
if (e.NewValueSource == BaseValueSourceInternal.Local)
{
header.SetFlag(flag, true);
}
else
{
header.SetFlag(flag, false);
GridViewHeaderRowPresenter headerRowPresenter = header.Parent as GridViewHeaderRowPresenter;
if (headerRowPresenter != null)
{
headerRowPresenter.UpdateHeaderProperty(header, e.Property);
}
}
}
}
}
private static void PropertyToFlags(DependencyProperty dp, out Flags flag, out Flags ignoreFlag)
{
if (dp == GridViewColumnHeader.StyleProperty)
{
flag = Flags.StyleSetByUser;
ignoreFlag = Flags.IgnoreStyle;
}
else if (dp == GridViewColumnHeader.ContentTemplateProperty)
{
flag = Flags.ContentTemplateSetByUser;
ignoreFlag = Flags.IgnoreContentTemplate;
}
else if (dp == GridViewColumnHeader.ContentTemplateSelectorProperty)
{
flag = Flags.ContentTemplateSelectorSetByUser;
ignoreFlag = Flags.IgnoreContentTemplateSelector;
}
else if (dp == GridViewColumnHeader.ContentStringFormatProperty)
{
flag = Flags.ContentStringFormatSetByUser;
ignoreFlag = Flags.IgnoreContentStringFormat;
}
else if (dp == GridViewColumnHeader.ContextMenuProperty)
{
flag = Flags.ContextMenuSetByUser;
ignoreFlag = Flags.IgnoreContextMenu;
}
else if (dp == GridViewColumnHeader.ToolTipProperty)
{
flag = Flags.ToolTipSetByUser;
ignoreFlag = Flags.IgnoreToolTip;
}
else
{
flag = ignoreFlag = Flags.None;
}
}
///
/// Hide the right half of gripper
/// +-----------------+
/// + +----+
/// + Header + Re +
/// + + +
/// + +----+
/// +-----------------+
///
///
private void HideGripperRightHalf(bool hide)
{
if (_headerGripper != null)
{
// hide gripper's right half by setting Parent.ClipToBounds=true
FrameworkElement gripperContainer = _headerGripper.Parent as FrameworkElement;
if (gripperContainer != null)
{
gripperContainer.ClipToBounds = hide;
}
}
}
// Save the original width before header resize
private void OnColumnHeaderGripperDragStarted(object sender, DragStartedEventArgs e)
{
MakeParentGotFocus();
_originalWidth = ColumnActualWidth;
e.Handled = true;
}
//Because ColumnHeader isn't focusable, we must forward focus to ListView when user invoke the header by access key
private void MakeParentGotFocus()
{
GridViewHeaderRowPresenter headerRP = this.Parent as GridViewHeaderRowPresenter;
if (headerRP != null)
{
headerRP.MakeParentItemsControlGotFocus();
}
}
// Resize the header
private void OnColumnHeaderResize(object sender, DragDeltaEventArgs e)
{
double width = ColumnActualWidth + e.HorizontalChange;
if (DoubleUtil.LessThanOrClose(width, 0.0))
{
width = 0.0;
}
UpdateColumnHeaderWidth(width);
e.Handled = true;
}
private void OnColumnHeaderGripperDragCompleted(object sender, DragCompletedEventArgs e)
{
if (e.Canceled)
{
// restore to original width
UpdateColumnHeaderWidth(_originalWidth);
}
UpdateGripperCursor();
e.Handled = true;
}
///
/// Find gripper and register drag event
///
/// The default style for GridViewColumnHeader is
/// +-----------------+
/// + +----------+
/// + Header + Gripper +
/// + + +
/// + +----------+
/// +-----------------+
///
private void HookupGripperEvents()
{
UnhookGripperEvents();
_headerGripper = GetTemplateChild(HeaderGripperTemplateName) as Thumb;
if (_headerGripper != null)
{
_headerGripper.DragStarted += new DragStartedEventHandler(OnColumnHeaderGripperDragStarted);
_headerGripper.DragDelta += new DragDeltaEventHandler(OnColumnHeaderResize);
_headerGripper.DragCompleted += new DragCompletedEventHandler(OnColumnHeaderGripperDragCompleted);
_headerGripper.MouseDoubleClick += new MouseButtonEventHandler(OnGripperDoubleClicked);
_headerGripper.MouseEnter += new MouseEventHandler(OnGripperMouseEnterLeave);
_headerGripper.MouseLeave += new MouseEventHandler(OnGripperMouseEnterLeave);
_headerGripper.Cursor = SplitCursor;
}
}
private void OnGripperDoubleClicked(object sender, MouseButtonEventArgs e)
{
if (Column != null)
{
if (Double.IsNaN(Column.Width))
{
// force update will be triggered
Column.Width = Column.ActualWidth;
}
Column.Width = Double.NaN;
e.Handled = true;
}
}
///
/// Clear gripper event
///
private void UnhookGripperEvents()
{
if (_headerGripper != null)
{
_headerGripper.DragStarted -= new DragStartedEventHandler(OnColumnHeaderGripperDragStarted);
_headerGripper.DragDelta -= new DragDeltaEventHandler(OnColumnHeaderResize);
_headerGripper.DragCompleted -= new DragCompletedEventHandler(OnColumnHeaderGripperDragCompleted);
_headerGripper.MouseDoubleClick -= new MouseButtonEventHandler(OnGripperDoubleClicked);
_headerGripper.MouseEnter -= new MouseEventHandler(OnGripperMouseEnterLeave);
_headerGripper.MouseLeave -= new MouseEventHandler(OnGripperMouseEnterLeave);
_headerGripper = null;
}
}
///
/// Critical - This code calls Marshal.GETHINSTANCE methods
/// TreatAsSafe - This code is safe to hand out (does not hand out HandleRef, the instance retrieved is not exposed to other code but is used to call the function)
///
[SecurityCritical, SecurityTreatAsSafe]
private Cursor GetCursor(int cursorID)
{
Invariant.Assert(cursorID == c_SPLIT || cursorID == c_SPLITOPEN, "incorrect cursor type");
Cursor cursor = null;
if (cursorID == c_SPLIT || cursorID == c_SPLITOPEN)
{
SafeHandle cursorHandle = SafeNativeMethods.LoadCursor(new HandleRef(this, Marshal.GetHINSTANCE(typeof(GridViewColumnHeader).Module)), (IntPtr)cursorID);
Debug.Assert(cursorHandle != null, "cursorHandle is null");
Debug.Assert(!cursorHandle.IsInvalid, "cursorHandle is invalid");
if (cursorHandle != null && !cursorHandle.IsInvalid)
{
cursor = new Cursor(cursorHandle);
}
}
return cursor;
}
private void UpdateGripperCursor()
{
if (_headerGripper != null && !_headerGripper.IsDragging)
{
Cursor gripperCursor;
if (DoubleUtil.IsZero(ActualWidth))
{
gripperCursor = SplitOpenCursor;
}
else
{
gripperCursor = SplitCursor;
}
Debug.Assert(gripperCursor != null, "gripper cursor is null");
if (gripperCursor != null)
{
_headerGripper.Cursor = gripperCursor;
}
}
}
// Set column header width and associated column width
private void UpdateColumnHeaderWidth(double width)
{
if (Column != null)
{
Column.Width = width;
}
else
{
Width = width;
}
}
private bool IsMouseOutside()
{
Point pos = Mouse.PrimaryDevice.GetPosition(this);
return !((pos.X >= 0) && (pos.X <= ActualWidth) && (pos.Y >= 0) && (pos.Y <= ActualHeight));
}
private void ClickImplement()
{
if (AutomationPeer.ListenerExists(AutomationEvents.InvokePatternOnInvoked))
{
AutomationPeer peer = UIElementAutomationPeer.CreatePeerForElement(this);
if (peer != null)
peer.RaiseAutomationEvent(AutomationEvents.InvokePatternOnInvoked);
}
base.OnClick();
}
private bool GetFlag(Flags flag)
{
return (_flags & flag) == flag;
}
private void SetFlag(Flags flag, bool set)
{
if (set)
{
_flags |= flag;
}
else
{
_flags &= (~flag);
}
}
// update the background visual brush
private void UpdateFloatingHeaderCanvas()
{
if (_floatingHeaderCanvas != null
&& FloatSourceHeader != null)
{
// because the gripper is partially positioned out of the header, we need to
// map the appropriate area(viewbox) in the source header to visual brush
// to avoid a distorded image on the floating header.
Vector offsetVector = VisualTreeHelper.GetOffset(FloatSourceHeader);
VisualBrush visualBrush = new VisualBrush(FloatSourceHeader);
// set visual brush's mapping
visualBrush.ViewboxUnits = BrushMappingMode.Absolute;
visualBrush.Viewbox = new Rect(offsetVector.X, offsetVector.Y, FloatSourceHeader.ActualWidth, FloatSourceHeader.ActualHeight);
_floatingHeaderCanvas.Background = visualBrush;
FloatSourceHeader = null;
}
}
///
/// Handle IsMouseOverChanged when ClickMode is Hover
///
// Note: When ClickMode is Hover, ColumnHeader will be click when mouse is over it
// Here are 2 cases:
// 1) Mouse is over column header
// OnClick will be called
// 2) Mouse is over gripper
// OnClick won't be called, only when the mouse leaves the gripper and move to header, OnClick will be called.
private bool HandleIsMouseOverChanged()
{
if (ClickMode == ClickMode.Hover)
{
if (IsMouseOver &&
//1) Gripper doesn't exist; 2) Gripper exists and Mouse isn't on Gripper;
(_headerGripper == null || !_headerGripper.IsMouseOver))
{
// Hovering over the button will click in the OnHover click mode
SetValue(IsPressedPropertyKey, BooleanBoxes.Box(true));
OnClick();
}
else
{
ClearValue(IsPressedPropertyKey);
}
return true;
}
return false;
}
// When mouse enters/leaves gripper, recall HandleIsMouseOverChanged to verify is mouse over header or not
private void OnGripperMouseEnterLeave(object sender, MouseEventArgs e)
{
HandleIsMouseOverChanged();
}
#endregion Private Methods
//-------------------------------------------------------------------
//
// Private Properties
//
//-------------------------------------------------------------------
#region Private Properties
#region SplitCursor
private Cursor SplitCursor
{
get
{
if (_splitCursorCache == null)
{
_splitCursorCache = GetCursor(c_SPLIT);
}
return _splitCursorCache;
}
}
static private Cursor _splitCursorCache = null;
#endregion SplitCursor
#region SplitOpenCursor
private Cursor SplitOpenCursor
{
get
{
if (_splitOpenCursorCache == null)
{
_splitOpenCursorCache = GetCursor(c_SPLITOPEN);
}
return _splitOpenCursorCache;
}
}
static private Cursor _splitOpenCursorCache = null;
#endregion SplitOpenCursor
// is clicked by access key or automation
private bool IsAccessKeyOrAutomation
{
get { return GetFlag(Flags.IsAccessKeyOrAutomation); }
set { SetFlag(Flags.IsAccessKeyOrAutomation, value); }
}
private double ColumnActualWidth
{
get { return (Column != null ? Column.ActualWidth : ActualWidth); }
}
#endregion Private Properties
//--------------------------------------------------------------------
//
// Private Fields
//
//-------------------------------------------------------------------
#region Private Fields
///
/// StyleSetByUser: the value of Style property is set by user.
/// IgnoreStyle: the OnStyleChanged is triggered by HeaderRowPresenter,
/// not by user. Don't turn on the StyleSetByUser flag.
/// And so on
/// (Only for user provided header. Ignored for internal generated header)
///
/// Go to UpdateProperty and OnPropetyChanged for how these flags work.
///
[Flags]
private enum Flags
{
// IgnoreXXX can't be combined into one flag.
// Reason:
// Define a Style with ContentTemplate and assign it to GridViewColumn.HeaderContainerStyle property. GridViewColumnHeader.OnPropertyChagned method will be called twice.
// The first call is for ContentTemplate property. In this call, IgnoreContentTemplate is false.
// The second call is for Style property. In this call, IgnoreStyle is true.
// One flag can’t distinguish them.
None = 0,
StyleSetByUser = 0x00000001,
IgnoreStyle = 0x00000002,
ContentTemplateSetByUser = 0x00000004,
IgnoreContentTemplate = 0x00000008,
ContentTemplateSelectorSetByUser = 0x00000010,
IgnoreContentTemplateSelector = 0x00000020,
ContextMenuSetByUser = 0x00000040,
IgnoreContextMenu = 0x00000080,
ToolTipSetByUser = 0x00000100,
IgnoreToolTip = 0x00000200,
SuppressClickEvent = 0x00000400,
IsInternalGenerated = 0x00000800,
IsAccessKeyOrAutomation = 0x00001000,
ContentStringFormatSetByUser = 0x00002000,
IgnoreContentStringFormat = 0x00004000,
}
private Flags _flags;
private Thumb _headerGripper;
private double _originalWidth;
// canvas for floating header
private Canvas _floatingHeaderCanvas;
private GridViewColumnHeader _srcHeader;
// cursor id in embedded win32 resource
private const int c_SPLIT = 100;
private const int c_SPLITOPEN = 101;
// Part name used in the style. The class TemplatePartAttribute should use the same name
private const string HeaderGripperTemplateName = "PART_HeaderGripper";
private const string FloatingHeaderCanvasTemplateName = "PART_FloatingHeaderCanvas";
#endregion Private Fields
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//---------------------------------------------------------------------------
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices; // SafeHandle
using System.Security; // [SecurityCritical]
using System.Windows.Automation.Peers; // AutomationPeer
using System.Windows.Controls.Primitives; // ButtonBase
using System.Windows.Input; // MouseButtonEventArgs
using System.Windows.Media; // VisualBrush
using MS.Internal; // DoubleUtil
using MS.Internal.KnownBoxes; // BooleanBoxes
using MS.Win32; // SafeNativeMethods
namespace System.Windows.Controls
{
///
/// Defines the different roles of GridViewColumnHeaders
///
public enum GridViewColumnHeaderRole
{
///
/// The normal header
///
Normal,
///
/// The floating header (when dragging a header)
///
Floating,
///
/// The padding header (the very last header in header bar)
///
Padding
}
///
/// column header of GridView
///
#if OLD_AUTOMATION
[Automation(AccessibilityControlType = "Button")]
#endif
[TemplatePart(Name = "PART_HeaderGripper", Type = typeof(Thumb))]
[TemplatePart(Name = "PART_FloatingHeaderCanvas", Type = typeof(Canvas))]
public class GridViewColumnHeader : ButtonBase
#if OLD_AUTOMATION
, IInvokeProvider
#endif
{
//-------------------------------------------------------------------
//
// Constructors
//
//-------------------------------------------------------------------
#region Constructor
static GridViewColumnHeader()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(GridViewColumnHeader), new FrameworkPropertyMetadata(typeof(GridViewColumnHeader)));
_dType = DependencyObjectType.FromSystemTypeInternal(typeof(GridViewColumnHeader));
FocusableProperty.OverrideMetadata(typeof(GridViewColumnHeader), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox));
// hookup property change event.
StyleProperty.OverrideMetadata(typeof(GridViewColumnHeader), new FrameworkPropertyMetadata(new PropertyChangedCallback(PropertyChanged)));
ContentTemplateProperty.OverrideMetadata(typeof(GridViewColumnHeader), new FrameworkPropertyMetadata(new PropertyChangedCallback(PropertyChanged)));
ContentTemplateSelectorProperty.OverrideMetadata(typeof(GridViewColumnHeader), new FrameworkPropertyMetadata(new PropertyChangedCallback(PropertyChanged)));
ContextMenuProperty.OverrideMetadata(typeof(GridViewColumnHeader), new FrameworkPropertyMetadata(new PropertyChangedCallback(PropertyChanged)));
ToolTipProperty.OverrideMetadata(typeof(GridViewColumnHeader), new FrameworkPropertyMetadata(new PropertyChangedCallback(PropertyChanged)));
}
#endregion
//--------------------------------------------------------------------
//
// Public Methods
//
//-------------------------------------------------------------------
#region Public Methods
///
/// Called when the Template's tree has been generated
///
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
GridViewColumnHeaderRole role = Role;
if (role == GridViewColumnHeaderRole.Normal)
{
HookupGripperEvents();
}
else if (role == GridViewColumnHeaderRole.Floating)
{
// if this is a floating header, try to find the FloatingHeaderCanvas,
// and copy source header's visual to it
_floatingHeaderCanvas = GetTemplateChild(FloatingHeaderCanvasTemplateName) as Canvas;
UpdateFloatingHeaderCanvas();
}
}
#endregion
//--------------------------------------------------------------------
//
// Public Properties
//
//--------------------------------------------------------------------
#region Public Properties
///
/// The key for Column (read-only property)
///
internal static readonly DependencyPropertyKey ColumnPropertyKey =
DependencyProperty.RegisterReadOnly(
"Column",
typeof(GridViewColumn),
typeof(GridViewColumnHeader),
null);
///
/// The DependencyProperty for the Column property.
///
public static readonly DependencyProperty ColumnProperty =
ColumnPropertyKey.DependencyProperty;
///
/// Column associated with this header
///
public GridViewColumn Column
{
get { return (GridViewColumn)GetValue(ColumnProperty); }
}
///
/// The key for Role (read-only property)
///
internal static readonly DependencyPropertyKey RolePropertyKey =
DependencyProperty.RegisterReadOnly(
"Role",
typeof(GridViewColumnHeaderRole),
typeof(GridViewColumnHeader),
new FrameworkPropertyMetadata(GridViewColumnHeaderRole.Normal));
///
/// The DependencyProperty for the Role property.
///
public static readonly DependencyProperty RoleProperty =
RolePropertyKey.DependencyProperty;
///
/// What the role of the header is: Normal, Floating, Padding.
///
[Category("Behavior")]
public GridViewColumnHeaderRole Role
{
get { return (GridViewColumnHeaderRole)GetValue(RoleProperty); }
}
#endregion Public Properties
#if OLD_AUTOMATION
//-------------------------------------------------------------------
//
// IInvodeProvider
//
//--------------------------------------------------------------------
void IInvokeProvider.Invoke()
{
IsAccessKeyOrAutomation = true;
OnClick();
}
#endif
//-------------------------------------------------------------------
//
// Protected Methods
//
//-------------------------------------------------------------------
#region Protected Methods
///
/// This is the method that responds to the MouseButtonEvent event.
///
/// Event arguments
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonUp(e);
// give parent a chance to handle MouseButtonEvent (for GridViewHeaderRowPresenter by default)
e.Handled = false;
if (ClickMode == ClickMode.Hover && IsMouseCaptured)
{
ReleaseMouseCapture();
}
}
///
/// This is the method that responds to the MouseButtonEvent event.
///
/// Event arguments
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
// give parent a chance to handle MouseButtonEvent (for GridViewHeaderRowPresenter by default)
e.Handled = false;
//If ClickMode is Hover, we must capture mouse in order to let column reorder work correctly (Bug#1496673)
if (ClickMode == ClickMode.Hover && e.ButtonState == MouseButtonState.Pressed)
{
CaptureMouse();
}
}
///
/// This is the method that responds to the MouseMoveEvent event.
///
/// Event arguments
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
// Override base method: if left mouse is pressed, always set IsPressed as true
if ((ClickMode != ClickMode.Hover) &&
(IsMouseCaptured && (Mouse.PrimaryDevice.LeftButton == MouseButtonState.Pressed)))
{
SetValue(ButtonBase.IsPressedPropertyKey, BooleanBoxes.TrueBox);
}
e.Handled = false;
}
///
/// Override for
///
protected internal override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
// when render size is changed, check to hide the previous header's right half gripper
CheckWidthForPreviousHeaderGripper();
}
///
/// Override base method: raises the Click event only when not re-ordering
///
protected override void OnClick()
{
// if not suppress click event
if (!SuppressClickEvent)
{
// if is clicked by access key or automation,
// otherwise should be clicked by mouse
if (IsAccessKeyOrAutomation || !IsMouseOutside())
{
IsAccessKeyOrAutomation = false;
ClickImplement();
MakeParentGotFocus();
}
}
}
///
/// The Access key for this control was invoked.
///
protected override void OnAccessKey(AccessKeyEventArgs e)
{
IsAccessKeyOrAutomation = true;
base.OnAccessKey(e);
}
///
/// Stop Style, ContentTemplate , ContentTemplateSelector, ContextMenu and ToolTip properties
/// from been serialized in case the value are pushed from GridView or GridViewHeaderRowPresenter.
///
protected internal override bool ShouldSerializeProperty(DependencyProperty dp)
{
if (IsInternalGenerated)
{
// we should never reach here since this header is instantiated by HeaderRowPresenter.
Debug.Assert(false, "Method ShouldSerializeProperty is called on an internally generated GridViewColumnHeader.");
// nothing should be serialized from this object.
return false;
}
Flags flag, ignoreFlag;
PropertyToFlags(dp, out flag, out ignoreFlag); //ignoreFlag is never used in this method.
return ((flag == Flags.None) || GetFlag(flag))
&& base.ShouldSerializeProperty(dp);
}
///
/// An event reporting the mouse entered this element.
///
/// Event arguments
//Override OnMouseEnter/Leave to process the ClickMode == Hover case
protected override void OnMouseEnter(MouseEventArgs e)
{
if (HandleIsMouseOverChanged())
{
e.Handled = true;
}
}
///
/// An event reporting the mouse left this element.
///
/// Event arguments
protected override void OnMouseLeave(MouseEventArgs e)
{
if (HandleIsMouseOverChanged())
{
e.Handled = true;
}
}
///
/// An event announcing that the keyboard is no longer focused
///
/// Event arguments
protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
base.OnLostKeyboardFocus(e);
if (ClickMode == ClickMode.Hover && IsMouseCaptured)
{
ReleaseMouseCapture();
}
}
#endregion Protected Methods
//-------------------------------------------------------------------
//
// Internal Methods
//
//--------------------------------------------------------------------
#region Internal Methods
///
/// This method is called when column header is clicked via IInvokeProvider.
///
internal void AutomationClick()
{
IsAccessKeyOrAutomation = true;
OnClick();
}
// cancel resizing if Escape key down.
internal void OnColumnHeaderKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape && _headerGripper != null && _headerGripper.IsDragging)
{
// NOTE: this will cause Thumb to complete the dragging and fire drag
// complete event with the Canceled property as 'True'. Handler
// OnColumnHeaderGripperDragCompleted will restore the width.
_headerGripper.CancelDrag();
e.Handled = true;
}
}
// Check to see if hide previous header's right half gripper
internal void CheckWidthForPreviousHeaderGripper()
{
bool hideGripperRightHalf = false;
if (_headerGripper != null)
{
// when header's width is less than gripper's width,
// hide the right half of the left header's gripper
hideGripperRightHalf = DoubleUtil.LessThan(ActualWidth, _headerGripper.Width);
}
if (_previousHeader != null)
{
_previousHeader.HideGripperRightHalf(hideGripperRightHalf);
}
UpdateGripperCursor();
}
// Fix for bug 1269757 in Windows OS Bugs. Reset the background visual brush ref to
// avoid keeping it alive. Keeping a VisualBrush alive causes us to assume that the
// entire Visual tree is a graph, preventing an optimized render walk of only
// the dirty subtree. We would end up rendering all of our realizations on each
// frame, causing high CPU consumption when a large realization tree is present.
internal void ResetFloatingHeaderCanvasBackground()
{
if (_floatingHeaderCanvas != null)
{
_floatingHeaderCanvas.Background = null;
}
}
///
/// This method is called iff related properties are passed from GirdView/GridViewColumn to header.
/// And must use this method to update property from GirdView/GridViewColumn to header.
///
/// If this header is instantiated by user, before actually update the property,
/// this method will turn on the IgnoreXXX flag. And the PropertyChangeCallBack
/// will check this flag, and know that this update is an internal operation. By
/// doing this, we can distinguish {the property change by user} from {the change
/// by HeaderRowPresenter}.
///
/// the property you want to update
/// a null value will result in ClearValue operation
internal void UpdateProperty(DependencyProperty dp, object value)
{
Flags ignoreFlag = Flags.None;
if (!IsInternalGenerated)
{
Flags flag;
PropertyToFlags(dp, out flag, out ignoreFlag);
Debug.Assert(flag != Flags.None && ignoreFlag != Flags.None, "Invalid parameter dp.");
if (GetFlag(flag)) /* user has provided value for the property */
{
return;
}
else
{
SetFlag(ignoreFlag, true);
}
}
if (value != null)
{
SetValue(dp, value);
}
else
{
ClearValue(dp);
}
SetFlag(ignoreFlag, false);
}
#endregion Internal Methods
//-------------------------------------------------------------------
//
// Internal Properties
//
//--------------------------------------------------------------------
#region Internal Properties
#region DTypeThemeStyleKey
// Returns the DependencyObjectType for the registered ThemeStyleKey's default
// value. Controls will override this method to return approriate types.
internal override DependencyObjectType DTypeThemeStyleKey
{
get { return _dType; }
}
private static DependencyObjectType _dType;
#endregion DTypeThemeStyleKey
#region PreviousVisualHeader
// Link to the previous visual column header, the value is filled by GridViewHeaderRowPresenter
internal GridViewColumnHeader PreviousVisualHeader
{
get { return _previousHeader; }
set { _previousHeader = value; }
}
private GridViewColumnHeader _previousHeader;
#endregion PreviousVisualHeader
#region SuppressClickEvent
// indicating whether to fire click event
internal bool SuppressClickEvent
{
get { return GetFlag(Flags.SuppressClickEvent); }
set { SetFlag(Flags.SuppressClickEvent, value); }
}
#endregion SuppressClickEvent
// the source header for floating
// This property is only used to create VisualBrush for floating header,
// and will be set to null when VisualBrush is created. Set to null for GC.
internal GridViewColumnHeader FloatSourceHeader
{
get { return _srcHeader; }
set { _srcHeader = value; }
}
// whether this header is generated by GVHeaderRowPresenter or user
internal bool IsInternalGenerated
{
get { return GetFlag(Flags.IsInternalGenerated); }
set { SetFlag(Flags.IsInternalGenerated, value); }
}
#endregion Internal Properties
//--------------------------------------------------------------------
//
// Accessibility
//
//-------------------------------------------------------------------
#region Accessibility
///
/// Creates AutomationPeer ( )
///
protected override AutomationPeer OnCreateAutomationPeer()
{
return new GridViewColumnHeaderAutomationPeer(this);
}
#endregion
//--------------------------------------------------------------------
//
// Private Methods
//
//-------------------------------------------------------------------
#region Private Methods
private static void PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
GridViewColumnHeader header = (GridViewColumnHeader)d;
if (!header.IsInternalGenerated)
{
Flags flag, ignoreFlag;
PropertyToFlags(e.Property, out flag, out ignoreFlag);
if (!header.GetFlag(ignoreFlag)) // value is updated by user
{
if (e.NewValueSource == BaseValueSourceInternal.Local)
{
header.SetFlag(flag, true);
}
else
{
header.SetFlag(flag, false);
GridViewHeaderRowPresenter headerRowPresenter = header.Parent as GridViewHeaderRowPresenter;
if (headerRowPresenter != null)
{
headerRowPresenter.UpdateHeaderProperty(header, e.Property);
}
}
}
}
}
private static void PropertyToFlags(DependencyProperty dp, out Flags flag, out Flags ignoreFlag)
{
if (dp == GridViewColumnHeader.StyleProperty)
{
flag = Flags.StyleSetByUser;
ignoreFlag = Flags.IgnoreStyle;
}
else if (dp == GridViewColumnHeader.ContentTemplateProperty)
{
flag = Flags.ContentTemplateSetByUser;
ignoreFlag = Flags.IgnoreContentTemplate;
}
else if (dp == GridViewColumnHeader.ContentTemplateSelectorProperty)
{
flag = Flags.ContentTemplateSelectorSetByUser;
ignoreFlag = Flags.IgnoreContentTemplateSelector;
}
else if (dp == GridViewColumnHeader.ContentStringFormatProperty)
{
flag = Flags.ContentStringFormatSetByUser;
ignoreFlag = Flags.IgnoreContentStringFormat;
}
else if (dp == GridViewColumnHeader.ContextMenuProperty)
{
flag = Flags.ContextMenuSetByUser;
ignoreFlag = Flags.IgnoreContextMenu;
}
else if (dp == GridViewColumnHeader.ToolTipProperty)
{
flag = Flags.ToolTipSetByUser;
ignoreFlag = Flags.IgnoreToolTip;
}
else
{
flag = ignoreFlag = Flags.None;
}
}
///
/// Hide the right half of gripper
/// +-----------------+
/// + +----+
/// + Header + Re +
/// + + +
/// + +----+
/// +-----------------+
///
///
private void HideGripperRightHalf(bool hide)
{
if (_headerGripper != null)
{
// hide gripper's right half by setting Parent.ClipToBounds=true
FrameworkElement gripperContainer = _headerGripper.Parent as FrameworkElement;
if (gripperContainer != null)
{
gripperContainer.ClipToBounds = hide;
}
}
}
// Save the original width before header resize
private void OnColumnHeaderGripperDragStarted(object sender, DragStartedEventArgs e)
{
MakeParentGotFocus();
_originalWidth = ColumnActualWidth;
e.Handled = true;
}
//Because ColumnHeader isn't focusable, we must forward focus to ListView when user invoke the header by access key
private void MakeParentGotFocus()
{
GridViewHeaderRowPresenter headerRP = this.Parent as GridViewHeaderRowPresenter;
if (headerRP != null)
{
headerRP.MakeParentItemsControlGotFocus();
}
}
// Resize the header
private void OnColumnHeaderResize(object sender, DragDeltaEventArgs e)
{
double width = ColumnActualWidth + e.HorizontalChange;
if (DoubleUtil.LessThanOrClose(width, 0.0))
{
width = 0.0;
}
UpdateColumnHeaderWidth(width);
e.Handled = true;
}
private void OnColumnHeaderGripperDragCompleted(object sender, DragCompletedEventArgs e)
{
if (e.Canceled)
{
// restore to original width
UpdateColumnHeaderWidth(_originalWidth);
}
UpdateGripperCursor();
e.Handled = true;
}
///
/// Find gripper and register drag event
///
/// The default style for GridViewColumnHeader is
/// +-----------------+
/// + +----------+
/// + Header + Gripper +
/// + + +
/// + +----------+
/// +-----------------+
///
private void HookupGripperEvents()
{
UnhookGripperEvents();
_headerGripper = GetTemplateChild(HeaderGripperTemplateName) as Thumb;
if (_headerGripper != null)
{
_headerGripper.DragStarted += new DragStartedEventHandler(OnColumnHeaderGripperDragStarted);
_headerGripper.DragDelta += new DragDeltaEventHandler(OnColumnHeaderResize);
_headerGripper.DragCompleted += new DragCompletedEventHandler(OnColumnHeaderGripperDragCompleted);
_headerGripper.MouseDoubleClick += new MouseButtonEventHandler(OnGripperDoubleClicked);
_headerGripper.MouseEnter += new MouseEventHandler(OnGripperMouseEnterLeave);
_headerGripper.MouseLeave += new MouseEventHandler(OnGripperMouseEnterLeave);
_headerGripper.Cursor = SplitCursor;
}
}
private void OnGripperDoubleClicked(object sender, MouseButtonEventArgs e)
{
if (Column != null)
{
if (Double.IsNaN(Column.Width))
{
// force update will be triggered
Column.Width = Column.ActualWidth;
}
Column.Width = Double.NaN;
e.Handled = true;
}
}
///
/// Clear gripper event
///
private void UnhookGripperEvents()
{
if (_headerGripper != null)
{
_headerGripper.DragStarted -= new DragStartedEventHandler(OnColumnHeaderGripperDragStarted);
_headerGripper.DragDelta -= new DragDeltaEventHandler(OnColumnHeaderResize);
_headerGripper.DragCompleted -= new DragCompletedEventHandler(OnColumnHeaderGripperDragCompleted);
_headerGripper.MouseDoubleClick -= new MouseButtonEventHandler(OnGripperDoubleClicked);
_headerGripper.MouseEnter -= new MouseEventHandler(OnGripperMouseEnterLeave);
_headerGripper.MouseLeave -= new MouseEventHandler(OnGripperMouseEnterLeave);
_headerGripper = null;
}
}
///
/// Critical - This code calls Marshal.GETHINSTANCE methods
/// TreatAsSafe - This code is safe to hand out (does not hand out HandleRef, the instance retrieved is not exposed to other code but is used to call the function)
///
[SecurityCritical, SecurityTreatAsSafe]
private Cursor GetCursor(int cursorID)
{
Invariant.Assert(cursorID == c_SPLIT || cursorID == c_SPLITOPEN, "incorrect cursor type");
Cursor cursor = null;
if (cursorID == c_SPLIT || cursorID == c_SPLITOPEN)
{
SafeHandle cursorHandle = SafeNativeMethods.LoadCursor(new HandleRef(this, Marshal.GetHINSTANCE(typeof(GridViewColumnHeader).Module)), (IntPtr)cursorID);
Debug.Assert(cursorHandle != null, "cursorHandle is null");
Debug.Assert(!cursorHandle.IsInvalid, "cursorHandle is invalid");
if (cursorHandle != null && !cursorHandle.IsInvalid)
{
cursor = new Cursor(cursorHandle);
}
}
return cursor;
}
private void UpdateGripperCursor()
{
if (_headerGripper != null && !_headerGripper.IsDragging)
{
Cursor gripperCursor;
if (DoubleUtil.IsZero(ActualWidth))
{
gripperCursor = SplitOpenCursor;
}
else
{
gripperCursor = SplitCursor;
}
Debug.Assert(gripperCursor != null, "gripper cursor is null");
if (gripperCursor != null)
{
_headerGripper.Cursor = gripperCursor;
}
}
}
// Set column header width and associated column width
private void UpdateColumnHeaderWidth(double width)
{
if (Column != null)
{
Column.Width = width;
}
else
{
Width = width;
}
}
private bool IsMouseOutside()
{
Point pos = Mouse.PrimaryDevice.GetPosition(this);
return !((pos.X >= 0) && (pos.X <= ActualWidth) && (pos.Y >= 0) && (pos.Y <= ActualHeight));
}
private void ClickImplement()
{
if (AutomationPeer.ListenerExists(AutomationEvents.InvokePatternOnInvoked))
{
AutomationPeer peer = UIElementAutomationPeer.CreatePeerForElement(this);
if (peer != null)
peer.RaiseAutomationEvent(AutomationEvents.InvokePatternOnInvoked);
}
base.OnClick();
}
private bool GetFlag(Flags flag)
{
return (_flags & flag) == flag;
}
private void SetFlag(Flags flag, bool set)
{
if (set)
{
_flags |= flag;
}
else
{
_flags &= (~flag);
}
}
// update the background visual brush
private void UpdateFloatingHeaderCanvas()
{
if (_floatingHeaderCanvas != null
&& FloatSourceHeader != null)
{
// because the gripper is partially positioned out of the header, we need to
// map the appropriate area(viewbox) in the source header to visual brush
// to avoid a distorded image on the floating header.
Vector offsetVector = VisualTreeHelper.GetOffset(FloatSourceHeader);
VisualBrush visualBrush = new VisualBrush(FloatSourceHeader);
// set visual brush's mapping
visualBrush.ViewboxUnits = BrushMappingMode.Absolute;
visualBrush.Viewbox = new Rect(offsetVector.X, offsetVector.Y, FloatSourceHeader.ActualWidth, FloatSourceHeader.ActualHeight);
_floatingHeaderCanvas.Background = visualBrush;
FloatSourceHeader = null;
}
}
///
/// Handle IsMouseOverChanged when ClickMode is Hover
///
// Note: When ClickMode is Hover, ColumnHeader will be click when mouse is over it
// Here are 2 cases:
// 1) Mouse is over column header
// OnClick will be called
// 2) Mouse is over gripper
// OnClick won't be called, only when the mouse leaves the gripper and move to header, OnClick will be called.
private bool HandleIsMouseOverChanged()
{
if (ClickMode == ClickMode.Hover)
{
if (IsMouseOver &&
//1) Gripper doesn't exist; 2) Gripper exists and Mouse isn't on Gripper;
(_headerGripper == null || !_headerGripper.IsMouseOver))
{
// Hovering over the button will click in the OnHover click mode
SetValue(IsPressedPropertyKey, BooleanBoxes.Box(true));
OnClick();
}
else
{
ClearValue(IsPressedPropertyKey);
}
return true;
}
return false;
}
// When mouse enters/leaves gripper, recall HandleIsMouseOverChanged to verify is mouse over header or not
private void OnGripperMouseEnterLeave(object sender, MouseEventArgs e)
{
HandleIsMouseOverChanged();
}
#endregion Private Methods
//-------------------------------------------------------------------
//
// Private Properties
//
//-------------------------------------------------------------------
#region Private Properties
#region SplitCursor
private Cursor SplitCursor
{
get
{
if (_splitCursorCache == null)
{
_splitCursorCache = GetCursor(c_SPLIT);
}
return _splitCursorCache;
}
}
static private Cursor _splitCursorCache = null;
#endregion SplitCursor
#region SplitOpenCursor
private Cursor SplitOpenCursor
{
get
{
if (_splitOpenCursorCache == null)
{
_splitOpenCursorCache = GetCursor(c_SPLITOPEN);
}
return _splitOpenCursorCache;
}
}
static private Cursor _splitOpenCursorCache = null;
#endregion SplitOpenCursor
// is clicked by access key or automation
private bool IsAccessKeyOrAutomation
{
get { return GetFlag(Flags.IsAccessKeyOrAutomation); }
set { SetFlag(Flags.IsAccessKeyOrAutomation, value); }
}
private double ColumnActualWidth
{
get { return (Column != null ? Column.ActualWidth : ActualWidth); }
}
#endregion Private Properties
//--------------------------------------------------------------------
//
// Private Fields
//
//-------------------------------------------------------------------
#region Private Fields
///
/// StyleSetByUser: the value of Style property is set by user.
/// IgnoreStyle: the OnStyleChanged is triggered by HeaderRowPresenter,
/// not by user. Don't turn on the StyleSetByUser flag.
/// And so on
/// (Only for user provided header. Ignored for internal generated header)
///
/// Go to UpdateProperty and OnPropetyChanged for how these flags work.
///
[Flags]
private enum Flags
{
// IgnoreXXX can't be combined into one flag.
// Reason:
// Define a Style with ContentTemplate and assign it to GridViewColumn.HeaderContainerStyle property. GridViewColumnHeader.OnPropertyChagned method will be called twice.
// The first call is for ContentTemplate property. In this call, IgnoreContentTemplate is false.
// The second call is for Style property. In this call, IgnoreStyle is true.
// One flag can’t distinguish them.
None = 0,
StyleSetByUser = 0x00000001,
IgnoreStyle = 0x00000002,
ContentTemplateSetByUser = 0x00000004,
IgnoreContentTemplate = 0x00000008,
ContentTemplateSelectorSetByUser = 0x00000010,
IgnoreContentTemplateSelector = 0x00000020,
ContextMenuSetByUser = 0x00000040,
IgnoreContextMenu = 0x00000080,
ToolTipSetByUser = 0x00000100,
IgnoreToolTip = 0x00000200,
SuppressClickEvent = 0x00000400,
IsInternalGenerated = 0x00000800,
IsAccessKeyOrAutomation = 0x00001000,
ContentStringFormatSetByUser = 0x00002000,
IgnoreContentStringFormat = 0x00004000,
}
private Flags _flags;
private Thumb _headerGripper;
private double _originalWidth;
// canvas for floating header
private Canvas _floatingHeaderCanvas;
private GridViewColumnHeader _srcHeader;
// cursor id in embedded win32 resource
private const int c_SPLIT = 100;
private const int c_SPLITOPEN = 101;
// Part name used in the style. The class TemplatePartAttribute should use the same name
private const string HeaderGripperTemplateName = "PART_HeaderGripper";
private const string FloatingHeaderCanvasTemplateName = "PART_FloatingHeaderCanvas";
#endregion Private Fields
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- OdbcConnectionHandle.cs
- URI.cs
- TextMetrics.cs
- basemetadatamappingvisitor.cs
- SecurityRuntime.cs
- GeometryCombineModeValidation.cs
- BevelBitmapEffect.cs
- SubMenuStyle.cs
- ToolStripTextBox.cs
- CodeSnippetTypeMember.cs
- Event.cs
- ByteAnimation.cs
- AudioSignalProblemOccurredEventArgs.cs
- FixedSOMImage.cs
- EventMemberCodeDomSerializer.cs
- TextViewSelectionProcessor.cs
- RootBrowserWindowProxy.cs
- RecommendedAsConfigurableAttribute.cs
- CatalogZoneDesigner.cs
- NotificationContext.cs
- SafeNativeMemoryHandle.cs
- JulianCalendar.cs
- KeyValueInternalCollection.cs
- ScriptControl.cs
- EntryIndex.cs
- IRCollection.cs
- ArrayList.cs
- PreservationFileWriter.cs
- IPEndPoint.cs
- IisTraceListener.cs
- CardSpaceSelector.cs
- UIPropertyMetadata.cs
- TimeSpanValidatorAttribute.cs
- Enum.cs
- HierarchicalDataBoundControlAdapter.cs
- initElementDictionary.cs
- ApplicationTrust.cs
- ProtocolProfile.cs
- Win32Native.cs
- ListItemCollection.cs
- TextParentUndoUnit.cs
- GetReadStreamResult.cs
- BinaryObjectReader.cs
- ClipboardProcessor.cs
- ContextMarshalException.cs
- ViewKeyConstraint.cs
- StatusBarDrawItemEvent.cs
- OperationCanceledException.cs
- AuthenticationSection.cs
- AttributeCollection.cs
- SubMenuStyleCollectionEditor.cs
- SafeRegistryHandle.cs
- KnownTypeAttribute.cs
- DataStreamFromComStream.cs
- ObjectSelectorEditor.cs
- FontResourceCache.cs
- PrimitiveXmlSerializers.cs
- MulticastIPAddressInformationCollection.cs
- XhtmlBasicTextViewAdapter.cs
- CommonDialog.cs
- WebUtil.cs
- SqlCommandAsyncResult.cs
- DataServiceKeyAttribute.cs
- panel.cs
- MessagingActivityHelper.cs
- CapabilitiesRule.cs
- StorageEntityContainerMapping.cs
- UserInitiatedNavigationPermission.cs
- CodeObjectCreateExpression.cs
- ConstrainedGroup.cs
- XmlLoader.cs
- Int32CollectionConverter.cs
- TemplatePagerField.cs
- SettingsPropertyNotFoundException.cs
- SimpleLine.cs
- TimeoutConverter.cs
- DataBindEngine.cs
- SQLBytesStorage.cs
- ProfileProvider.cs
- SQLBinary.cs
- RequestCache.cs
- Wizard.cs
- EntityDataSourceState.cs
- UnaryNode.cs
- TemplateBindingExtension.cs
- InputMethodStateTypeInfo.cs
- JobDuplex.cs
- KeyTime.cs
- XsltConvert.cs
- BasicExpressionVisitor.cs
- IdentitySection.cs
- AtomParser.cs
- X509Extension.cs
- ServiceOperationUIEditor.cs
- XPathParser.cs
- Action.cs
- JsonObjectDataContract.cs
- DurableOperationContext.cs
- NegationPusher.cs
- ExpressionEditorAttribute.cs