Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / System / Windows / Controls / Primitives / TabPanel.cs / 1305600 / TabPanel.cs
//---------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System; using MS.Internal; using MS.Utility; using System.Collections; using System.ComponentModel; using System.Diagnostics; using System.Reflection; using System.Windows.Threading; using System.Windows.Media; using System.Windows.Input; namespace System.Windows.Controls.Primitives { ////// TabPanel is a Panel designed to handle the intricacies of laying out the tab buttons in a TabControl. Specically, it handles: /// Serving as an ItemsHost for TabItems within a TabControl /// Determining correct sizing and positioning for TabItems /// Handling the logic associated with MultiRow scenarios, namely: /// Calculating row breaks in a collection of TabItems /// Laying out TabItems in multiple rows based on those breaks /// Performing specific layout for a selected item to indicate selection, namely: /// Bringing the selected tab to the front, or, in other words, making the selected tab appear to be in front of other tabs. /// Increasing the size pre-layout size of a selected item (note that this is not a transform, but rather an increase in the size allotted to the element in which to perform layout). /// Bringing the selected tab to the front /// Exposing attached properties that allow TabItems to be styled based on their placement within the TabPanel. /// public class TabPanel : Panel { //------------------------------------------------------------------- // // Constructors // //------------------------------------------------------------------- #region Constructors ////// Default DependencyObject constructor /// ////// Automatic determination of current Dispatcher. Use alternative constructor /// that accepts a Dispatcher for best performance. /// public TabPanel() : base() { } static TabPanel() { KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(TabPanel), new FrameworkPropertyMetadata(KeyboardNavigationMode.Once)); KeyboardNavigation.DirectionalNavigationProperty.OverrideMetadata(typeof(TabPanel), new FrameworkPropertyMetadata(KeyboardNavigationMode.Cycle)); } #endregion //-------------------------------------------------------------------- // // Public Methods // //------------------------------------------------------------------- #region Public Methods #endregion //-------------------------------------------------------------------- // // Protected Methods // //-------------------------------------------------------------------- #region Protected Methods ////// Updates DesiredSize of the TabPanel. Called by parent UIElement. This is the first pass of layout. /// ////// TabPanel /// /// Constraint size is an "upper limit" that TabPanel should not exceed. ///TabPanel' desired size. protected override Size MeasureOverride(Size constraint) { Size contentSize = new Size(); Dock tabAlignment = TabStripPlacement; _numRows = 1; _numHeaders = 0; _rowHeight = 0; // For top and bottom placement the panel flow its children to calculate the number of rows and // desired vertical size if (tabAlignment == Dock.Top || tabAlignment == Dock.Bottom) { int numInCurrentRow = 0; double currentRowWidth = 0; double maxRowWidth = 0; foreach (UIElement child in InternalChildren) { if (child.Visibility == Visibility.Collapsed) continue; _numHeaders++; // Helper measures child, and deals with Min, Max, and base Width & Height properties. // Helper returns the size a child needs to take up (DesiredSize or property specified size). child.Measure(constraint); Size childSize = GetDesiredSizeWithoutMargin(child); if (_rowHeight < childSize.Height) _rowHeight = childSize.Height; if (currentRowWidth + childSize.Width > constraint.Width && numInCurrentRow > 0) { // If child does not fit in the current row - create a new row if (maxRowWidth < currentRowWidth) maxRowWidth = currentRowWidth; currentRowWidth = childSize.Width; numInCurrentRow = 1; _numRows++; } else { currentRowWidth += childSize.Width; numInCurrentRow++; } } if (maxRowWidth < currentRowWidth) maxRowWidth = currentRowWidth; contentSize.Height = _rowHeight * _numRows; // If we don't have constraint or content wisth is smaller than constraint width then size to content if (double.IsInfinity(contentSize.Width) || DoubleUtil.IsNaN(contentSize.Width) || maxRowWidth < constraint.Width) contentSize.Width = maxRowWidth; else contentSize.Width = constraint.Width; } else if (tabAlignment == Dock.Left || tabAlignment == Dock.Right) { foreach (UIElement child in InternalChildren) { if (child.Visibility == Visibility.Collapsed) continue; _numHeaders++; // Helper measures child, and deals with Min, Max, and base Width & Height properties. // Helper returns the size a child needs to take up (DesiredSize or property specified size). child.Measure(constraint); Size childSize = GetDesiredSizeWithoutMargin(child); if (contentSize.Width < childSize.Width) contentSize.Width = childSize.Width; contentSize.Height += childSize.Height; } } // Returns our minimum size & sets DesiredSize. return contentSize; } ////// TabPanel arranges each of its children. /// /// Size that TabPanel will assume to position children. protected override Size ArrangeOverride(Size arrangeSize) { Dock tabAlignment = TabStripPlacement; if (tabAlignment == Dock.Top || tabAlignment == Dock.Bottom) { ArrangeHorizontal(arrangeSize); } else if (tabAlignment == Dock.Left || tabAlignment == Dock.Right) { ArrangeVertical(arrangeSize); } return arrangeSize; } ////// Override of ///. /// Geometry to use as additional clip in case when element is larger then available space protected override Geometry GetLayoutClip(Size layoutSlotSize) { return null; } #endregion Protected Methods //------------------------------------------------------------------- // // Private Methods // //-------------------------------------------------------------------- #region Private Methods private Size GetDesiredSizeWithoutMargin(UIElement element) { Thickness margin = (Thickness)element.GetValue(MarginProperty); Size desiredSizeWithoutMargin = new Size(); desiredSizeWithoutMargin.Height = Math.Max(0d, element.DesiredSize.Height - margin.Top - margin.Bottom); desiredSizeWithoutMargin.Width = Math.Max(0d, element.DesiredSize.Width - margin.Left - margin.Right); return desiredSizeWithoutMargin; } private double[] GetHeadersSize() { double[] headerSize = new double[_numHeaders]; int childIndex = 0; foreach (UIElement child in InternalChildren) { if (child.Visibility == Visibility.Collapsed) continue; Size childSize = GetDesiredSizeWithoutMargin(child); headerSize[childIndex] = childSize.Width; childIndex++; } return headerSize; } private void ArrangeHorizontal(Size arrangeSize) { Dock tabAlignment = TabStripPlacement; bool isMultiRow = _numRows > 1; int activeRow = 0; int[] solution = new int[0]; Vector childOffset = new Vector(); double[] headerSize = GetHeadersSize(); // If we have multirows, then calculate the best header distribution if (isMultiRow) { solution = CalculateHeaderDistribution(arrangeSize.Width, headerSize); activeRow = GetActiveRow(solution); // TabPanel starts to layout children depend on activeRow which should be always on bottom (top) // The first row should start from Y = (_numRows - 1 - activeRow) * _rowHeight if (tabAlignment == Dock.Top) childOffset.Y = (_numRows - 1 - activeRow) * _rowHeight; if (tabAlignment == Dock.Bottom && activeRow != 0) childOffset.Y = (_numRows - activeRow) * _rowHeight; } int childIndex = 0; int separatorIndex = 0; foreach (UIElement child in InternalChildren) { if (child.Visibility == Visibility.Collapsed) continue; Thickness margin = (Thickness)child.GetValue(MarginProperty); double leftOffset = margin.Left; double rightOffset = margin.Right; double topOffset = margin.Top; double bottomOffset = margin.Bottom; bool lastHeaderInRow = isMultiRow && (separatorIndex < solution.Length && solution[separatorIndex] == childIndex || childIndex == _numHeaders - 1); //Length left, top, right, bottom; Size cellSize = new Size(headerSize[childIndex], _rowHeight); // Align the last header in the row; If headers are not aligned directional nav would not work correctly if (lastHeaderInRow) { cellSize.Width = arrangeSize.Width - childOffset.X; } child.Arrange(new Rect(childOffset.X, childOffset.Y, cellSize.Width, cellSize.Height)); Size childSize = cellSize; childSize.Height = Math.Max(0d, childSize.Height - topOffset - bottomOffset); childSize.Width = Math.Max(0d, childSize.Width - leftOffset - rightOffset); // Calculate the offset for the next child childOffset.X += cellSize.Width; if (lastHeaderInRow) { if ((separatorIndex == activeRow && tabAlignment == Dock.Top) || (separatorIndex == activeRow - 1 && tabAlignment == Dock.Bottom)) childOffset.Y = 0d; else childOffset.Y += _rowHeight; childOffset.X = 0d; separatorIndex++; } childIndex++; } } private void ArrangeVertical(Size arrangeSize) { double childOffsetY = 0d; foreach (UIElement child in InternalChildren) { if (child.Visibility != Visibility.Collapsed) { Size childSize = GetDesiredSizeWithoutMargin(child); child.Arrange(new Rect(0, childOffsetY, arrangeSize.Width, childSize.Height)); // Calculate the offset for the next child childOffsetY += childSize.Height; } } } // Returns the row which contain the child with IsSelected==true private int GetActiveRow(int[] solution) { int activeRow = 0; int childIndex = 0; if (solution.Length > 0) { foreach (UIElement child in InternalChildren) { if (child.Visibility == Visibility.Collapsed) continue; bool isActiveTab = (bool)child.GetValue(Selector.IsSelectedProperty); if (isActiveTab) { return activeRow; } if (activeRow < solution.Length && solution[activeRow] == childIndex) { activeRow++; } childIndex++; } } // If the is no selected element and aligment is Top - then the active row is the last row if (TabStripPlacement == Dock.Top) { activeRow = _numRows - 1; } return activeRow; } /* TabPanel layout calculation: After measure call we have: rowWidthLimit - width of the TabPanel Header[0..n-1] - headers headerWidth[0..n-1] - header width Calculated values: numSeparators - number of separators between numSeparators+1 rows rowWidth[0..numSeparators] - row width rowHeaderCount[0..numSeparators] - Row Count = number of headers on that row rowAverageGap[0..numSeparators] - Average Gap for the row i = (rowWidth - rowWidth[i])/rowHeaderCount[i] currentSolution[0..numSeparators-1] - separator currentSolution[i]=x means Header[x] and h[x+1] are separated with new line bestSolution[0..numSeparators-1] - keep the last Best Solution bestSolutionRowAverageGap - keep the last Best Solution Average Gap Between all separators distribution the best solution have minimum Average Gap - this is the amount of pixels added to the header (to justify) in the row How does it work: First we flow the headers to calculate the number of necessary rows (numSeparators+1). That means we need to insert numSeparators separators between n headers (numSeparatorsrowWidthLimit && numberOfHeadersInCurrentRow > 0) { // if we cannot add next header - flow to next row // Store current row before we go to the next rowWidth[currentRowIndex] = currentRowWidth; // Store the current row width rowHeaderCount[currentRowIndex] = numberOfHeadersInCurrentRow; // For each row we store the number os headers inside currentAverageGap = Math.Max(0d, (rowWidthLimit - currentRowWidth) / numberOfHeadersInCurrentRow); // The amout of width that should be added to justify the header rowAverageGap[currentRowIndex] = currentAverageGap; currentSolution[currentRowIndex] = index - 1; // Separator points to the last header in the row if (bestSolutionMaxRowAverageGap < currentAverageGap) // Remember the maximum of all currentAverageGap bestSolutionMaxRowAverageGap = currentAverageGap; // Iterate to next row currentRowIndex++; currentRowWidth = headerWidth[index]; // Accumulate header widths on the same row numberOfHeadersInCurrentRow = 1; } else { currentRowWidth += headerWidth[index]; // Accumulate header widths on the same row // Increase the number of headers only if they are not collapsed (width=0) if (headerWidth[index] != 0) numberOfHeadersInCurrentRow++; } } // If everithing fit in 1 row then exit (no separators needed) if (currentRowIndex == 0) return new int[0]; // Add the last row rowWidth[currentRowIndex] = currentRowWidth; rowHeaderCount[currentRowIndex] = numberOfHeadersInCurrentRow; currentAverageGap = (rowWidthLimit - currentRowWidth) / numberOfHeadersInCurrentRow; rowAverageGap[currentRowIndex] = currentAverageGap; if (bestSolutionMaxRowAverageGap < currentAverageGap) bestSolutionMaxRowAverageGap = currentAverageGap; currentSolution.CopyTo(bestSolution, 0); // Remember the first solution as initial bestSolution rowAverageGap.CopyTo(bestSolutionRowAverageGap, 0); // bestSolutionRowAverageGap is used in ArrangeOverride to calculate header sizes // Search for the best solution // The exit condition if when we cannot move header to the next row while (true) { // Find the row with maximum AverageGap int worstRowIndex = 0; // Keep the row index with maximum AverageGap double maxAG = 0; for (int i = 0; i < _numRows; i++) // for all rows { if (maxAG < rowAverageGap[i]) { maxAG = rowAverageGap[i]; worstRowIndex = i; } } // If we are on the first row - cannot move from previous if (worstRowIndex == 0) break; // From the row with maximum AverageGap we try to move a header from previous row int moveToRow = worstRowIndex; int moveFromRow = moveToRow - 1; int moveHeader = currentSolution[moveFromRow]; double movedHeaderWidth = headerWidth[moveHeader]; rowWidth[moveToRow] += movedHeaderWidth; // If the moved header cannot fit - exit. We have the best solution already. if (rowWidth[moveToRow] > rowWidthLimit) break; // If header is moved successfully to the worst row // we update the arrays keeping the row state currentSolution[moveFromRow]--; rowHeaderCount[moveToRow]++; rowWidth[moveFromRow] -= movedHeaderWidth; rowHeaderCount[moveFromRow]--; rowAverageGap[moveFromRow] = (rowWidthLimit - rowWidth[moveFromRow]) / rowHeaderCount[moveFromRow]; rowAverageGap[moveToRow] = (rowWidthLimit - rowWidth[moveToRow]) / rowHeaderCount[moveToRow]; // EvaluateSolution: // If the current solution is better than bestSolution - keep it in bestSolution maxAG = 0; for (int i = 0; i < _numRows; i++) // for all rows { if (maxAG < rowAverageGap[i]) { maxAG = rowAverageGap[i]; } } if (maxAG < bestSolutionMaxRowAverageGap) { bestSolutionMaxRowAverageGap = maxAG; currentSolution.CopyTo(bestSolution, 0); rowAverageGap.CopyTo(bestSolutionRowAverageGap, 0); } } // Each header size should be increased so headers in the row stretch to fit the row currentRowIndex = 0; for (int index = 0; index < numHeaders; index++) { headerWidth[index] += bestSolutionRowAverageGap[currentRowIndex]; if (currentRowIndex < numSeparators && bestSolution[currentRowIndex] == index) currentRowIndex++; } // Use the best solution bestSolution[0..numSeparators-1] to layout return bestSolution; } private Dock TabStripPlacement { get { Dock placement = Dock.Top; TabControl tc = TemplatedParent as TabControl; if (tc != null) placement = tc.TabStripPlacement; return placement; } } #endregion //------------------------------------------------------------------- // // Private Data // //------------------------------------------------------------------- #region Private data private int _numRows = 1; // Nubmer of row calculated in measure and used in arrange private int _numHeaders = 0; // Number of headers excluding the collapsed items private double _rowHeight = 0; // Maximum of all headers height #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
- SettingsPropertyValueCollection.cs
- ResolveNameEventArgs.cs
- HtmlElementEventArgs.cs
- SmiEventStream.cs
- SimpleWebHandlerParser.cs
- WebBrowserPermission.cs
- XsdDateTime.cs
- ExceptionTrace.cs
- WebPartTransformerCollection.cs
- InputBinding.cs
- X509Certificate2Collection.cs
- GridViewEditEventArgs.cs
- DeviceSpecific.cs
- ButtonFlatAdapter.cs
- KeyFrames.cs
- PasswordTextContainer.cs
- SpStreamWrapper.cs
- MessageDecoder.cs
- TextPointerBase.cs
- UnsafeNativeMethods.cs
- HostedHttpTransportManager.cs
- TableHeaderCell.cs
- SqlDataSourceCustomCommandPanel.cs
- ClientUIRequest.cs
- FactoryMaker.cs
- NativeRightsManagementAPIsStructures.cs
- RenderContext.cs
- Image.cs
- XmlnsDefinitionAttribute.cs
- CustomAttribute.cs
- IdentifierCreationService.cs
- KeyedHashAlgorithm.cs
- IncrementalReadDecoders.cs
- ResourcePermissionBaseEntry.cs
- EntitySetBaseCollection.cs
- RTLAwareMessageBox.cs
- BypassElement.cs
- DbProviderConfigurationHandler.cs
- OdbcHandle.cs
- PopupRoot.cs
- ApplicationSecurityManager.cs
- XmlCountingReader.cs
- LocalizationParserHooks.cs
- ReadOnlyAttribute.cs
- Behavior.cs
- CharacterMetricsDictionary.cs
- CriticalHandle.cs
- RightsManagementPermission.cs
- DeviceContext.cs
- TableLayoutRowStyleCollection.cs
- AnnotationHighlightLayer.cs
- SessionStateModule.cs
- TabControlEvent.cs
- RequestCache.cs
- TaskFormBase.cs
- DataGridCell.cs
- CodePageUtils.cs
- EventLogPermissionEntryCollection.cs
- RenameRuleObjectDialog.cs
- TextBoxRenderer.cs
- UserControlBuildProvider.cs
- HandledMouseEvent.cs
- CodeParameterDeclarationExpression.cs
- CodeNamespaceImportCollection.cs
- TextSimpleMarkerProperties.cs
- FontFamilyConverter.cs
- HtmlElementErrorEventArgs.cs
- SBCSCodePageEncoding.cs
- UserControl.cs
- CapabilitiesAssignment.cs
- SectionInformation.cs
- AVElementHelper.cs
- SslStreamSecurityUpgradeProvider.cs
- BasicViewGenerator.cs
- ConfigurationHelpers.cs
- TaskFactory.cs
- Codec.cs
- LinqDataSourceHelper.cs
- OracleColumn.cs
- ReplacementText.cs
- LicenseManager.cs
- TextEditorMouse.cs
- SQLChars.cs
- WSSecureConversationDec2005.cs
- Publisher.cs
- TCEAdapterGenerator.cs
- PerformanceCounter.cs
- PropertyTab.cs
- SimplePropertyEntry.cs
- ServiceErrorHandler.cs
- SerializationInfo.cs
- CompilerParameters.cs
- RotateTransform.cs
- FontEditor.cs
- HijriCalendar.cs
- QilNode.cs
- SmiMetaData.cs
- AddInAdapter.cs
- UnsafeNetInfoNativeMethods.cs
- SHA256Managed.cs