zoukankan      html  css  js  c++  java
  • 开篇!WPF自定义控件(1)——转盘菜单

    注册园子账号有半年了吧,一直想写点什么,但不知道从哪写起。最近一个月在无锡一个公司实习,主要做的就是WPF相关的开发,虽然之前没接触过WPF,但好歹学过C#,上手理解起来还算容易。

    最近做了个自定义圆形转盘菜单控件,效果如下:

    实现过程:

    1. 圆周平分孩子节点(这里孩子节点放的都是图标)
    2. 实现点击中间图标,外围图标旋转一周动画
    3. 实现鼠标点击拖动跟随旋转动画

    关键代码:

      1.圆周平分孩子节点。先遍历所有孩子节点,再减去中间图标,得到要平分圆周的图标个数,然后得到平分角度,最后根据角度、外圆半径,利用三角函数设置每个图标的TopPropertyLeftProperty属性

     1 //等分圆周 显示中间和周围图标
     2         private void DivideCircleOnShowMenu()
     3         {
     4             //获取孩子节点
     5             UIElementCollection children = this.Children;
     6             FrameworkElement f;
     7             
     8             //外圆半径
     9             double outCircleRadius = this.Width/2-IconWidthAndHeight/2;
    10 
    11             //平分度数
    12             double divideEquallyAngle = 360 / (children.Count - 1);
    13 
    14             for (int i = 0; i < children.Count; i++)
    15             {
    16                 f = children[i] as FrameworkElement;
    17                 //第一个中间图标
    18                 if (i == 0)
    19                 {
    20                     if (ShowMenu)
    21                     {
    22                         f.SetValue(Canvas.TopProperty, outCircleRadius );
    23                         f.SetValue(Canvas.LeftProperty, outCircleRadius );
    24                         if (OperateType == OperateEnum.MouseOperate)
    25                         {
    26                             f.MouseDown += F_MouseDown;
    27                         }
    28                         else
    29                         {
    30                             f.TouchUp += F_TouchUp;
    31                         }
    32                     }
    33 
    34                     else
    35                     {
    36                         f.SetValue(Canvas.TopProperty, outCircleRadius - f.Width / 2);
    37                         f.SetValue(Canvas.LeftProperty, outCircleRadius - f.Width / 2);
    38                         f.Visibility = Visibility.Hidden;
    39                     }
    40                 }
    41 
    42                 else
    43                 {
    44                     //内角度数  角度转换为弧度
    45                     double innerAngle = divideEquallyAngle * (i-1) * Math.PI / 180;
    46 
    47                     //TOP距离
    48                     double topHeight = outCircleRadius - Math.Cos(innerAngle) * outCircleRadius;
    49 
    50                     //Left距离
    51                     double leftWidth = Math.Sin(innerAngle) * outCircleRadius;
    52 
    53                     if (innerAngle <= 180)
    54                     {
    55                         f.SetValue(Canvas.TopProperty, topHeight );
    56                         f.SetValue(Canvas.LeftProperty, outCircleRadius + leftWidth );
    57                     }
    58                     if (innerAngle > 180)
    59                     {
    60                         f.SetValue(Canvas.TopProperty, topHeight);
    61                         f.SetValue(Canvas.LeftProperty, outCircleRadius - leftWidth );
    62                     }
    63                 }
    64             }
    65         }

       2.点击中间图标,外围图标旋转一周动画。这里用到的是RotateTransform动画,动画很简单,就是绕点旋转。但是还得设置每个图标的RenderTransformOrigin,这个属性是一个相对Point,以每个图标的左上角为坐标原点,向下和向右为正,比如设为(0.5,0.5)表示每个图标绕本身的中心点旋转,(1,1)表示绕本身的右下角旋转。这里我是选择让整个canvas绕着自身中心点旋转,然后再让每个图标绕自身反转,就可以实现图标方向相对不变。最后我这里rotateNumber(旋转度数)是自定义的依赖属性,可以在前台XAML应用这个控件时自行设置的。

     1  //canvans旋转
     2         private void RotateAnimation(UIElement uIelement, double rotateNumber)
     3         {
     4             RotateTransform rtf = new RotateTransform();
     5             uIelement.RenderTransform = rtf;
     6             uIelement.RenderTransformOrigin = new Point(0.5, 0.5);
     7 
     8             //定义动画路径和事件
     9             DoubleAnimation dbAnimation = new DoubleAnimation(0, rotateNumber,
    10                 new Duration(TimeSpan.FromSeconds(RotateSpeed)));
    11 
    12             //开始动画
    13             rtf.BeginAnimation(RotateTransform.AngleProperty, dbAnimation);

      3.实现鼠标拖动跟随旋转动画。这里主要就是MouseDown,MouseMove和MouseUp事件。主要是MouseMove中要获取到每次移动旋转的度数,以及转动方向的判断(顺时针转还是逆时针转)。求每次拖动旋转的度数我用到了高中的数学知识,两向量夹角的余弦公式(没错,就是它了,这也是我目前能想到的办法了= =)。根据这个公式算出旋转度数后,就要开始根据每次点击点和结束点的象限来判断旋转方向。

      

    //拖动图标旋转事件
            private void Rotate_MouseMove(object sender, MouseEventArgs e)
            {
                Point mAfter = e.GetPosition(this); //获取鼠标移动过程中的坐标
    
                Point n1 = new Point(centerP.X - mBefore.X, centerP.Y - mBefore.Y);
                Point n2 = new Point(centerP.X - mAfter.X, centerP.Y - mAfter.Y);
    
                //n1*n2
                double n1n2 = n1.X * n2.X + n1.Y * n2.Y;
                //n1的模
                double n1mo = Math.Sqrt(Math.Pow(n1.X, 2) + Math.Pow(n1.Y, 2));
                //n2的模
                double n2mo = Math.Sqrt(Math.Pow(n2.X, 2) + Math.Pow(n2.Y, 2));
    
    
                //得带旋转角度
                double rotateNum = Math.Acos(n1n2 / (n1mo * n2mo));
    
                //相对坐标原点位置
                Point potM = new Point();
                potM.X = mAfter.X - centerP.X;
                potM.Y = centerP.Y - mAfter.Y;
                Point potD = new Point();
                potD.X = mBefore.X - centerP.X;
                potD.Y = centerP.Y - mBefore.Y;
    
    
                //当鼠标移动超出边界时停止旋转
                if (mAfter.X < 0 || mAfter.X > this.Width || mAfter.Y < 0 || mAfter.Y > this.Height)
                {
                    this.MouseMove -= Rotate_MouseMove;
    
                }
    
                else
                {
                    if (GetcLockwise(potD, potM))
                    {
                        rotateAng += rotateNum;
                    }
                    else
                    {
                        rotateAng -= rotateNum;
                    }
                }
    
                //执行旋转动画
    
                IconRotateAnimation(-rotateAng);
                CanvansRotateAnimation(rotateAng);
    
            }
    
            /// <summary>
            /// 获取顺时针还是逆时针
            /// </summary>
            /// <param name="potD">按下坐标</param>
            /// <param name="potM">移动坐标</param>
            /// <returns>True:顺,False:逆</returns>
            private bool GetcLockwise(Point potD, Point potM)
            {
                if (potM.Y >= 0 && potD.Y >= 0) //一二象限
                {
                    return potM.X >= potD.X;
                }
                if (potM.Y < 0 && potD.Y < 0)   //三四象限
                {
                    return potM.X <= potD.X;
                }
                if (potM.X >= 0 && potD.X >= 0) //一四象限
                {
                    return potM.Y <= potD.Y;
                }
                if (potM.X < 0 && potD.X < 0)   //二三象限
                {
                    return potM.Y >= potD.Y;
                }
                else
                {
                    return true;
                }
            }    

      最后,又增加了触摸滑动功能,其实大体上是就是把对应的Mouse事件换成Touch事件就OK了,但效果实现上可能需要略微的改动。本来还要做成旋转带惯性的效果的,只可惜太菜,网上资源也比较少,只能无告而终。现在觉得做WPF的控件开发也蛮有意思的,蛮锻炼人的逻辑思维能力的。开篇文到此为止了,再接再励吧!

    附上Demo源码:旋转菜单控件

  • 相关阅读:
    Web开发中的显示与隐藏
    Html中的表格
    go标准库的学习-encoding/json
    go-simplejson文档学习
    go标准库的学习-regexp
    go标准库的学习-net
    go标准库的学习-strconv-字符串转换
    go标准库的学习-strings-字符串操作
    go标准库的学习-net/rpc
    go标准库的学习-net/rpc/jsonrpc
  • 原文地址:https://www.cnblogs.com/xiaobo5630/p/4254644.html
Copyright © 2011-2022 走看看