コード例 #1
0
ファイル: FlowForm.cs プロジェクト: sat1582/CODEFramework
        /// <summary>
        /// Calculates the width of the complete row
        /// </summary>
        /// <param name="row">The row.</param>
        /// <param name="labelWidth">Width of the first label.</param>
        /// <param name="availableWidth">Total available width.</param>
        /// <returns>Total width of the row including all spacing</returns>
        protected virtual double GetTotalRowWidth(ControlRow row, double labelWidth, double availableWidth)
        {
            if (row.Elements.Count == 1 && row.Elements[0].IsFullSpanControl) return availableWidth;

            var totalWidth = labelWidth + LabelControlLeftSpacing; // TODO: Should we support shrinking here?

            for (var counter = 1; counter < row.Elements.Count; counter++) // Calculating all but the first one (which is already handled with the labelWidth parameter)
            {
                var element = row.Elements[counter];
                totalWidth += element.IsConsideredLabelElement ? LabelControlLeftSpacing : EditControlLeftSpacing;
                totalWidth += element.DesiredSize.Width;
                //if (counter < row.Elements.Count - 1)
                //    totalWidth += element.IsConsideredLabelElement ? EditControlLeftSpacing : LabelControlLeftSpacing;
            }

            return totalWidth;
        }
コード例 #2
0
ファイル: FlowForm.cs プロジェクト: sat1582/CODEFramework
        /// <summary>
        /// When overridden in a derived class, measures the size in layout required for child elements and determines a size for the <see cref="T:System.Windows.FrameworkElement" />-derived class.
        /// </summary>
        /// <param name="availableSize">The available size that this element can give to child elements. Infinity can be specified as a value to indicate that the element will size to whatever content is available.</param>
        /// <returns>The size that this element determines it needs during layout, based on its calculations of child element sizes.</returns>
        protected override Size MeasureOverride(Size availableSize)
        {
            base.MeasureOverride(availableSize);

            InvalidateVisual();

            var resultingHeight = 0d;
            var resultingWidth = 0d;

            // Before we do anything else, we allow each control to measure itself to the size they would ideally like to be
            var measureWidth = double.IsNaN(availableSize.Width) ? 10000d : availableSize.Width;
            var veryLarge = new Size(measureWidth, 100000d); // Giving the control a very large size so we see how much it really wants to use
            foreach (UIElement child in Children)
                child.Measure(veryLarge);

            // Arranging all controls into flowing rows (at this point assuming infinite horizontal space)
            _currentRows = new List<ControlRow>();
            var currentRow = AddNewRow(_currentRows);

            foreach (var child in Children)
            {
                var element = child as FrameworkElement;
                if (element == null) continue;

                var lineBreak = SimpleView.GetLineBreak(element);
                if (lineBreak && currentRow.Elements.Count > 0) currentRow = AddNewRow(_currentRows);

                var spanWidth = SimpleView.GetSpanFullWidth(element);
                if (spanWidth)
                {
                    if (currentRow.Elements.Count > 0) currentRow = AddNewRow(_currentRows);
                    currentRow.Elements.Add(new ControlRowElement {Element = element, IsFullSpanControl = true});
                    continue;
                }

                var label = SimpleView.GetLabel(element);
                var isStandAloneEditControl = SimpleView.GetIsStandAloneEditControl(element);
                if (label != null || isStandAloneEditControl)
                {
                    var individualFont = SimpleView.GetLabelFontFamily(element);
                    var font = individualFont ?? LabelFontFamily;

                    var individualStyle = SimpleView.GetLabelFontStyle(element);
                    var style = individualStyle == FontStyles.Normal ? LabelFontStyle : individualStyle;

                    var individualWeight = SimpleView.GetLabelFontWeight(element);
                    var weight = individualWeight == FontWeights.Normal ? LabelFontWeight : individualWeight;

                    var individualSize = SimpleView.GetLabelFontSize(element);
                    var size = individualSize > 0d ? individualSize : LabelFontSize;

                    var individualBrush = SimpleView.GetLabelForegroundBrush(element);
                    var brush = individualBrush ?? LabelForegroundBrush;

                    currentRow.Elements.Add(new ControlRowElement
                        {
                            Label = label ?? string.Empty,
                            IsAutoGeneratedLabel = true,
                            IsConsideredLabelElement = true,
                            HadOriginalLineBreak = currentRow.Elements.Count == 0,
                            DesiredSize = CalculateLabelSize(label ?? string.Empty, element),
                            FontFamily = font,
                            FontStyle = style,
                            FontWeight = weight,
                            FontSize = size,
                            ForegroundBrush = brush
                        });
                }

                var isLabelElement = false;
                if (currentRow.Elements.Count%2 == 0)
                    // Could be a label
                    if (element is Label || element is TextBlock)
                        isLabelElement = true;

                currentRow.Elements.Add(new ControlRowElement
                    {
                        Element = element,
                        IsAutoGeneratedLabel = false,
                        IsConsideredLabelElement = isLabelElement,
                        HadOriginalLineBreak = currentRow.Elements.Count == 0,
                        DesiredSize = isLabelElement ? element.DesiredSize : GetControlSize(element.DesiredSize)
                    });
            }

            // See which items need to be wrapped to the next row (since we do not really have infinite horizontal space)
            var rowCounter = 0;
            while (true)
            {
                if (rowCounter >= _currentRows.Count) break;

                var widestLabelWidth = GetWidestFirstLabel(_currentRows);
                var row = _currentRows[rowCounter];
                var rowWidth = GetTotalRowWidth(row, widestLabelWidth, availableSize.Width);

                if (rowWidth > availableSize.Width)
                {
                    // This row is too wide... we need to do something about it
                    var fittingItemCount = GetNumberOfElementsThatFitInRow(row, widestLabelWidth, availableSize.Width);

                    if (fittingItemCount < row.Elements.Count)
                    {
                        // We insert a new row into the current flow
                        var insertedRow = new ControlRow();
                        var elementsToMove = row.Elements.Count - fittingItemCount;
                        for (var moveCounter = 0; moveCounter < elementsToMove; moveCounter++)
                        {
                            var moveElement = row.Elements[fittingItemCount]; // it will always be the next item since we are removing them as we go
                            row.Elements.RemoveAt(fittingItemCount);
                            if (moveCounter == 0 || !moveElement.IsFakeLabel)
                                insertedRow.Elements.Add(moveElement);
                        }
                        if (row.Elements.Count > 1 && row.Elements[row.Elements.Count - 1].IsConsideredLabelElement)
                        {
                            var elementToMove = row.Elements[row.Elements.Count - 1];
                            row.Elements.RemoveAt(row.Elements.Count - 1);
                            insertedRow.Elements.Insert(0, elementToMove);
                        }
                        _currentRows.Insert(rowCounter + 1, insertedRow);

                        rowCounter = 0; // We need to start over
                        continue;
                    }
                }

                rowCounter++;
            }

            // Ready to calculate the actual control positions
            var finalLabelWidth = GetWidestFirstLabel(_currentRows);
            var currentY = VerticalSpacing;
            foreach (var row in _currentRows)
            {
                if (row.Elements.Count == 1 && row.Elements[0].IsFullSpanControl)
                {
                    // TODO: Maybe we want some special margins for this case
                    row.Elements[0].DesiredPosition = new Point(0d, currentY);
                    row.Elements[0].DesiredSize = new Size(availableSize.Width, row.Elements[0].Element.DesiredSize.Height);
                    currentY += row.Elements[0].Element.DesiredSize.Height + VerticalSpacing;
                    continue;
                }

                var currentX = 0d;
                var elementCounter = 0;
                var tallestElement = 0d;
                var mustUseVerticalLabelOffsetForLabelsInRow = false;
                foreach (var element in row.Elements)
                {
                    if (element.IsConsideredLabelElement)
                    {
                        currentX += LabelControlLeftSpacing;
                        if (elementCounter == 0) // We can still decide whether labels must be offset or not
                            if (row.Elements.Count > 1)
                            {
                                var nextElement = row.Elements[1];
                                if (nextElement != null)
                                    if (nextElement.DesiredSize.Height >= element.DesiredSize.Height + VerticalLabelControlOffset)
                                        mustUseVerticalLabelOffsetForLabelsInRow = true; // If the next label is taller than the current label, we add an offset.
                            }
                        var controlY = mustUseVerticalLabelOffsetForLabelsInRow ? currentY + VerticalLabelControlOffset : currentY;
                        element.DesiredPosition = new Point(currentX, controlY);
                        element.DesiredSize = new Size(element.DesiredSize.Width, element.DesiredSize.Height);
                        if (elementCounter == 0)
                            element.MaxLabelWidth = finalLabelWidth;
                    }
                    else
                    {
                        currentX += EditControlLeftSpacing;
                        element.DesiredPosition = new Point(currentX, currentY);
                        element.DesiredSize = new Size(element.DesiredSize.Width, element.DesiredSize.Height);
                    }

                    currentX += elementCounter == 0 ? finalLabelWidth : element.DesiredSize.Width;
                    tallestElement = Math.Max(element.DesiredSize.Height, tallestElement);

                    elementCounter++;
                }

                currentY += tallestElement + VerticalSpacing;

                resultingWidth = Math.Max(resultingWidth, currentX);
                resultingHeight = currentY;
            }

            return new Size(resultingWidth, resultingHeight);
        }
コード例 #3
0
ファイル: FlowForm.cs プロジェクト: sat1582/CODEFramework
        /// <summary>Returns how many of the specified row's elements fit in the available horizontal space</summary>
        /// <param name="row">The row.</param>
        /// <param name="labelWidth">Width of the label.</param>
        /// <param name="availableWidth">Total available width.</param>
        /// <returns>Number of elements that fit in the row.</returns>
        protected virtual int GetNumberOfElementsThatFitInRow(ControlRow row, double labelWidth, double availableWidth)
        {
            if (row.Elements.Count == 1 && row.Elements[0].IsFullSpanControl) return 1;

            var fitCount = 1;
            var totalWidth = labelWidth + LabelControlLeftSpacing; // TODO: Should we support shrinking here?

            for (var counter = 1; counter < row.Elements.Count; counter++) // Calculating all but the first two (which we will always leave on the first row, even if that forces scrolling)
            {
                var element = row.Elements[counter];
                totalWidth += element.IsConsideredLabelElement ? LabelControlLeftSpacing : EditControlLeftSpacing;
                totalWidth += element.DesiredSize.Width;
                if (totalWidth < availableWidth) fitCount++; // This control still fits on the row
                else
                {
                    // This one doesn't fit into this row anymore.

                    // Maybe checkboxes are special
                    if (row.Elements[counter].Element is CheckBox && SpecialCheckBoxBehaviorActive)
                    {
                        // If this isn't the first special captioned checkbox in the group, we need to break the whole group
                        var labelIndex = counter - 1;
                        while (labelIndex > 0)
                        {
                            if (row.Elements[labelIndex].IsConsideredLabelElement) // This would make for a label that is not the first element in the row, and thus we break the whole group of checkboxes over to the next row
                            {
                                fitCount = labelIndex;
                                if (fitCount < 2) fitCount = 2; // Even if 2 controls don't fit, we leave them on that row anyway. Worst case, things have to scroll
                                return fitCount;
                            }
                            labelIndex--;
                        }

                        // Looks like we have not found an inline label yet, so we just need to break however many checkboxes we have
                        if (counter > 1)
                            // We insert another fake label if the control to the left is a checkbox and not a label
                            if (!row.Elements[counter - 1].IsConsideredLabelElement && row.Elements[counter - 1].Element is CheckBox)
                            {
                                InsertFakeLabel(row.Elements, row.Elements[counter].Element, fitCount);
                                if (fitCount < 2) fitCount = 2; // Even if 2 controls don't fit, we leave them on that row anyway. Worst case, things have to scroll
                                return fitCount;
                            }
                        if (fitCount == 1) // We can really only fit one control space-wise, but we still need to keep a label and control on the same line
                        {
                            if (row.Elements.Count > 2 && !row.Elements[2].IsConsideredLabelElement) // If there are any elements after the second one, we need to add another fake label
                                InsertFakeLabel(row.Elements, row.Elements[counter].Element, 2);
                            return 2;
                        }
                    }

                    // Maybe radio buttons are special
                    if (row.Elements[counter].Element is RadioButton && SpecialRadioButtonBehaviorActive)
                    {
                        // If this isn't the first special captioned radio button in the group, we need to break the whole group
                        var labelIndex = counter - 1;
                        while (labelIndex > 0)
                        {
                            if (row.Elements[labelIndex].IsConsideredLabelElement) // This would make for a label that is not the first element in the row, and thus we break the whole group of radio buttons over to the next row
                            {
                                fitCount = labelIndex;
                                if (fitCount < 2) fitCount = 2; // Even if 2 controls don't fit, we leave them on that row anyway. Worst case, things have to scroll
                                return fitCount;
                            }
                            labelIndex--;
                        }

                        // Looks like we have not found an inline label yet, so we just need to break however many radio buttons we have
                        if (counter > 1)
                            // We insert another fake label if the control to the left is a radio button and not a label
                            if (!row.Elements[counter - 1].IsConsideredLabelElement && row.Elements[counter - 1].Element is RadioButton)
                            {
                                InsertFakeLabel(row.Elements, row.Elements[counter].Element, fitCount);
                                if (fitCount < 2) fitCount = 2; // Even if 2 controls don't fit, we leave them on that row anyway. Worst case, things have to scroll
                                return fitCount;
                            }
                        if (fitCount == 1) // We can really only fit one control space-wise, but we still need to keep a label and control on the same line
                        {
                            if (row.Elements.Count > 2 && !row.Elements[2].IsConsideredLabelElement) // If there are any elements after the second one, we need to add another fake label
                                InsertFakeLabel(row.Elements, row.Elements[counter].Element, 2);
                            return 2;
                        }
                    }

                    // If the current control is the edit control, we also consider the label to not have fit
                    if (!element.IsConsideredLabelElement) fitCount--;
                    break;
                }
            }

            if (fitCount < 2) fitCount = 2; // Even if 2 controls don't fit, we leave them on that row anyway. Worst case, things have to scroll
            return fitCount;
        }