最近新做了一个倒计时按钮,感觉跟游戏里的技能冷却图标挺像的。
整理了一下,先上demo。
主要用了控件的裁剪功能。
控件的XAML
其中,文本控件是用来显示倒计时用的。冷却按钮里面没有,所以隐藏掉了。
1 <UserControl x:Class="NingTao.coolDownButton" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 4 <Grid> 5 6 <Image Margin="0" Name="imageBtnBack" MouseUp="imageBtnBack_MouseUp"></Image> 7 <Image Margin="0" Name="imageBtnFront" IsHitTestVisible="False"></Image> 8 <Label Margin="0,0,0,0" Name="labelCounter" IsHitTestVisible="False" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Foreground="Red" FontSize="26" Visibility="Hidden">0</Label> 9 10 </Grid> 11 </UserControl>
控件的CS文件
其中,把以前的检测无动作功能去掉,以前是有动作就从新计时。
注释掉的部分是反方向旋转的,因为跟常见的冷却方向不一致,所以更改方向。
虽然代码不是很多,可是考虑cos和sin的时候也挺费工夫的。
弧线的起点和终点不一样,弧线的圆心在哪一侧也是不一样的。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Windows; 6 using System.Windows.Controls; 7 using System.Windows.Data; 8 using System.Windows.Documents; 9 using System.Windows.Input; 10 using System.Windows.Media; 11 using System.Windows.Media.Imaging; 12 using System.Windows.Navigation; 13 using System.Windows.Shapes; 14 using System.Runtime.InteropServices; 15 16 namespace NingTao 17 { 18 /// <summary> 19 /// UserControl1.xaml 的交互逻辑 20 /// </summary> 21 public partial class coolDownButton : UserControl 22 { 23 // 按钮的背景前景图片 24 private ImageSource imageBack = null; 25 private ImageSource imageFront = null; 26 // 文本属性 27 private FontFamily textFamily; 28 private double textSize; 29 private Brush textColor; 30 // 显示倒计时的计时器 31 private System.Timers.Timer msgTimer = new System.Timers.Timer(); 32 // 计时器到期委托调用 33 private delegate void invokeTimerTrick(); 34 // 倒计时最大值 35 private double maxCounter = 0; 36 // 无动作时间 37 private double nowCounter = 0; 38 // 点击事件 或者倒计时结束触发 39 public event EventHandler Click = null; 40 // 是否由点击触发(可能为页面后退事件) 41 public bool isClickEvent = false; 42 43 // 按钮背景图片 44 public ImageSource ImageBack 45 { 46 get { return imageBack; } 47 set 48 { 49 imageBack = value; 50 imageBtnBack.Source = imageBack; 51 } 52 } 53 // 按钮前景图片 54 public ImageSource ImageFront 55 { 56 get { return imageFront; } 57 set 58 { 59 imageFront = value; 60 imageBtnFront.Source = imageFront; 61 } 62 } 63 // 文本字体 64 public FontFamily TextFamily 65 { 66 get { return textFamily; } 67 set 68 { 69 textFamily = value; 70 labelCounter.FontFamily = textFamily; 71 } 72 } 73 // 文本字号 74 public double TextSize 75 { 76 get { return textSize; } 77 set 78 { 79 textSize = value; 80 labelCounter.FontSize = textSize; 81 } 82 } 83 // 文本颜色 84 public Brush TextColor 85 { 86 get { return textColor; } 87 set 88 { 89 textColor = value; 90 labelCounter.Foreground = textColor; 91 } 92 } 93 // 倒计时最大值 94 public double MaxCounter 95 { 96 get { return maxCounter; } 97 set 98 { 99 nowCounter = 0; 100 maxCounter = value; 101 // 启动计时器 102 msgTimer.Start(); 103 } 104 } 105 106 public coolDownButton() 107 { 108 InitializeComponent(); 109 110 // 初始化海报倒计时计数器 111 msgTimer.Elapsed += new System.Timers.ElapsedEventHandler(msgTimer_Elapsed); 112 msgTimer.Interval = 200; 113 } 114 115 // 计数器到期 116 private void msgTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) 117 { 118 //nowCounter = GetIdleTick() / 1000; 119 nowCounter += 0.2; 120 if (nowCounter < maxCounter + 0.2) 121 { 122 Dispatcher.Invoke(new invokeTimerTrick(reDraw)); 123 } 124 else 125 { 126 isClickEvent = false; 127 Dispatcher.Invoke(new invokeTimerTrick(buttonClick)); 128 } 129 } 130 131 private void reDraw() 132 { 133 // 显示剩余时间 134 int timeLeft = Convert.ToInt32(maxCounter - nowCounter); 135 int intMinute = timeLeft / 60; 136 int intSecond = timeLeft % 60; 137 labelCounter.Content = string.Format("{0:D2}:{1:D2}",intMinute, intSecond); 138 // 裁剪剩余时间扇形区域 139 double angle = 360 - 360 * nowCounter / maxCounter; 140 if (360 == angle) 141 { 142 imageBtnFront.Clip = new EllipseGeometry(new Rect(0, 0, imageBtnFront.ActualWidth, imageBtnFront.ActualHeight)); 143 } 144 else 145 { 146 //PathGeometry PathGeometry1 = new PathGeometry(); 147 //PathFigure PathFigure1 = new PathFigure(); 148 //PathFigure1.StartPoint = new Point(imageBtnFront.ActualWidth / 2, imageBtnFront.ActualHeight / 2); 149 //LineSegment LineSegment1 = new LineSegment(); 150 //LineSegment1.Point = new Point(imageBtnFront.ActualWidth / 2, 0); 151 //PathFigure1.Segments.Add(LineSegment1); 152 //double offsetX = Math.Sin(angle * Math.PI / 180); 153 //double offsetY = Math.Cos(angle * Math.PI / 180); 154 //Point endPoint = new Point(imageBtnFront.ActualWidth * (1 + offsetX) / 2, imageBtnFront.ActualHeight * (1 - offsetY) / 2); 155 //ArcSegment ArcSegment1 = new ArcSegment( 156 // endPoint, 157 // new Size(imageBtnFront.ActualWidth / 2, imageBtnFront.ActualHeight / 2), 158 // angle, 159 // (angle > 180), 160 // SweepDirection.Clockwise, 161 // true); 162 //PathFigure1.Segments.Add(ArcSegment1); 163 //PathGeometry1.Figures.Add(PathFigure1); 164 //imageBtnFront.Clip = PathGeometry1; 165 // 顺时针 166 PathGeometry PathGeometry1 = new PathGeometry(); 167 PathFigure PathFigure1 = new PathFigure(); 168 // 起点为中心点 169 PathFigure1.StartPoint = new Point(imageBtnFront.ActualWidth / 2, imageBtnFront.ActualHeight / 2); 170 // 连接到扇形的圆弧起点 171 LineSegment LineSegment1 = new LineSegment(); 172 double offsetX = Math.Sin(angle * Math.PI / 180); 173 double offsetY = Math.Cos(angle * Math.PI / 180); 174 LineSegment1.Point = new Point(imageBtnFront.ActualWidth * (1 - offsetX) / 2, imageBtnFront.ActualHeight * (1 - offsetY) / 2); 175 PathFigure1.Segments.Add(LineSegment1); 176 // 圆弧 终点在正上方 177 ArcSegment ArcSegment1 = new ArcSegment( 178 new Point(imageBtnFront.ActualWidth / 2, 0), 179 new Size(imageBtnFront.ActualWidth / 2, imageBtnFront.ActualHeight / 2), 180 angle, 181 (angle > 180), 182 SweepDirection.Clockwise, 183 true); 184 PathFigure1.Segments.Add(ArcSegment1); 185 // 对图像控件进行裁剪 186 PathGeometry1.Figures.Add(PathFigure1); 187 imageBtnFront.Clip = PathGeometry1; 188 } 189 } 190 191 // 点击事件 192 private void imageBtnBack_MouseUp(object sender, MouseButtonEventArgs e) 193 { 194 isClickEvent = true; 195 buttonClick(); 196 } 197 198 private void buttonClick() 199 { 200 // 停止计时器 201 msgTimer.Stop(); 202 // 触发点击事件 203 if (null != Click) Click(null, null); 204 } 205 206 public void stopCount() 207 { 208 // 停止计时器 209 msgTimer.Stop(); 210 } 211 212 [DllImport("user32.dll")] 213 static extern bool GetLastInputInfo(ref LASTINPUTINFO plii); 214 215 /// <summary> 216 /// 获取最后一次输入经过的时间 217 /// </summary> 218 /// <returns></returns> 219 private int GetIdleTick() 220 { 221 LASTINPUTINFO lastInputInfo = new LASTINPUTINFO(); 222 lastInputInfo.cbSize = Marshal.SizeOf(lastInputInfo); 223 if (!GetLastInputInfo(ref lastInputInfo)) return 0; 224 return Environment.TickCount - (int)lastInputInfo.dwTime; 225 } 226 227 [StructLayout(LayoutKind.Sequential)] 228 private struct LASTINPUTINFO 229 { 230 [MarshalAs(UnmanagedType.U4)] 231 public int cbSize; 232 [MarshalAs(UnmanagedType.U4)] 233 public uint dwTime; 234 } 235 } 236 }
调用:
先添加引用coolDownButton.dll。
前边是xaml里的调用部分,后边是动态加载图片和设置点击事件。
1 // xaml 2 // xmlns:coolDownButton="clr-namespace:NingTao;assembly=coolDownButton" 3 //<coolDownButton:coolDownButton x:Name="cdButton" HorizontalAlignment="Right" Width="200" Margin="0,12,12,50"></coolDownButton:coolDownButton> 4 5 // ================ 冷却按钮 ================ 6 cdButton.ImageBack = new BitmapImage(new Uri(System.Environment.CurrentDirectory + "\\skins\\a1.png")); 7 cdButton.ImageFront = new BitmapImage(new Uri(System.Environment.CurrentDirectory + "\\skins\\a2.png")); 8 cdButton.Click += new EventHandler(cdButton_Click); 9 10 void cdButton_Click(object sender, EventArgs e) 11 { 12 // 按钮被点击 13 if (cdButton.isClickEvent) 14 { 15 cdButton.MaxCounter = Convert.ToDouble(textBox1.Text); 16 } 17 else 18 { 19 MessageBox.Show("冷却完成"); 20 } 21 }