WPF 圆型布局(测量过程)

这个例子来自书上。html

记录过程。ide

主要是数学上极坐标,WPF中的测量过程函数

简单来讲在一个具备固定轴的坐标系内,一个由原点射出的向量并与固定轴有必定角度且在向量上肯定长度的这么个东西。布局

能够参考:this

知乎https://www.zhihu.com/question/318613418/answer/640194590spa

B站https://www.bilibili.com/video/BV1Sb411n7FG?t=1773d

极坐标与直角坐标系转换。code

极坐标中某一点是M,也就是M(ρ,θ)。orm

将M链接至原点成为一个线段L1,将此线段放置直角坐标系,其中M点变为点M1(X,Y)。htm

此时咱们能够利用三角函数肯定X,Y

X=ρ*cosθ=L1*X[点在X轴的垂线坐标]/L1

Y=ρ*sinθ=L1*Y[点在Y轴的垂线坐标]/L1

L1也能够理解为半径

那么直角坐标转换为极坐标则是M(X,Y),一样咱们需使用圆的标准方程(以前的极坐标转直角中的L1,原本就应该是R【半径】,不过我不太喜欢这么快,先用线段,这么好理解) R2=X2+Y2

另外还有三角函数tan,对边比邻边 ,(坐标系内)y/x

ρ=根号下X2+Y2

θ=tan=y/x

 

剩下就是WPF的测量过程

没什么好说,第一步是测量,第二步是排列。

第一步主要是Measure方法,可是主要是经过MeasureOverride方法来肯定,这个方法自己是预计UI的大小,Measure是没有返回值,可是有形参,是这个控件的父控件留给这个控件的可用空间,

若是控件重写measureoverride方法,这个方法的形参就是Measure的形参,同时也会和Measure造成递归布局更新,

第二步是Arrange,是最终确认UI控件的大小,一般是经过ArrageOvrride方法确认,过程和第一步差很少,只不过形参和返回值不一样。由于要定位,因此是Rect。

 

对于Measure是父控件给子控件赋值通知子控件你可用大小

MeasureOverride是测量自身大小并返回通知父控件我预计用这么大(DesiredSize)

对于Arrange是父控件决定子控件的位置和大小

ArrangeOverride是肯定自身大小和位置而后返回最终的大小(finalsize【在这以前会有rendersize】)

具体过程还能够参考https://www.cnblogs.com/powertoolsteam/archive/2011/01/10/1932036.html

 

差很少就这么多

class Test : Panel
    {
        public static readonly DependencyProperty RadiusProperty = DependencyProperty.Register("Radius", typeof(double), typeof(Test), new PropertyMetadata(0.0, new PropertyChangedCallback(OnValueChanged)));
        public double Radius
        {
            get => Convert.ToDouble(GetValue(RadiusProperty));
            set => SetValue(RadiusProperty, value);
        }


        private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as Test).Radius = Convert.ToDouble(e.NewValue);
            (d as Test).InvalidateArrange();
        }
      
        protected override Size MeasureOverride(Size availableSize)
        {
           
            double MaxElementWidth = 0;
            foreach(UIElement item in Children)
            {
//给定子控件的可用空间 item.Measure(availableSize); MaxElementWidth
= Math.Max(item.DesiredSize.Width, MaxElementWidth); } double PanelWidth = 2 * this.Radius+2 * MaxElementWidth; double width = Math.Min(PanelWidth, availableSize.Width); double height = Math.Min(PanelWidth, availableSize.Height); return new Size(width, height); } protected override Size ArrangeOverride(Size finalSize) { double Degree = 0; double DegreeSrep = (double)360 / this.Children.Count; double X = this.DesiredSize.Width / 2; double Y = this.DesiredSize.Height / 2; foreach(UIElement Item in Children) { //角度转弧度 double angle = Math.PI * Degree / 180.0; //转换为直角坐标系 r*cos double x = Math.Cos(angle) * this.Radius; //转换为直角坐标系 r*sin double y = Math.Sin(angle) * this.Radius; RotateTransform rotate = new RotateTransform(); rotate.Angle = Degree; rotate.CenterX = 0; rotate.CenterY = 0; Item.RenderTransform = rotate; //决定子控件的位置和大小 Item.Arrange(new Rect(X + x, Y + y, Item.DesiredSize.Width, this.DesiredSize.Height)); Degree += DegreeSrep; } return finalSize; } }

 

xaml

<Grid>
        <local:Test Radius="50">
            <TextBlock Text="abc"/>
            <TextBlock Text="abc"/>
            <TextBlock Text="abc"/>
            <TextBlock Text="abc"/>
            <TextBlock Text="abc"/>
            <TextBlock Text="abc"/>
            <TextBlock Text="abc"/>
            <TextBlock Text="abc"/>
            <TextBlock Text="abc"/>
            <TextBlock Text="abc"/>
            <TextBlock Text="abc"/>
            <TextBlock Text="abc"/>
            <TextBlock Text="abc"/>
            <TextBlock Text="abc"/>
            <TextBlock Text="abc"/>
            <TextBlock Text="abc"/>
        </local:Test>
    </Grid>