這個例子來自書上。 記錄過程。 主要是數學上極坐標,WPF中的測量過程 簡單來說在一個具有固定軸的坐標系內,一個由原點射出的向量並與固定軸有一定角度且在向量上確定長度的這麼個東西。 可以參考: 知乎https://www.zhihu.com/question/318613418/answer/640 ...
這個例子來自書上。
記錄過程。
主要是數學上極坐標,WPF中的測量過程
簡單來說在一個具有固定軸的坐標系內,一個由原點射出的向量並與固定軸有一定角度且在向量上確定長度的這麼個東西。
可以參考:
知乎https://www.zhihu.com/question/318613418/answer/640194590
B站https://www.bilibili.com/video/BV1Sb411n7FG?t=177
極坐標與直角坐標系轉換。
極坐標中某一點是M,也就是M(ρ,θ)。
將M連接至原點成為一個線段L1,將此線段放置直角坐標系,其中M點變為點M1(X,Y)。
此時我們可以利用三角函數確定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>