Avalonia 的 TilePanel,`DependencyProperty` 到 `AvaloniaProperty`

问题描述 投票:0回答:1

我想要一个 Avalonia 的 WrapPanel,它通过将项目彼此相邻放置而包装其内容,没有间距,就像本文中一样:https://www.codeproject.com/Articles/482501/TilePanel-An-alternative-到 WrapPanel

像这样enter image description here

我正在尝试将代码从 WPF 转换为 Avalonia 等效代码。问题是我正在努力解决如何将

DependencyProperty
编码为
AvaloniaProperty

WPF 控件的原始代码:

public class TilePanel : Panel
{
    public enum OrientationCategories
    {
        Vertical = 0,
        Horizontal = 1,
    }
    
    public static readonly DependencyProperty OrientationProperty =
    DependencyProperty.Register("Orientation", typeof(OrientationCategories), typeof(TilePanel), new PropertyMetadata(OrientationCategories.Vertical, OnOrientationChanged));

    public OrientationCategories Orientation  // here I get an error "Expected class, delegate, enum, interface or struct"
        {
            get { return (OrientationCategories)GetValue(OrientationProperty); }
            set
            {
                SetValue(OrientationProperty, value);
            }
        }

    private static void OnOrientationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((Panel)d).InvalidateMeasure();
    }

    public static readonly DependencyProperty AlignmentBoundaryProperty =
    DependencyProperty.Register("AlignmentBoundary", typeof(double), typeof(TilePanel), new PropertyMetadata(0.0, OnAlignmentBoundaryChanged));

    public double AlignmentBoundary
    {
        get { return (double)GetValue(AlignmentBoundaryProperty); }
        set
        {
            SetValue(AlignmentBoundaryProperty, value);
        }
    }

    private static void OnAlignmentBoundaryChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((Panel)d).InvalidateMeasure();
    }

    public static readonly DependencyProperty AlignOnLesserEdgeProperty =
    DependencyProperty.Register("AlignOnLesserEdge", typeof(bool), typeof(TilePanel), new PropertyMetadata(false, OnAlignOnLesserEdgeChanged));

    public bool AlignOnLesserEdge  // here I get an error "Expected class, delegate, enum, interface or struct"
    {
        get { return (bool)GetValue(AlignOnLesserEdgeProperty); }
        set
        {
            SetValue(AlignOnLesserEdgeProperty, value);
        }
    }

    private static void OnAlignOnLesserEdgeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((Panel)d).InvalidateMeasure();
    }

    /// <summary> A utility class used to tile blocks into a vertical stack of blocks. </summary>
    private class SpaceManager
    {
        private enum CompareActions
        {
            None,
            Delete,
            Split
        }

        /// <summary> Edge item - an item that defines a level at which a new item can be added. </summary>
        private class EdgeItem
        {
            /// <summary> Constructor. </summary>
            /// <param name="edgePosition1">  The left body (or top body if transposed). </param>
            /// <param name="edgePosition2"> The right body (or bottom body if transposed). </param>
            /// <param name="level">     The level. </param>
            /// <param name="edgeLimit1">  The left wing  (or top wing if transposed). </param>
            /// <param name="edgeLimit2"> The right wing  (or bottom body if transposed). </param>
            public EdgeItem(double edgePosition1, double edgePosition2, double level, double edgeLimit1, double edgeLimit2)
            {
                Level = level;
                EdgePosition1 = edgePosition1;
                EdgePosition2 = edgePosition2;
                EdgeLimit1 = edgeLimit1;
                EdgeLimit2 = edgeLimit2;
            }

            /// <summary> Copy constructor. </summary>
            /// <param name="item"> The item. </param>
            public EdgeItem(EdgeItem item)
            {
                Copy(item);
            }

            /// <summary> Copies the given item. </summary>
            /// <param name="item"> The item. </param>
            public void Copy(EdgeItem item)
            {
                Level = item.Level;
                EdgePosition1 = item.EdgePosition1;
                EdgePosition2 = item.EdgePosition2;
                EdgeLimit1 = item.EdgeLimit1;
                EdgeLimit2 = item.EdgeLimit2;
            }

            /// <summary> Compare and adjust this item with a higher level item. </summary>
            /// <param name="item"> The item to compare against. </param>
            /// <returns> true if this item can be removed otherwise false. </returns>
            public CompareActions CompareAndAdjustBody(EdgeItem item)
            {
                if (item.Level < Level)
                {
                    // item has a lower level so we don't need to adjust
                    return CompareActions.None;
                }
                if((item.EdgePosition2 < EdgePosition1) || (item.EdgePosition1 > EdgePosition2))
                {
                    // No overlap
                    return CompareActions.None;
                }
                if ((item.EdgePosition2 >= EdgePosition2) && (item.EdgePosition1 <= EdgePosition1))
                {
                    // completely overlapped
                    return CompareActions.Delete;
                }
                // partially overlapped so reduce size to only non overlapped
                if((EdgePosition1 < item.EdgePosition1) && (EdgePosition2 > item.EdgePosition2))
                {
                    return CompareActions.Split;
                }
                if (EdgePosition1 < item.EdgePosition1)
                {
                    EdgePosition2 = item.EdgePosition1;
                }
                else
                {
                    EdgePosition1 = item.EdgePosition2;
                }
                // if size is too small, mark it for delete
                return (EdgePosition2 - EdgePosition1 < 0) ? CompareActions.Delete : CompareActions.None;
            }

            /// <summary> Compare and adjust this item with a higher level item. </summary>
            /// <param name="item"> The item to compare against. </param>
            /// <returns> true if this item can be removed otherwise false. </returns>
            public void CompareAndAdjustWing(EdgeItem item)
            {
                if (item.Level <= Level)
                {
                    // item has a lower level so we don't need to adjust
                    return;
                }
                if ((item.EdgePosition2 < EdgeLimit1) || (item.EdgePosition1 > EdgeLimit2))
                {
                    // No overlap
                    return;
                }
                if ((item.EdgePosition2 >= EdgeLimit2) && (item.EdgePosition1 <= EdgeLimit1))
                {
                    // completely overlapped
                    Debug.Assert(false, "This Item should have been deleted already");
                    return;
                }
                // partially overlapped so reduce size to only non overlapped
                if ((EdgeLimit1 < item.EdgePosition1) && (EdgeLimit2 > item.EdgePosition2))
                {
                    if(EdgePosition1 > item.EdgePosition1)
                    {
                        EdgeLimit1 = item.EdgePosition2;
                    }
                    else
                    {
                        EdgeLimit2 = item.EdgePosition1;
                    }
                    return;
                }
                if (EdgeLimit1 < item.EdgePosition1)
                {
                    EdgeLimit2 = item.EdgePosition1;
                }
                else
                {
                    EdgeLimit1 = item.EdgePosition2;
                }
            }

            /// <summary> Determine if we can be with the supplied item. </summary>
            /// <param name="edgeItem">          The edge item to be merged with. </param>
            /// <param name="alignBoundary">     The align boundary. </param>
            /// <param name="alignOnLesserEdge"> true to align on lesser edge. </param>
            /// <returns> true if we can be merged, false if not. </returns>
            public bool CanBeMerged(EdgeItem edgeItem, double alignBoundary, bool alignOnLesserEdge)
            {
                if ((Level - edgeItem.Level > 0) && (Level - edgeItem.Level < alignBoundary))
                {
                    return ((edgeItem.EdgeLimit1 >= EdgeLimit1) && (edgeItem.EdgeLimit1 <= EdgeLimit2)) && ((edgeItem.EdgeLimit2 >= EdgeLimit1) && (edgeItem.EdgeLimit2 <= EdgeLimit2) &&
                        ((alignOnLesserEdge) || (EdgePosition1 < edgeItem.EdgePosition2)));
                }
                return false;
            }

            /// <summary> Gets or sets the vertical edge level. </summary>
            /// <value> The level. </value>
            public double Level { get; set; }

            /// <summary> Gets or sets the right body (or bottom body if transposed). </summary>
            /// <value> The right body  (or bottom body if transposed). </value>
            public double EdgePosition2 { get; set; }

            /// <summary> Gets or sets the left (or top body if transposed). </summary>
            /// <value> The left (or top body if transposed). </value>
            public double EdgePosition1 { get; set; }

            /// <summary> Gets or sets the right (or bottom wing if transposed). </summary>
            /// <value> The right  (or bottom wing if transposed). </value>
            public double EdgeLimit2 { get; set; }

            /// <summary> Gets or sets the left  (or top wing if transposed). </summary>
            /// <value> The left  (or top wing if transposed). </value>
            public double EdgeLimit1 { get; set; }

            /// <summary> Fit rectangle the rectangle into the edge space. </summary>
            /// <param name="blockWidth"> the width of the block to be fitted. </param>
            /// <param name="transposed"> true if transposed. </param>
            /// <returns> returns the top left position if fitted else null. </returns>
            public Point? FitBlock(double blockWidth, bool transposed)
            {
                if (blockWidth <= EdgeLimit2 - EdgeLimit1)
                {
                    return transposed ? new Point(Level, EdgeLimit1) : new Point(EdgeLimit1, Level);
                }
                return null;
            }
        }

        /// <summary> Gets or sets the align limit. </summary>
        /// <value> The align limit. </value>
        public double AlignLimit { get; set; }

        /// <summary> Gets or sets a value indicating whether the align on lesser edge. </summary>
        /// <value> true if align on lesser edge, false if not. </value>
        public bool AlignOnLesserEdge { get; set; }

        /// <summary> The left stack position. </summary>
        private readonly double _areaLimit1;
        /// <summary> The right stack position. </summary>
        private readonly double _areaLimit2;
        /// <summary> The top stack position. </summary>
        private readonly double _minAreaLevel;

        /// <summary> true if transposed (Horizontal) else (Vertical). </summary>
        private readonly bool _transposed;

        /// <summary> List of linked block rectangles. </summary>
        private readonly LinkedList<Rect> _linkedList = new LinkedList<Rect>();
        /// <summary> List of edge items. </summary>
        private List<EdgeItem> _edgeItemList;
        /// <summary> Collection of edge items. </summary>
        private LinkedList<EdgeItem> _edgeItemCollection;

        /// <summary> Constructor. </summary>
        /// <param name="areaLimit2">   The right edge of the area (or bottom edge if transposed). </param>
        /// <param name="transposed">   true if transposed. </param>
        /// <param name="minAreaLevel"> (optional) the top edge of the area (or left edge if transposed). </param>
        /// <param name="areaLimit1">   (optional) the left edge of the area (or top edge if transposed. </param>
        public SpaceManager(double areaLimit2, bool transposed=false, double minAreaLevel=0, double areaLimit1=0)
        {
            _transposed = transposed;
            _areaLimit1 = areaLimit1;
            _areaLimit2 = areaLimit2;
            _minAreaLevel = minAreaLevel;
        }

        /// <summary> Check for overlap. </summary>
        /// <param name="index"> Zero-based index of the. </param>
        /// <param name="edges"> The edges. </param>
        /// <returns> true if it succeeds, false if it fails. </returns>
        private CompareActions CheckForOverlap(LinkedListNode<EdgeItem> index, List<EdgeItem> edges)
        {
            // compare the current item to the following items (should be greater level)
            LinkedListNode<EdgeItem> subIndex = index.Next;
            while(subIndex != null)
            {
                switch(index.Value.CompareAndAdjustBody(subIndex.Value))
                {
                    // item is no longer neede so request it be deleted
                    case CompareActions.Delete:
                        return CompareActions.Delete;
                    // item needs to be split into 2 items
                    case CompareActions.Split:
                        // compare split item sizes
                        double w1 = subIndex.Value.EdgePosition1 - index.Value.EdgePosition1;
                        double w2 = index.Value.EdgePosition2 - subIndex.Value.EdgePosition2;
                        if((w1 < 0) && (w2 < 0))
                        {
                            // both too small, so delete item instead
                            return CompareActions.Delete;
                        }
                        if(w1 < 0)
                        {
                            // ignore w1 and use only w2
                            index.Value.EdgePosition1 = subIndex.Value.EdgePosition2;
                        }
                        else if(w2 < 0)
                        {
                            // ignore w2 and use only w1
                            index.Value.EdgePosition2 = subIndex.Value.EdgePosition1;
                        }
                        else
                        {
                            // shortened w1 and insert new w2
                            EdgeItem newItem = new EdgeItem(subIndex.Value.EdgePosition2, index.Value.EdgePosition2, index.Value.Level, _areaLimit1, _areaLimit2);
                            index.Value.EdgePosition2 = subIndex.Value.EdgePosition1;
                            index.List.AddAfter(index, newItem);
                            int idx = edges.FindIndex(edge => index.Value == edge);
                            edges.Insert(idx + 1, newItem);
                        }
                        break;
                }
                subIndex = subIndex.Next;
            }
            return CompareActions.None;
        }

        /// <summary> Expand size of the edge to it's nax size. </summary>
        /// <param name="index"> Zero-based index of the. </param>
        private void GetExpandedEdgeItem(LinkedListNode<EdgeItem> index)
        {
            LinkedListNode<EdgeItem> subIndex = index.Next;
            while (subIndex != null)
            {
                index.Value.CompareAndAdjustWing(subIndex.Value);
                subIndex = subIndex.Next;
            }
            subIndex = index.Previous;
            while (subIndex != null)
            {
                index.Value.CompareAndAdjustWing(subIndex.Value);
                subIndex = subIndex.Previous;
            }
        }

        /// <summary> Adds an edge to the edge list and recalculates edge collection. </summary>
        /// <param name="rect"> The rectangle. </param>
        private void AddEdgeToCollection(Rect rect)
        {
            _linkedList.AddLast(rect);
            if (_edgeItemList == null)
            {
                _edgeItemList = new List<EdgeItem>();
                _edgeItemList.Add(new EdgeItem(_areaLimit1, _areaLimit2, _minAreaLevel, _areaLimit1, _areaLimit2));
            }
            _edgeItemList.Add(_transposed ? new EdgeItem(rect.Top, rect.Bottom, rect.Right, _areaLimit1, _areaLimit2) : new EdgeItem(rect.Left, rect.Right, rect.Bottom, _areaLimit1, _areaLimit2));
            _edgeItemList = _edgeItemList.AsEnumerable().OrderBy(r => r.Level).ToList();

            // adjust item sizes if overlapped by item with greater edge, removing items that are too small or completely overlapped
            _edgeItemCollection = new LinkedList<EdgeItem>(_edgeItemList);
            LinkedListNode<EdgeItem> index = _edgeItemCollection.First;
            while (index != null)
            {
                if (CheckForOverlap(index, _edgeItemList) == CompareActions.Delete)
                {
                    _edgeItemList.Remove(index.Value);
                }
                index = index.Next;
            }

            // calculate extents of each edge
            _edgeItemCollection = new LinkedList<EdgeItem>(_edgeItemList);
            index = _edgeItemCollection.First;
            while (index != null)
            {
                GetExpandedEdgeItem(index);
                index = index.Next;
            }

            // level off close edges
            index = _edgeItemCollection.First;
            while ((index != null)/* && (index.Next != null)*/)
            {
                List<EdgeItem> closeItems = _edgeItemCollection.Where(edge => index.Value.CanBeMerged(edge, AlignLimit, AlignOnLesserEdge)).ToList();
                foreach (var closeItem in closeItems)
                {
                    closeItem.Level = index.Value.Level;
                    closeItem.EdgeLimit1 = _areaLimit1;
                    closeItem.EdgeLimit2 = _areaLimit2;
                    var closeIndex = index.List.Find(closeItem);
                    GetExpandedEdgeItem(closeIndex);
                }
                index = index.Next;
            }
        }

        /// <summary> Inserts an edge described by size. </summary>
        /// <param name="size"> The size. </param>
        /// <returns> . </returns>
        public Point InsertEdge(Size size)
        {
            Rect r;
            // handle first element
            if (_linkedList.Count == 0)
            {
                _edgeItemList = null;
                _edgeItemCollection = null;
                r = _transposed ? new Rect(new Point(_minAreaLevel, _areaLimit1), size) : new Rect(new Point(_areaLimit1, _minAreaLevel), size);
                AddEdgeToCollection(r);
                return r.TopLeft;
            }
            LinkedListNode<EdgeItem> index = _edgeItemCollection.First;
            while (index != null)
            {
                Point? pt = _transposed ? index.Value.FitBlock(size.Height, true) : index.Value.FitBlock(size.Width, false);
                if (pt.HasValue)
                {
                    AddEdgeToCollection(new Rect(pt.Value, size));
                    return pt.Value;
                }
                index = index.Next;
            }
            Size sz = BoundingArea();
            r = _transposed ? new Rect(new Point(sz.Width + _minAreaLevel, _areaLimit1), size) : new Rect(new Point(_areaLimit1, sz.Height + _minAreaLevel), size);
            AddEdgeToCollection(r);
            return r.TopLeft;
        }

        public Size BoundingArea()
        {
            if(_linkedList.Count == 0)
            {
                if(_transposed)
                {
                    return new Size(_minAreaLevel, _areaLimit1);
                }
                return new Size(_areaLimit1, _minAreaLevel);
            }
            double right = _linkedList.AsEnumerable().Select(r => r.Right).Max();
            double bottom = _linkedList.AsEnumerable().Select(r => r.Bottom).Max();
            if (_transposed)
            {
                return new Size(right - _minAreaLevel, bottom - _areaLimit1);
            }
            return new Size(right - _areaLimit1, bottom - _minAreaLevel);
        }
    }

    protected override Size ArrangeOverride(Size finalSize)
    {
        SpaceManager spaceManager = (Orientation == OrientationCategories.Vertical) ? new SpaceManager(finalSize.Width, false) : new SpaceManager(finalSize.Height, true);
        spaceManager.AlignLimit = AlignmentBoundary;
        spaceManager.AlignOnLesserEdge = AlignOnLesserEdge;

        foreach (UIElement child in Children)
        {
            Point point = spaceManager.InsertEdge(child.DesiredSize);
            child.Arrange(new Rect(point.X, point.Y, child.DesiredSize.Width, child.DesiredSize.Height));
        }
        return spaceManager.BoundingArea();
    }

    protected override Size MeasureOverride(Size availableSize)
    {
        SpaceManager spaceManager = (Orientation == OrientationCategories.Vertical) ? new SpaceManager(availableSize.Width, false) : new SpaceManager(availableSize.Height, true);
        spaceManager.AlignLimit = AlignmentBoundary;
        spaceManager.AlignOnLesserEdge = AlignOnLesserEdge;

        foreach (UIElement child in Children)
        {
            child.Measure(availableSize);
            spaceManager.InsertEdge(child.DesiredSize);
        }
        Size size = spaceManager.BoundingArea();
        return new Size(double.IsPositiveInfinity(availableSize.Width) ? size.Width : availableSize.Width, double.IsPositiveInfinity(availableSize.Height) ? size.Height : availableSize.Height);
    }

}

到目前为止我的尝试:

UIElement
更改为
Control
,将
DependencyProperty
更改为
AvaloniaProperty
,但我不明白
DependencyProperty
AvaloniaProperty
,无法弄清楚如何转换它

c# wpf dependency-properties avaloniaui avalonia
1个回答
0
投票

有人有好的三文鱼食谱吗?

© www.soinside.com 2019 - 2024. All rights reserved.