Code:
/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / System / Windows / Controls / GridViewHeaderRowPresenter.cs / 4 / GridViewHeaderRowPresenter.cs
//---------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System.Collections.Generic; // Listusing System.Collections.Specialized; // NotifyCollectionChangedAction using System.ComponentModel; // DesignerSerializationVisibility using System.Windows.Automation.Peers; // AutomationPeer using System.Windows.Controls.Primitives; // GridViewRowPresenterBase using System.Windows.Data; // Binding using System.Windows.Input; // MouseEventArgs using System.Windows.Media; // SolidColorBrush using System.Diagnostics; using MS.Internal; // Helper namespace System.Windows.Controls { /// /// GridViewHeaderRowPresenter is used within the style to denote the headers place /// in GridView's visual tree /// [StyleTypedProperty(Property = "ColumnHeaderContainerStyle", StyleTargetType = typeof(GridViewColumnHeader))] public class GridViewHeaderRowPresenter : GridViewRowPresenterBase { //------------------------------------------------------------------- // // Constructors // //------------------------------------------------------------------- //-------------------------------------------------------------------- // // Public Methods // //------------------------------------------------------------------- //-------------------------------------------------------------------- // // Public Properties // //-------------------------------------------------------------------- #region Public Properties #region ColumnHeaderContainerStyle ////// ColumnHeaderContainerStyleProperty DependencyProperty /// public static readonly DependencyProperty ColumnHeaderContainerStyleProperty = GridView.ColumnHeaderContainerStyleProperty.AddOwner( typeof(GridViewHeaderRowPresenter), new FrameworkPropertyMetadata( new PropertyChangedCallback(PropertyChanged)) ); ////// header container's style /// public Style ColumnHeaderContainerStyle { get { return (Style)GetValue(ColumnHeaderContainerStyleProperty); } set { SetValue(ColumnHeaderContainerStyleProperty, value); } } #endregion // ColumnHeaderContainerStyle #region ColumnHeaderTemplate ////// ColumnHeaderTemplate DependencyProperty /// public static readonly DependencyProperty ColumnHeaderTemplateProperty = GridView.ColumnHeaderTemplateProperty.AddOwner( typeof(GridViewHeaderRowPresenter), new FrameworkPropertyMetadata( new PropertyChangedCallback(PropertyChanged)) ); ////// column header template /// public DataTemplate ColumnHeaderTemplate { get { return (DataTemplate)GetValue(ColumnHeaderTemplateProperty); } set { SetValue(ColumnHeaderTemplateProperty, value); } } #endregion ColumnHeaderTemplate #region ColumnHeaderTemplateSelector ////// ColumnHeaderTemplateSelector DependencyProperty /// public static readonly DependencyProperty ColumnHeaderTemplateSelectorProperty = GridView.ColumnHeaderTemplateSelectorProperty.AddOwner( typeof(GridViewHeaderRowPresenter), new FrameworkPropertyMetadata( new PropertyChangedCallback(PropertyChanged)) ); ////// header template selector /// ////// This property is ignored if [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public DataTemplateSelector ColumnHeaderTemplateSelector { get { return (DataTemplateSelector)GetValue(ColumnHeaderTemplateSelectorProperty); } set { SetValue(ColumnHeaderTemplateSelectorProperty, value); } } #endregion ColumnHeaderTemplateSelector #region AllowsColumnReorder ///is set. /// /// AllowsColumnReorder DependencyProperty /// public static readonly DependencyProperty AllowsColumnReorderProperty = GridView.AllowsColumnReorderProperty.AddOwner( typeof(GridViewHeaderRowPresenter) ); ////// Allow column re-order or not /// public bool AllowsColumnReorder { get { return (bool)GetValue(AllowsColumnReorderProperty); } set { SetValue(AllowsColumnReorderProperty, value); } } #endregion AllowsColumnReorder #region ColumnHeaderContextMenu ////// ColumnHeaderContextMenu DependencyProperty /// public static readonly DependencyProperty ColumnHeaderContextMenuProperty = GridView.ColumnHeaderContextMenuProperty.AddOwner( typeof(GridViewHeaderRowPresenter), new FrameworkPropertyMetadata( new PropertyChangedCallback(PropertyChanged)) ); ////// ColumnHeaderContextMenu /// public ContextMenu ColumnHeaderContextMenu { get { return (ContextMenu)GetValue(ColumnHeaderContextMenuProperty); } set { SetValue(ColumnHeaderContextMenuProperty, value); } } #endregion ColumnHeaderContextMenu #region ColumnHeaderToolTip ////// ColumnHeaderToolTip DependencyProperty /// public static readonly DependencyProperty ColumnHeaderToolTipProperty = GridView.ColumnHeaderToolTipProperty.AddOwner( typeof(GridViewHeaderRowPresenter), new FrameworkPropertyMetadata( new PropertyChangedCallback(PropertyChanged)) ); ////// ColumnHeaderToolTip /// public object ColumnHeaderToolTip { get { return GetValue(ColumnHeaderToolTipProperty); } set { SetValue(ColumnHeaderToolTipProperty, value); } } private static void PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { GridViewHeaderRowPresenter presenter = (GridViewHeaderRowPresenter)d; if (e.Property == ColumnHeaderTemplateProperty || e.Property == ColumnHeaderTemplateSelectorProperty) { // Check to prevent Template and TemplateSelector at the same time Helper.CheckTemplateAndTemplateSelector("GridViewHeaderRowPresenter", ColumnHeaderTemplateProperty, ColumnHeaderTemplateSelectorProperty, presenter); } presenter.UpdateAllHeaders(e.Property); } #endregion ColumnHeaderToolTip #endregion //------------------------------------------------------------------- // // Protected Methods // //-------------------------------------------------------------------- #region Protected Methods ////// Override of /// Constraint size is an "upper limit" that the return value should not exceed. ///. /// The GridViewHeaderRowPresenter's desired size. protected override Size MeasureOverride(Size constraint) { GridViewColumnCollection columns = Columns; UIElementCollection children = InternalChildren; double maxHeight = 0.0; // Max height of children. double accumulatedWidth = 0.0; // Total width consumed by children. double constraintHeight = constraint.Height; bool desiredWidthListEnsured = false; if (columns != null) { // Measure working headers for (int i = 0; i < columns.Count; ++i) { UIElement child = children[GetVisualIndex(i)]; if (child == null) { continue; } double childConstraintWidth = Math.Max(0.0, constraint.Width - accumulatedWidth); GridViewColumn column = columns[i]; if (column.State == ColumnMeasureState.Init) { if (!desiredWidthListEnsured) { EnsureDesiredWidthList(); LayoutUpdated += new EventHandler(OnLayoutUpdated); desiredWidthListEnsured = true; } child.Measure(new Size(childConstraintWidth, constraintHeight)); DesiredWidthList[column.ActualIndex] = column.EnsureWidth(child.DesiredSize.Width); accumulatedWidth += column.DesiredWidth; } else if (column.State == ColumnMeasureState.Headered || column.State == ColumnMeasureState.Data) { childConstraintWidth = Math.Min(childConstraintWidth, column.DesiredWidth); child.Measure(new Size(childConstraintWidth, constraintHeight)); accumulatedWidth += column.DesiredWidth; } else // ColumnMeasureState.SpecificWidth { childConstraintWidth = Math.Min(childConstraintWidth, column.Width); child.Measure(new Size(childConstraintWidth, constraintHeight)); accumulatedWidth += column.Width; } maxHeight = Math.Max(maxHeight, child.DesiredSize.Height); } } // Measure padding header Debug.Assert(_paddingHeader != null, "padding header is null"); _paddingHeader.Measure(new Size(0.0, constraintHeight)); maxHeight = Math.Max(maxHeight, _paddingHeader.DesiredSize.Height); // reserve space for padding header next to the last column accumulatedWidth += c_PaddingHeaderMinWidth; // Measure indicator & floating header in re-ordering if (_isHeaderDragging) { Debug.Assert(_indicator != null, "_indicator is null"); Debug.Assert(_floatingHeader != null, "_floatingHeader is null"); // Measure indicator _indicator.Measure(constraint); // Measure floating header _floatingHeader.Measure(constraint); } return (new Size(accumulatedWidth, maxHeight)); } ////// GridViewHeaderRowPresenter computes the position of its children inside each child's Margin and calls Arrange /// on each child. /// /// Size the GridViewHeaderRowPresenter will assume. protected override Size ArrangeOverride(Size arrangeSize) { GridViewColumnCollection columns = Columns; UIElementCollection children = InternalChildren; double accumulatedWidth = 0.0; double remainingWidth = arrangeSize.Width; Rect rect; HeadersPositionList.Clear(); if (columns != null) { // Arrange working headers for (int i = 0; i < columns.Count; ++i) { UIElement child = children[GetVisualIndex(i)]; if (child == null) { continue; } GridViewColumn column = columns[i]; // has a given value or 'auto' double childArrangeWidth = Math.Min(remainingWidth, ((column.State == ColumnMeasureState.SpecificWidth) ? column.Width : column.DesiredWidth)); // calculate the header rect rect = new Rect(accumulatedWidth, 0.0, childArrangeWidth, arrangeSize.Height); // arrange header child.Arrange(rect); //Store rect in HeadersPositionList as i-th column position HeadersPositionList.Add(rect); remainingWidth -= childArrangeWidth; accumulatedWidth += childArrangeWidth; } // check width to hide previous header's right half gripper, from the first working header to padding header // only happens after column delete, insert, move if (_isColumnChangedOrCreated) { for (int i = 0; i < columns.Count; ++i) { GridViewColumnHeader header = children[GetVisualIndex(i)] as GridViewColumnHeader; header.CheckWidthForPreviousHeaderGripper(); } _paddingHeader.CheckWidthForPreviousHeaderGripper(); _isColumnChangedOrCreated = false; } } // Arrange padding header Debug.Assert(_paddingHeader != null, "padding header is null"); rect = new Rect(accumulatedWidth, 0.0, Math.Max(remainingWidth, 0.0), arrangeSize.Height); _paddingHeader.Arrange(rect); HeadersPositionList.Add(rect); // if re-order started, arrange floating header & indicator if (_isHeaderDragging) { _floatingHeader.Arrange(new Rect(new Point(_currentPos.X - _relativeStartPos.X, 0), HeadersPositionList[_startColumnIndex].Size)); Point pos = FindPositionByIndex(_desColumnIndex); _indicator.Arrange(new Rect(pos, new Size(_indicator.DesiredSize.Width, arrangeSize.Height))); } return arrangeSize; } ////// This is the method that responds to the MouseButtonEvent event. /// /// Event arguments protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { GridViewColumnHeader header = e.Source as GridViewColumnHeader; if (header != null && AllowsColumnReorder) { PrepareHeaderDrag(header, e.GetPosition(this), e.GetPosition(header), false); MakeParentItemsControlGotFocus(); } e.Handled = true; base.OnMouseLeftButtonDown(e); } ////// This is the method that responds to the MouseButtonEvent event. /// /// Event arguments protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) { // Important to clean the prepare dragging state _prepareDragging = false; if (_isHeaderDragging) { FinishHeaderDrag(false); } e.Handled = true; base.OnMouseLeftButtonUp(e); } ////// This is the method that responds to the MouseEvent event. /// /// Event arguments protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); if (e.LeftButton == MouseButtonState.Pressed) { // if prepare re-order or already re-order if (_prepareDragging) { Debug.Assert(_draggingSrcHeader != null, "_draggingSrcHeader is null"); _currentPos = e.GetPosition(this); _desColumnIndex = FindIndexByPosition(_currentPos, true); if (!_isHeaderDragging) { // Re-order begins only if horizontal move exceeds threshold if (CheckStartHeaderDrag(_currentPos, _startPos)) { // header dragging start StartHeaderDrag(); // need to measure indicator because floating header is updated InvalidateMeasure(); } } else // NOTE: Not-Dragging/Dragging should be divided into two stages in MouseMove { // Check floating & indicator visibility // Display floating header if vertical move not exceeds header.Height * 2 bool isDisplayingFloatingHeader = IsMousePositionValid(_floatingHeader, _currentPos, 2.0); // floating header and indicator are visibile/invisible at the same time _indicator.Visibility = _floatingHeader.Visibility = isDisplayingFloatingHeader ? Visibility.Visible : Visibility.Hidden; InvalidateArrange(); } } } e.Handled = true; } ////// Cancel header dragging if we lost capture /// /// protected override void OnLostMouseCapture(MouseEventArgs e) { base.OnLostMouseCapture(e); // OnLostMouseCapture is invoked before OnMouseLeftButtonUp, so we need to distinguish // the cause of capture lose // if LeftButton is pressed when lost mouse capture, we treat it as cancel // Because GridViewHeaderRowPresenter never capture Mouse (GridViewColumnHeader did this), // the Mouse.Captured is always null if (e.LeftButton == MouseButtonState.Pressed && _isHeaderDragging) { FinishHeaderDrag(true); } // Important to clean the prepare dragging state _prepareDragging = false; } #endregion Protected Methods //------------------------------------------------------------------- // // Internal Methods // //------------------------------------------------------------------- #region Internal Methods ////// create the headers tree /// internal override void OnPreApplyTemplate() { // +-- GridViewHeaderRowPresenter ----------------------------+ // | | // | +- Header1 ---+ +- Header2 ---+ +- PaddingHeader -+ | // | | +--------+ +--------+ | | // | | +Gripper + +Gripper + | ... | // | | +--------+ +--------+ | | // | +-------------+ +-------------+ +---------------+ | // +---------------------------------------------------------+ // // Given a column collection with 3 columns // // { A, B, C }, // // Visually, GridViewHeaderRowPresenter will them present as: // // { A, B, C, Padding } // // If in Reorder-Mode, there will be 2 more be visible: // // { A, [Indctr], B, C, Padding } // { [-- Float --] } // // And internally the visual child collection is in order of: // // { Padding, C, B, A, Indicator, Float} // // Method GetVisualIndex() is for coverting a Columns based // index to visual child collection based index. // // E.g., Columns based index of column A is 0, while // and it lives 3rd in the visual collection, so // // GetVisualIndex(0) = 3. // base.OnPreApplyTemplate(); if (NeedUpdateVisualTree) { // build the whole collection from draft. // IMPORTANT! // The correct sequence to build the VisualTree in Z-order: // 1. Padding header // 2. The working Column header (if any) // 3. Indicator // 4. Floating header // UIElementCollection children = InternalChildren; GridViewColumnCollection columns = Columns; // renew ScrollViewer, ScrollChanged event, ItemsControl and KeyDown event RenewEvents(); if (children.Count == 0) { // Build and add the padding header, even if no GridViewColumn is defined AddPaddingColumnHeader(); // Create and add indicator AddIndicator(); // Create and add floating header AddFloatingHeader(null); } else if (children.Count > 3) { // clear column headers left from last view. int count = children.Count - 3; for (int i = 0; i < count; i++) { RemoveHeader(null, 1); } } UpdatePaddingHeader(_paddingHeader); // // Build the column header. // The interesting thing is headers must be built from right to left, // in order to make the left header's gripper overlay the right header // if (columns != null) { int visualIndex = 1; for (int columnIndex = columns.Count - 1; columnIndex >= 0; columnIndex--) { GridViewColumn column = columns[columnIndex]; GridViewColumnHeader header = CreateAndInsertHeader(column, visualIndex++); } } // Link headers BuildHeaderLinks(); NeedUpdateVisualTree = false; _isColumnChangedOrCreated = true; } } ////// Override column's PropertyChanged event handler. Update correspondent /// property if change is of Width / Header / /// HeaderContainerStyle / Template / Selector. /// internal override void OnColumnPropertyChanged(GridViewColumn column, string propertyName) { Debug.Assert(column != null); if (column.ActualIndex >= 0) { GridViewColumnHeader header = FindHeaderByColumn(column); if (header != null) { if (GridViewColumn.WidthProperty.Name.Equals(propertyName) || GridViewColumn.c_ActualWidthName.Equals(propertyName)) { InvalidateMeasure(); } else if (GridViewColumn.HeaderProperty.Name.Equals(propertyName)) { if (!header.IsInternalGenerated /* the old header is its own container */ || column.Header is GridViewColumnHeader /* the new header is its own container */) { // keep the header index in Children collection int i = InternalChildren.IndexOf(header); // Remove the old header RemoveHeader(header, -1); // Insert a (the) new header GridViewColumnHeader newHeader = CreateAndInsertHeader(column, i); // Link headers BuildHeaderLinks(); } else { UpdateHeaderContent(header); } } else { DependencyProperty columnDP = GetColumnDPFromName(propertyName); if (columnDP != null) { UpdateHeaderProperty(header, columnDP); } } } } } internal override void OnColumnCollectionChanged(GridViewColumnCollectionChangedEventArgs e) { base.OnColumnCollectionChanged(e); int index; GridViewColumnHeader header; UIElementCollection children = InternalChildren; GridViewColumn column; switch (e.Action) { case NotifyCollectionChangedAction.Move: int start = GetVisualIndex(e.OldStartingIndex); int end = GetVisualIndex(e.NewStartingIndex); header = (GridViewColumnHeader)children[start]; children.RemoveAt(start); children.InsertInternal(end, header); break; case NotifyCollectionChangedAction.Add: index = GetVisualIndex(e.NewStartingIndex); column = (GridViewColumn)(e.NewItems[0]); CreateAndInsertHeader(column, index + 1); // index + 1 because visual index is reversed from column index break; case NotifyCollectionChangedAction.Remove: RemoveHeader(null, GetVisualIndex(e.OldStartingIndex)); break; case NotifyCollectionChangedAction.Replace: index = GetVisualIndex(e.OldStartingIndex); RemoveHeader(null, index); column = (GridViewColumn)(e.NewItems[0]); CreateAndInsertHeader(column, index); break; case NotifyCollectionChangedAction.Reset: int count = e.ClearedColumns.Count; for (int i = 0; i < count; i++) { RemoveHeader(null, 1); } break; } // Link headers BuildHeaderLinks(); _isColumnChangedOrCreated = true; } // Make the parent got focus if it's ItemsControl // make this method internal, so GVCH can call it when header is invoked through access key internal void MakeParentItemsControlGotFocus() { if (_itemsControl != null && !_itemsControl.IsKeyboardFocusWithin) { // send focus to item. ListBox parent = _itemsControl as ListBox; if (parent != null && parent.LastActionItem != null) { parent.LastActionItem.Focus(); } else { _itemsControl.Focus(); } } } ////// Refresh a dp of a header. Column is the 1st priority source, GridView 2nd. /// /// the header to update /// the DP which trigger this update internal void UpdateHeaderProperty(GridViewColumnHeader header, DependencyProperty property) { DependencyProperty gvDP, columnDP, headerDP; GetMatchingDPs(property, out gvDP, out columnDP, out headerDP); UpdateHeaderProperty(header, headerDP, columnDP, gvDP); } #endregion Internal Methods //-------------------------------------------------------------------- // // Accessibility // //------------------------------------------------------------------- #region Accessibility ////// Creates AutomationPeer ( protected override AutomationPeer OnCreateAutomationPeer() { return new GridViewHeaderRowPresenterAutomationPeer(this); } #endregion //-------------------------------------------------------------------- // // Private Methods // //-------------------------------------------------------------------- #region Private Methods private void OnLayoutUpdated(object sender, EventArgs e) { bool desiredWidthChanged = false; // whether the shared minimum width has been changed since last layout foreach (GridViewColumn column in Columns) { if ((column.State != ColumnMeasureState.SpecificWidth)) { if (column.State == ColumnMeasureState.Init) { column.State = ColumnMeasureState.Headered; } if (DesiredWidthList == null || column.ActualIndex >= DesiredWidthList.Count) { // How can this happen? // Between the last measure was called and this update is called, there can be a // change done to the ColumnCollection and result in DesiredWidthList out of sync // with the columnn collection. What can we do is end this call asap and the next // measure will fix it. desiredWidthChanged = true; break; } if (!DoubleUtil.AreClose(column.DesiredWidth, DesiredWidthList[column.ActualIndex])) { // Update the record because collection operation latter on might // need to verified this list again, e.g. insert an 'auto' // column, so that we won't trigger unnecessary update due to // inconsistency of this column. DesiredWidthList[column.ActualIndex] = column.DesiredWidth; desiredWidthChanged = true; } } } if (desiredWidthChanged) { InvalidateMeasure(); } LayoutUpdated -= new EventHandler(OnLayoutUpdated); } // Map column collection index to header collection index in visual tree private int GetVisualIndex(int columnIndex) { // Elements in visual tree: working headers, padding header, indicator, and floating header int index = InternalChildren.Count - 3 - columnIndex; Debug.Assert(index >= 0 && index < InternalChildren.Count, "Error index when GetVisualIndex"); return index; } // Link headers from right to left private void BuildHeaderLinks() { GridViewColumnHeader lastHeader = null; if (Columns != null) { // link working headers. for (int i = 0; i < Columns.Count; i++) { GridViewColumnHeader header = (GridViewColumnHeader)InternalChildren[GetVisualIndex(i)]; header.PreviousVisualHeader = lastHeader; lastHeader = header; } } // link padding header to last header if (_paddingHeader != null) { _paddingHeader.PreviousVisualHeader = lastHeader; } } // // This method will do following tasks: // 1. Disconnect header from visual parent and logical parent, if any. // 2. Create a new header or use the header directly if the GridViewColumn.Header property // is qualify for its own container; // 3. Insert the header in the InternalChildren collection // 4. Perform routine update and hookup jobs // private GridViewColumnHeader CreateAndInsertHeader(GridViewColumn column, int index) { object header = column.Header; GridViewColumnHeader headerContainer = header as GridViewColumnHeader; // // NOTE: when theme chagned, all properties, templates and styles will be reevaluated. // But there are 2 cases we need to handle: // // 1: header property is a qualified container for itself //) /// // // // 2: header property is a Visual element //// // // // // In both cases, we need to diconnect them from the visual and logical tree before // they got inserted into the new tree. // if (header != null) { DependencyObject d = header as DependencyObject; if (d != null) { // disconnect from visual tree Visual headerAsVisual = d as Visual; if (headerAsVisual != null) { Visual parent = VisualTreeHelper.GetParent(headerAsVisual) as Visual; if (parent != null) { if (headerContainer != null) { // case 1 GridViewHeaderRowPresenter parentAsGVHRP = parent as GridViewHeaderRowPresenter; if (parentAsGVHRP != null) { parentAsGVHRP.InternalChildren.RemoveNoVerify(headerContainer); } else { Debug.Assert(false, "Head is container for itself, but parent is neither GridViewHeaderRowPresenter nor null."); } } else { // case 2 GridViewColumnHeader parentAsGVCH = parent as GridViewColumnHeader; if (parentAsGVCH != null) { parentAsGVCH.ClearValue(ContentControl.ContentProperty); } } } } // disconnect from logical tree DependencyObject logicalParent = LogicalTreeHelper.GetParent(d); if (logicalParent != null) { LogicalTreeHelper.RemoveLogicalChild(logicalParent, header); } } } if (headerContainer == null) { headerContainer = new GridViewColumnHeader(); headerContainer.IsInternalGenerated = true; } // Pass column reference to GridViewColumnHeader headerContainer.SetValue(GridViewColumnHeader.ColumnPropertyKey, column); // Hookup _itemsControl.KeyDown event for canceling resizing if user press 'Esc'. HookupItemsControlKeyboardEvent(headerContainer); InternalChildren.InsertInternal(index, headerContainer); // NOTE: the order here is important! // Need to add headerContainer into visual tree first, then apply Style, Template etc. UpdateHeader(headerContainer); _gvHeadersValid = false; return headerContainer; } private void RemoveHeader(GridViewColumnHeader header, int index) { Debug.Assert(header != null || index != -1); _gvHeadersValid = false; if (header != null) { InternalChildren.Remove(header); } else { header = (GridViewColumnHeader)InternalChildren[index]; InternalChildren.RemoveAt(index); } UnhookItemsControlKeyboardEvent(header); } // find needed elements and hook up events private void RenewEvents() { ScrollViewer oldHeaderSV = _headerSV; _headerSV = Parent as ScrollViewer; if (oldHeaderSV != _headerSV) { if (oldHeaderSV != null) { oldHeaderSV.ScrollChanged -= new ScrollChangedEventHandler(OnHeaderScrollChanged); } if (_headerSV != null) { _headerSV.ScrollChanged += new ScrollChangedEventHandler(OnHeaderScrollChanged); } } ScrollViewer oldSV = _mainSV; // backup the old value _mainSV = TemplatedParent as ScrollViewer; if (oldSV != _mainSV) { if (oldSV != null) { oldSV.ScrollChanged -= new ScrollChangedEventHandler(OnMasterScrollChanged); } if (_mainSV != null) { _mainSV.ScrollChanged += new ScrollChangedEventHandler(OnMasterScrollChanged); } } // hook up key down event from ItemsControl, // because GridViewColumnHeader and GridViewHeaderRowPresenter can not get focus ItemsControl oldIC = _itemsControl; // backup the old value _itemsControl = FindItemsControlThroughTemplatedParent(this); if (oldIC != _itemsControl) { if (oldIC != null) { // NOTE: headers have unhooked the KeyDown event in RemoveHeader. oldIC.KeyDown -= new KeyEventHandler(OnColumnHeadersPresenterKeyDown); } if (_itemsControl != null) { // register to HeadersPresenter to cancel dragging _itemsControl.KeyDown += new KeyEventHandler(OnColumnHeadersPresenterKeyDown); // NOTE: headers will hookup the KeyDown event latter in CreateAndInsertHeader. } } //Set GridViewHeaderRowPresenter to ListView // ListView lv = _itemsControl as ListView; if (lv != null && lv.View != null && lv.View is GridView) { ((GridView)lv.View).HeaderRowPresenter = this; } } private void UnhookItemsControlKeyboardEvent(GridViewColumnHeader header) { Debug.Assert(header != null); if (_itemsControl != null) { _itemsControl.KeyDown -= new KeyEventHandler(header.OnColumnHeaderKeyDown); } } private void HookupItemsControlKeyboardEvent(GridViewColumnHeader header) { Debug.Assert(header != null); if (_itemsControl != null) { _itemsControl.KeyDown += new KeyEventHandler(header.OnColumnHeaderKeyDown); } } // The following two scroll changed methods will not be called recursively and lead to dead loop. // When scrolling _masterSV, OnMasterScrollChanged will be called, so _headerSV also scrolled // to the same offset. Then, OnHeaderScrollChanged be called, and try to scroll _masterSV, but // it's already scrolled to that offset, so OnMasterScrollChanged will not be called. // When master scroll viewer changed its offset, change header scroll viewer accordingly private void OnMasterScrollChanged(object sender, ScrollChangedEventArgs e) { if (_headerSV != null && _mainSV == e.OriginalSource) { _headerSV.ScrollToHorizontalOffset(e.HorizontalOffset); } } // When header scroll viewer changed its offset, change master scroll viewer accordingly private void OnHeaderScrollChanged(object sender, ScrollChangedEventArgs e) { if (_mainSV != null && _headerSV == e.OriginalSource) { _mainSV.ScrollToHorizontalOffset(e.HorizontalOffset); } } // Create the last padding column header in GridViewHeaderRowPresenter private void AddPaddingColumnHeader() { GridViewColumnHeader paddingHeader = new GridViewColumnHeader(); paddingHeader.IsInternalGenerated = true; paddingHeader.SetValue(GridViewColumnHeader.RolePropertyKey, GridViewColumnHeaderRole.Padding); paddingHeader.Content = null; paddingHeader.ContentTemplate = null; paddingHeader.ContentTemplateSelector = null; paddingHeader.MinWidth = 0; paddingHeader.Padding = new Thickness(0.0); paddingHeader.Width = Double.NaN; paddingHeader.HorizontalAlignment = HorizontalAlignment.Stretch; InternalChildren.AddInternal(paddingHeader); _paddingHeader = paddingHeader; } // Create the indicator for column re-ordering private void AddIndicator() { Separator indicator = new Separator(); indicator.Visibility = Visibility.Hidden; // Indicator style: // //// // // indicator.Margin = new Thickness(0); indicator.Width = 2.0; FrameworkElementFactory border = new FrameworkElementFactory(typeof(Border)); border.SetValue(Border.BackgroundProperty, new SolidColorBrush(Color.FromUInt32(0xFF000080))); ControlTemplate template = new ControlTemplate(typeof(Separator)); template.VisualTree = border; template.Seal(); indicator.Template = template; InternalChildren.AddInternal(indicator); _indicator = indicator; } // Create the floating header private void AddFloatingHeader(GridViewColumnHeader srcHeader) { GridViewColumnHeader header; // #1426973: Because users may put subclassed header as the column header, // we need to create the same type per source header as the floating one // Get source header's type Type headerType = (srcHeader != null ? srcHeader.GetType() : typeof(GridViewColumnHeader)); try { // Instantiate the same type for floating header header = Activator.CreateInstance(headerType) as GridViewColumnHeader; } catch (MissingMethodException e) { throw new ArgumentException(SR.Get(SRID.ListView_MissingParameterlessConstructor, headerType), e); } Debug.Assert(header != null, "Cannot instantiate GridViewColumnHeader in AddFloatingHeader"); header.IsInternalGenerated = true; header.SetValue(GridViewColumnHeader.RolePropertyKey, GridViewColumnHeaderRole.Floating); header.Visibility = Visibility.Hidden; InternalChildren.AddInternal(header); _floatingHeader = header; } // Fill necessary properties in floating header private void UpdateFloatingHeader(GridViewColumnHeader srcHeader) { Debug.Assert(srcHeader != null, "srcHeader is null"); Debug.Assert(_floatingHeader != null, "floating header is null"); _floatingHeader.Style = srcHeader.Style; _floatingHeader.FloatSourceHeader = srcHeader; _floatingHeader.Width = srcHeader.ActualWidth; _floatingHeader.Height = srcHeader.ActualHeight; _floatingHeader.SetValue(GridViewColumnHeader.ColumnPropertyKey, srcHeader.Column); _floatingHeader.Visibility = Visibility.Hidden; // override floating header's MinWidth/MinHeight to disable users to change floating header's Width/Height _floatingHeader.MinWidth = srcHeader.MinWidth; _floatingHeader.MinHeight = srcHeader.MinHeight; object template = srcHeader.ReadLocalValue(GridViewColumnHeader.ContentTemplateProperty); if ((template != DependencyProperty.UnsetValue) && (template != null)) { _floatingHeader.ContentTemplate = srcHeader.ContentTemplate; } object selector = srcHeader.ReadLocalValue(GridViewColumnHeader.ContentTemplateSelectorProperty); if ((selector != DependencyProperty.UnsetValue) && (selector != null)) { _floatingHeader.ContentTemplateSelector = srcHeader.ContentTemplateSelector; } if (!(srcHeader.Content is Visual)) { _floatingHeader.Content = srcHeader.Content; } } ///// //// //// /// This method is invoked when mouse move and left button is pressed. /// This method judges if the mouse move exceeds a threshold to start re-order. /// /// The current mouse position, relative to GridViewHeaderRowPresenter /// The original start position, relative to GridViewHeaderRowPresenter ///private bool CheckStartHeaderDrag(Point currentPos, Point originalPos) { return (DoubleUtil.GreaterThan(Math.Abs(currentPos.X - originalPos.X), c_thresholdX)); } // Find ItemsControl through TemplatedParent private static ItemsControl FindItemsControlThroughTemplatedParent(GridViewHeaderRowPresenter presenter) { FrameworkElement fe = presenter.TemplatedParent as FrameworkElement; ItemsControl itemsControl = null; while (fe != null) { itemsControl = fe as ItemsControl; if (itemsControl != null) { break; } fe = fe.TemplatedParent as FrameworkElement; } return itemsControl; } private void OnColumnHeadersPresenterKeyDown(object sender, KeyEventArgs e) { // if press Escape when re-ordering, cancel re-order if (e.Key == Key.Escape && _isHeaderDragging) { // save the source header b/c FinishHeaderDrag will clear it GridViewColumnHeader srcHeader = _draggingSrcHeader; FinishHeaderDrag(true); PrepareHeaderDrag(srcHeader, _currentPos, _relativeStartPos, true); InvalidateArrange(); } } // Find the column header from the visual tree by column private GridViewColumnHeader FindHeaderByColumn(GridViewColumn column) { GridViewColumnCollection columns = Columns; UIElementCollection children = InternalChildren; if (columns != null && children.Count > columns.Count) { int index = columns.IndexOf(column); if (index != -1) { // Becuase column headers is generated from right to left int visualIndex = GetVisualIndex(index); GridViewColumnHeader header = children[visualIndex] as GridViewColumnHeader; if (header.Column != column) { // NOTE: if user change the GridViewColumn.Header/HeaderStyle // in the event handler of column move. And in such case, the header // we found by the algorithm above will be fail. So we turn to below // a more reliable one. for (int i = 1; i < children.Count; i++) { header = children[i] as GridViewColumnHeader; if (header != null && header.Column == column) { return header; } } } else { return header; } } } return null; } // Find logic column index by position // If parameter 'findNearestColumn' is true, find the nearest column relative to mouse position private int FindIndexByPosition(Point startPos, bool findNearestColumn) { int index = -1; if (startPos.X < 0.0) { return 0; } for (int i = 0; i < HeadersPositionList.Count; i++) { index++; Rect rect = HeadersPositionList[i]; double startX = rect.X; double endX = startX + rect.Width; if (DoubleUtil.GreaterThanOrClose(startPos.X, startX) && DoubleUtil.LessThanOrClose(startPos.X, endX)) { if (findNearestColumn) { double midX = (startX + endX) * 0.5; if (DoubleUtil.GreaterThanOrClose(startPos.X, midX)) { // if not the padding header if (i != HeadersPositionList.Count - 1) { index++; } } } break; } } return index; } // Find position by logic column index private Point FindPositionByIndex(int index) { Debug.Assert(index >= 0 && index < HeadersPositionList.Count, "wrong index"); return new Point(HeadersPositionList[index].X, 0); } // Update header Content, Style, ContentTemplate and ContentTemplateSelector, ContextMenu and ToolTip private void UpdateHeader(GridViewColumnHeader header) { UpdateHeaderContent(header); for (int i = 0; i < 5; i++) { UpdateHeaderProperty(header, s_DPList[2][i] /* header */, s_DPList[1][i] /* column */, s_DPList[0][i] /* GV */); } } // Update Content of GridViewColumnHeader private void UpdateHeaderContent(GridViewColumnHeader header) { if (header != null && header.IsInternalGenerated) { GridViewColumn column = header.Column; if (column != null) { if (column.Header == null) { header.ClearValue(ContentControl.ContentProperty); } else { header.Content = column.Header; } } } } // Update Style, ContextMenu and ToolTip properties for padding header. // GridView.ColumnHeaderTemplate(Selector) don't work on padding header. private void UpdatePaddingHeader(GridViewColumnHeader header) { UpdateHeaderProperty(header, ColumnHeaderContainerStyleProperty); UpdateHeaderProperty(header, ColumnHeaderContextMenuProperty); UpdateHeaderProperty(header, ColumnHeaderToolTipProperty); } private void UpdateAllHeaders(DependencyProperty dp) { DependencyProperty gvDP, columnDP, headerDP; GetMatchingDPs(dp, out gvDP, out columnDP, out headerDP); int iStart, iEnd; GetIndexRange(dp, out iStart, out iEnd); UIElementCollection children = InternalChildren; for (int i = iStart; i <= iEnd; i++) { GridViewColumnHeader header = children[i] as GridViewColumnHeader; if (header != null) { UpdateHeaderProperty(header, headerDP, columnDP, gvDP); } } } /// /// get index range of header for need update /// /// the GridView DP in this update /// starting index of header need update /// ending index of header need update private void GetIndexRange(DependencyProperty dp, out int iStart, out int iEnd) { // whether or not include the padding header iStart = (dp == ColumnHeaderTemplateProperty || dp == ColumnHeaderTemplateSelectorProperty) ? 1 : 0; iEnd = InternalChildren.Count - 3; // skip the floating header and the indicator. } private void UpdateHeaderProperty( GridViewColumnHeader header, // the header need to update DependencyProperty targetDP, // the target DP on header DependencyProperty columnDP, // the DP on Column as 1st source, can be Null DependencyProperty gvDP // the DP on GridView as 2nd source ) { if (gvDP == ColumnHeaderContainerStyleProperty && header.Role == GridViewColumnHeaderRole.Padding) { // Because padding header has no chance to be instantiated by a sub-classed GridViewColumnHeader, // we ignore GridView.ColumnHeaderContainerStyle silently if its TargetType is not GridViewColumnHeader (or parent) // I.e. for padding header, only accept the GridViewColumnHeader as TargetType Style style = ColumnHeaderContainerStyle; if (style != null && !style.TargetType.IsAssignableFrom(typeof(GridViewColumnHeader))) { // use default style for padding header in this case header.Style = null; return; } } GridViewColumn column = header.Column; object value = null; if (column != null /* not the padding one */ && columnDP != null /* Column doesn't has ContextMenu property*/) { value = column.GetValue(columnDP); } if (value == null) { value = this.GetValue(gvDP); } header.UpdateProperty(targetDP, value); } // Prepare column header re-ordering private void PrepareHeaderDrag(GridViewColumnHeader header, Point pos, Point relativePos, bool cancelInvoke) { if (header.Role == GridViewColumnHeaderRole.Normal) { _prepareDragging = true; _isHeaderDragging = false; _draggingSrcHeader = header; _startPos = pos; _relativeStartPos = relativePos; if (!cancelInvoke) { _startColumnIndex = FindIndexByPosition(_startPos, false); } } } // Start header drag private void StartHeaderDrag() { _startPos = _currentPos; _isHeaderDragging = true; // suppress src header's click event _draggingSrcHeader.SuppressClickEvent = true; // lock Columns during header dragging if (Columns != null) { Columns.BlockWrite(); } // Remove the old floating header, // then create & add the new one per the source header's type InternalChildren.Remove(_floatingHeader); AddFloatingHeader(_draggingSrcHeader); UpdateFloatingHeader(_draggingSrcHeader); } // Finish header drag private void FinishHeaderDrag(bool isCancel) { // clear related fields _prepareDragging = false; _isHeaderDragging = false; // restore src header's click event _draggingSrcHeader.SuppressClickEvent = false; _floatingHeader.Visibility = Visibility.Hidden; _floatingHeader.ResetFloatingHeaderCanvasBackground(); _indicator.Visibility = Visibility.Hidden; // unlock Columns during header dragging if (Columns != null) { Columns.UnblockWrite(); } // if cancelled, do nothing if (!isCancel) { // Display floating header if vertical move not exceeds header.Height * 2 bool isMoveHeader = IsMousePositionValid(_floatingHeader, _currentPos, 2.0); Debug.Assert(Columns != null, "Columns is null in OnHeaderDragCompleted"); // Revise the destinate column index int newColumnIndex = (_startColumnIndex >= _desColumnIndex) ? _desColumnIndex : _desColumnIndex - 1; if (isMoveHeader) { Columns.Move(_startColumnIndex, newColumnIndex); } } } // check if the Mouse position is in the given valid area private static bool IsMousePositionValid(FrameworkElement floatingHeader, Point currentPos, double arrange) { // valid area: - height * arrange <= currentPos.Y <= height * ( arrange + 1) return DoubleUtil.LessThanOrClose(-floatingHeader.Height * arrange, currentPos.Y) && DoubleUtil.LessThanOrClose(currentPos.Y, floatingHeader.Height * (arrange + 1)); } #endregion Private Methods //------------------------------------------------------------------- // // Private Class / Properties / Fields // //-------------------------------------------------------------------- #region Private Class / Properties / Fields //Return the actual column headers array internal ListActualColumnHeaders { get { if (_gvHeaders == null || !_gvHeadersValid) { _gvHeadersValid = true; _gvHeaders = new List (); if (Columns != null) { UIElementCollection children = InternalChildren; for (int i = 0, count = Columns.Count; i < count; ++i) { GridViewColumnHeader header = children[GetVisualIndex(i)] as GridViewColumnHeader; if (header != null) { _gvHeaders.Add(header); } } } } return _gvHeaders; } } private bool _gvHeadersValid; private List _gvHeaders; // Store the column header's position in visual tree // including the padding header private List HeadersPositionList { get { if (_headersPositionList == null) { _headersPositionList = new List (); } return _headersPositionList; } } private List _headersPositionList; private ScrollViewer _mainSV; private ScrollViewer _headerSV; private GridViewColumnHeader _paddingHeader; private GridViewColumnHeader _floatingHeader; private Separator _indicator; // parent ItemsControl private ItemsControl _itemsControl; // source header when header dragging private GridViewColumnHeader _draggingSrcHeader; // start position when dragging (position relative to GridViewHeaderRowPresenter) private Point _startPos; // relative start position when dragging (position relative to Header) private Point _relativeStartPos; // current mouse position (position relative to GridViewHeaderRowPresenter) private Point _currentPos; // start column index when begin dragging private int _startColumnIndex; // destination column index when finish dragging private int _desColumnIndex; // indicating if header is dragging private bool _isHeaderDragging; // indicating column is changed or created for the first private bool _isColumnChangedOrCreated; // indicating a mouse down, ready to drag the header private bool _prepareDragging; // the threshold for horizontal move when header dragging private const double c_thresholdX = 4.0; #endregion Private Properties #region DP resolve helper // resolve dp from name private static DependencyProperty GetColumnDPFromName(string dpName) { foreach (DependencyProperty dp in s_DPList[1]) { if ((dp != null) && dpName.Equals(dp.Name)) { return dp; } } return null; } // resolve matching DPs from one private static void GetMatchingDPs(DependencyProperty indexDP, out DependencyProperty gvDP, out DependencyProperty columnDP, out DependencyProperty headerDP) { for (int i = 0; i < s_DPList.Length; i++) { for (int j = 0; j < s_DPList[i].Length; j++) { if (indexDP == s_DPList[i][j]) { gvDP = s_DPList[0][j]; columnDP = s_DPList[1][j]; headerDP = s_DPList[2][j]; goto found; } } } gvDP = columnDP = headerDP = null; found: ; } private static readonly DependencyProperty[][] s_DPList = new DependencyProperty[][] { // DPs on GridViewHeaderRowPresenter new DependencyProperty[] { ColumnHeaderContainerStyleProperty, ColumnHeaderTemplateProperty, ColumnHeaderTemplateSelectorProperty, ColumnHeaderContextMenuProperty, ColumnHeaderToolTipProperty, }, // DPs on GridViewColumn new DependencyProperty[] { GridViewColumn.HeaderContainerStyleProperty, GridViewColumn.HeaderTemplateProperty, GridViewColumn.HeaderTemplateSelectorProperty, null, null, }, // DPs on GridViewColumnHeader new DependencyProperty[] { GridViewColumnHeader.StyleProperty, GridViewColumnHeader.ContentTemplateProperty, GridViewColumnHeader.ContentTemplateSelectorProperty, GridViewColumnHeader.ContextMenuProperty, GridViewColumnHeader.ToolTipProperty, } }; #endregion } } // 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
- HttpClientCertificate.cs
- PerformanceCounterNameAttribute.cs
- ProviderException.cs
- ObservableCollectionDefaultValueFactory.cs
- VisualCollection.cs
- MetabaseServerConfig.cs
- DuplexClientBase.cs
- GradientSpreadMethodValidation.cs
- AesCryptoServiceProvider.cs
- PartialCachingControl.cs
- PersonalizableAttribute.cs
- SafeWaitHandle.cs
- OleDbErrorCollection.cs
- InternalSafeNativeMethods.cs
- SoapIncludeAttribute.cs
- MouseEvent.cs
- ExpressionBuilderContext.cs
- SizeValueSerializer.cs
- CachedCompositeFamily.cs
- CollectionsUtil.cs
- PrintPreviewGraphics.cs
- ResourceAssociationTypeEnd.cs
- AddressHeaderCollectionElement.cs
- _WebProxyDataBuilder.cs
- DataSourceSelectArguments.cs
- JobInputBins.cs
- HttpPostClientProtocol.cs
- AsymmetricSignatureFormatter.cs
- WeakReferenceEnumerator.cs
- MediaElementAutomationPeer.cs
- ColorAnimationBase.cs
- HttpRuntimeSection.cs
- OdbcTransaction.cs
- RectangleF.cs
- ADRoleFactory.cs
- EntityDataSourceColumn.cs
- AlignmentXValidation.cs
- DeclarativeCatalogPart.cs
- AttachmentService.cs
- ContextBase.cs
- DesignerActionUIService.cs
- ProcessHostServerConfig.cs
- RegexReplacement.cs
- SecurityDescriptor.cs
- Process.cs
- BindingSource.cs
- Effect.cs
- LeafCellTreeNode.cs
- TheQuery.cs
- OleDbCommand.cs
- CssClassPropertyAttribute.cs
- NetworkInformationPermission.cs
- RecipientInfo.cs
- XmlILModule.cs
- Control.cs
- Emitter.cs
- FaultDescription.cs
- AlgoModule.cs
- VersionValidator.cs
- BaseTypeViewSchema.cs
- BrowserDefinition.cs
- LongCountAggregationOperator.cs
- SessionStateModule.cs
- DataGridViewCheckBoxColumn.cs
- GPRECTF.cs
- AppDomainInfo.cs
- SchemaNotation.cs
- ErrorStyle.cs
- Ray3DHitTestResult.cs
- ReaderContextStackData.cs
- StdValidatorsAndConverters.cs
- SessionPageStateSection.cs
- WebControlAdapter.cs
- LambdaCompiler.Generated.cs
- TextBounds.cs
- ITextView.cs
- Metadata.cs
- DataTableCollection.cs
- NamedPermissionSet.cs
- HwndSourceKeyboardInputSite.cs
- ServicesUtilities.cs
- FrameworkElementAutomationPeer.cs
- CacheEntry.cs
- BooleanProjectedSlot.cs
- TextDecorationCollection.cs
- SmtpSection.cs
- DataMemberAttribute.cs
- WebBrowserNavigatingEventHandler.cs
- CurrencyManager.cs
- PhysicalAddress.cs
- BaseDataBoundControl.cs
- FlowchartDesigner.xaml.cs
- Message.cs
- Matrix3DStack.cs
- Deflater.cs
- IdleTimeoutMonitor.cs
- ClientApiGenerator.cs
- ReadOnlyHierarchicalDataSource.cs
- AuthenticationConfig.cs
- SQLInt64Storage.cs