PopTip.cs 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070
  1. using System;
  2. using System.Runtime.InteropServices;
  3. using System.Windows;
  4. using System.Windows.Controls;
  5. using System.Windows.Controls.Primitives;
  6. using System.Windows.Media;
  7. namespace ComPDFKit.Controls.Common.BaseControl
  8. {
  9. #region EnumPlacement
  10. public enum EnumPlacement
  11. {
  12. /// <summary>
  13. /// 左上
  14. /// </summary>
  15. LeftTop,
  16. /// <summary>
  17. /// 左中
  18. /// </summary>
  19. LeftCenter,
  20. /// <summary>
  21. /// 左下
  22. /// </summary>
  23. LeftBottom,
  24. /// <summary>
  25. /// 右上
  26. /// </summary>
  27. RightTop,
  28. /// <summary>
  29. /// 右中
  30. /// </summary>
  31. RightCenter,
  32. /// <summary>
  33. /// 右下
  34. /// </summary>
  35. RightBottom,
  36. /// <summary>
  37. /// 上左
  38. /// </summary>
  39. TopLeft,
  40. /// <summary>
  41. /// 上中
  42. /// </summary>
  43. TopCenter,
  44. /// <summary>
  45. /// 上右
  46. /// </summary>
  47. TopRight,
  48. /// <summary>
  49. /// 下左
  50. /// </summary>
  51. BottomLeft,
  52. /// <summary>
  53. /// 下中
  54. /// </summary>
  55. BottomCenter,
  56. /// <summary>
  57. /// 下右
  58. /// </summary>
  59. BottomRight,
  60. }
  61. #endregion EnumPlacement
  62. public class DoubleUtil
  63. {
  64. public static double DpiScaleX
  65. {
  66. get
  67. {
  68. int dx = 0;
  69. int dy = 0;
  70. GetDPI(out dx, out dy);
  71. if (dx != 96)
  72. {
  73. return (double)dx / 96.0;
  74. }
  75. return 1.0;
  76. }
  77. }
  78. public static double DpiScaleY
  79. {
  80. get
  81. {
  82. int dx = 0;
  83. int dy = 0;
  84. GetDPI(out dx, out dy);
  85. if (dy != 96)
  86. {
  87. return (double)dy / 96.0;
  88. }
  89. return 1.0;
  90. }
  91. }
  92. public static void GetDPI(out int dpix, out int dpiy)
  93. {
  94. dpix = 0;
  95. dpiy = 0;
  96. using (System.Management.ManagementClass mc = new System.Management.ManagementClass("Win32_DesktopMonitor"))
  97. {
  98. using (System.Management.ManagementObjectCollection moc = mc.GetInstances())
  99. {
  100. foreach (System.Management.ManagementObject each in moc)
  101. {
  102. dpix = int.Parse((each.Properties["PixelsPerXLogicalInch"].Value.ToString()));
  103. dpiy = int.Parse((each.Properties["PixelsPerYLogicalInch"].Value.ToString()));
  104. }
  105. }
  106. }
  107. }
  108. public static bool AreClose(double value1, double value2)
  109. {
  110. if (value1 == value2)
  111. {
  112. return true;
  113. }
  114. double num = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * 2.2204460492503131E-16;
  115. double num2 = value1 - value2;
  116. return -num < num2 && num > num2;
  117. }
  118. [StructLayout(LayoutKind.Explicit)]
  119. private struct NanUnion
  120. {
  121. [FieldOffset(0)]
  122. internal double DoubleValue;
  123. [FieldOffset(0)]
  124. internal ulong UintValue;
  125. }
  126. public static bool IsNaN(double value)
  127. {
  128. DoubleUtil.NanUnion nanUnion = default(DoubleUtil.NanUnion);
  129. nanUnion.DoubleValue = value;
  130. ulong num = nanUnion.UintValue & 18442240474082181120uL;
  131. ulong num2 = nanUnion.UintValue & 4503599627370495uL;
  132. return (num == 9218868437227405312uL || num == 18442240474082181120uL) && num2 != 0uL;
  133. }
  134. }
  135. public class UIElementEx
  136. {
  137. public static double RoundLayoutValue(double value, double dpiScale)
  138. {
  139. double num;
  140. if (!DoubleUtil.AreClose(dpiScale, 1.0))
  141. {
  142. num = Math.Round(value * dpiScale) / dpiScale;
  143. if (DoubleUtil.IsNaN(num) || double.IsInfinity(num) || DoubleUtil.AreClose(num, 1.7976931348623157E+308))
  144. {
  145. num = value;
  146. }
  147. }
  148. else
  149. {
  150. num = Math.Round(value);
  151. }
  152. return num;
  153. }
  154. }
  155. /// <summary>
  156. /// 带三角形的气泡边框
  157. /// </summary>
  158. public class AngleBorder : Decorator
  159. {
  160. #region 依赖属性
  161. public static readonly DependencyProperty PlacementProperty =
  162. DependencyProperty.Register("Placement", typeof(EnumPlacement), typeof(AngleBorder),
  163. new FrameworkPropertyMetadata(EnumPlacement.RightCenter, FrameworkPropertyMetadataOptions.AffectsRender, OnDirectionPropertyChangedCallback));
  164. public EnumPlacement Placement
  165. {
  166. get { return (EnumPlacement)GetValue(PlacementProperty); }
  167. set { SetValue(PlacementProperty, value); }
  168. }
  169. public static readonly DependencyProperty TailWidthProperty =
  170. DependencyProperty.Register("TailWidth", typeof(double), typeof(AngleBorder), new PropertyMetadata(10d));
  171. /// <summary>
  172. /// 尾巴的宽度,默认值为7
  173. /// </summary>
  174. public double TailWidth
  175. {
  176. get { return (double)GetValue(TailWidthProperty); }
  177. set { SetValue(TailWidthProperty, value); }
  178. }
  179. public static readonly DependencyProperty TailHeightProperty =
  180. DependencyProperty.Register("TailHeight", typeof(double), typeof(AngleBorder), new PropertyMetadata(10d));
  181. /// <summary>
  182. /// 尾巴的高度,默认值为10
  183. /// </summary>
  184. public double TailHeight
  185. {
  186. get { return (double)GetValue(TailHeightProperty); }
  187. set { SetValue(TailHeightProperty, value); }
  188. }
  189. public static readonly DependencyProperty TailVerticalOffsetProperty =
  190. DependencyProperty.Register("TailVerticalOffset", typeof(double), typeof(AngleBorder), new PropertyMetadata(13d));
  191. /// <summary>
  192. /// 尾巴距离顶部的距离,默认值为10
  193. /// </summary>
  194. public double TailVerticalOffset
  195. {
  196. get { return (double)GetValue(TailVerticalOffsetProperty); }
  197. set { SetValue(TailVerticalOffsetProperty, value); }
  198. }
  199. public static readonly DependencyProperty TailHorizontalOffsetProperty =
  200. DependencyProperty.Register("TailHorizontalOffset", typeof(double), typeof(AngleBorder),
  201. new PropertyMetadata(12d));
  202. /// <summary>
  203. /// 尾巴距离顶部的距离,默认值为10
  204. /// </summary>
  205. public double TailHorizontalOffset
  206. {
  207. get { return (double)GetValue(TailHorizontalOffsetProperty); }
  208. set { SetValue(TailHorizontalOffsetProperty, value); }
  209. }
  210. public static readonly DependencyProperty BackgroundProperty =
  211. DependencyProperty.Register("Background", typeof(Brush), typeof(AngleBorder)
  212. , new PropertyMetadata(new SolidColorBrush(Color.FromRgb(255, 255, 255))));
  213. /// <summary>
  214. /// 背景色,默认值为#FFFFFF,白色
  215. /// </summary>
  216. public Brush Background
  217. {
  218. get { return (Brush)GetValue(BackgroundProperty); }
  219. set { SetValue(BackgroundProperty, value); }
  220. }
  221. public static readonly DependencyProperty PaddingProperty =
  222. DependencyProperty.Register("Padding", typeof(Thickness), typeof(AngleBorder)
  223. , new PropertyMetadata(new Thickness(10, 5, 10, 5)));
  224. /// <summary>
  225. /// 内边距
  226. /// </summary>
  227. public Thickness Padding
  228. {
  229. get { return (Thickness)GetValue(PaddingProperty); }
  230. set { SetValue(PaddingProperty, value); }
  231. }
  232. public static readonly DependencyProperty BorderBrushProperty =
  233. DependencyProperty.Register("BorderBrush", typeof(Brush), typeof(AngleBorder)
  234. , new PropertyMetadata(default(Brush)));
  235. /// <summary>
  236. /// 边框颜色
  237. /// </summary>
  238. public Brush BorderBrush
  239. {
  240. get { return (Brush)GetValue(BorderBrushProperty); }
  241. set { SetValue(BorderBrushProperty, value); }
  242. }
  243. public static readonly DependencyProperty BorderThicknessProperty =
  244. DependencyProperty.Register("BorderThickness", typeof(Thickness), typeof(AngleBorder), new PropertyMetadata(new Thickness(1d)));
  245. /// <summary>
  246. /// 边框大小
  247. /// </summary>
  248. public Thickness BorderThickness
  249. {
  250. get { return (Thickness)GetValue(BorderThicknessProperty); }
  251. set { SetValue(BorderThicknessProperty, value); }
  252. }
  253. public static readonly DependencyProperty CornerRadiusProperty =
  254. DependencyProperty.Register("CornerRadius", typeof(System.Windows.CornerRadius)
  255. , typeof(AngleBorder), new PropertyMetadata(new CornerRadius(0)));
  256. /// <summary>
  257. /// 边框大小
  258. /// </summary>
  259. public System.Windows.CornerRadius CornerRadius
  260. {
  261. get { return (System.Windows.CornerRadius)GetValue(CornerRadiusProperty); }
  262. set { SetValue(CornerRadiusProperty, value); }
  263. }
  264. #endregion 依赖属性
  265. #region 方法重写
  266. /// <summary>
  267. /// 该方法用于测量整个控件的大小
  268. /// </summary>
  269. /// <param name="constraint"></param>
  270. /// <returns>控件的大小</returns>
  271. protected override Size MeasureOverride(Size constraint)
  272. {
  273. Thickness padding = this.Padding;
  274. Size result = new Size();
  275. if (Child != null)
  276. {
  277. //测量子控件的大小
  278. Child.Measure(constraint);
  279. //三角形在左边与右边的,整个容器的宽度则为:里面子控件的宽度 + 设置的padding + 三角形的宽度
  280. //三角形在上面与下面的,整个容器的高度则为:里面子控件的高度 + 设置的padding + 三角形的高度
  281. switch (Placement)
  282. {
  283. case EnumPlacement.LeftTop:
  284. case EnumPlacement.LeftBottom:
  285. case EnumPlacement.LeftCenter:
  286. case EnumPlacement.RightTop:
  287. case EnumPlacement.RightBottom:
  288. case EnumPlacement.RightCenter:
  289. result.Width = Child.DesiredSize.Width + padding.Left + padding.Right + this.TailWidth;
  290. result.Height = Child.DesiredSize.Height + padding.Top + padding.Bottom;
  291. break;
  292. case EnumPlacement.TopLeft:
  293. case EnumPlacement.TopCenter:
  294. case EnumPlacement.TopRight:
  295. case EnumPlacement.BottomLeft:
  296. case EnumPlacement.BottomCenter:
  297. case EnumPlacement.BottomRight:
  298. result.Width = Child.DesiredSize.Width + padding.Left + padding.Right;
  299. result.Height = Child.DesiredSize.Height + padding.Top + padding.Bottom + this.TailHeight;
  300. break;
  301. default:
  302. break;
  303. }
  304. }
  305. return result;
  306. }
  307. /// <summary>
  308. /// 设置子控件的大小与位置
  309. /// </summary>
  310. /// <param name="arrangeSize"></param>
  311. /// <returns></returns>
  312. protected override Size ArrangeOverride(Size arrangeSize)
  313. {
  314. Thickness padding = this.Padding;
  315. if (Child != null)
  316. {
  317. switch (Placement)
  318. {
  319. case EnumPlacement.LeftTop:
  320. case EnumPlacement.LeftBottom:
  321. case EnumPlacement.LeftCenter:
  322. this.TailWidth = 6;
  323. Child.Arrange(new Rect(new Point(padding.Left + this.TailWidth, padding.Top), Child.DesiredSize));
  324. //ArrangeChildLeft();
  325. break;
  326. case EnumPlacement.RightTop:
  327. case EnumPlacement.RightBottom:
  328. case EnumPlacement.RightCenter:
  329. ArrangeChildRight(padding);
  330. break;
  331. case EnumPlacement.TopLeft:
  332. case EnumPlacement.TopRight:
  333. case EnumPlacement.TopCenter:
  334. Child.Arrange(new Rect(new Point(padding.Left, this.TailHeight + padding.Top), Child.DesiredSize));
  335. break;
  336. case EnumPlacement.BottomLeft:
  337. case EnumPlacement.BottomRight:
  338. case EnumPlacement.BottomCenter:
  339. Child.Arrange(new Rect(new Point(padding.Left, padding.Top), Child.DesiredSize));
  340. break;
  341. default:
  342. break;
  343. }
  344. }
  345. return arrangeSize;
  346. }
  347. private void ArrangeChildRight(Thickness padding)
  348. {
  349. double x = padding.Left;
  350. double y = padding.Top;
  351. if (!Double.IsNaN(this.Height) && this.Height != 0)
  352. {
  353. y = (this.Height - (Child.DesiredSize.Height)) / 2;
  354. }
  355. Child.Arrange(new Rect(new Point(x, y), Child.DesiredSize));
  356. }
  357. /// <summary>
  358. /// 绘制控件
  359. /// </summary>
  360. /// <param name="drawingContext"></param>
  361. protected override void OnRender(DrawingContext drawingContext)
  362. {
  363. if (Child != null)
  364. {
  365. Geometry cg = null;
  366. Brush brush = null;
  367. Pen pen = new Pen();
  368. pen.Brush = this.BorderBrush;
  369. pen.Thickness = UIElementEx.RoundLayoutValue(BorderThickness.Left, DoubleUtil.DpiScaleX);
  370. switch (Placement)
  371. {
  372. case EnumPlacement.LeftTop:
  373. case EnumPlacement.LeftBottom:
  374. case EnumPlacement.LeftCenter:
  375. //生成小尾巴在左侧的图形和底色
  376. cg = CreateGeometryTailAtLeft();
  377. brush = CreateFillBrush();
  378. break;
  379. case EnumPlacement.RightTop:
  380. case EnumPlacement.RightCenter:
  381. case EnumPlacement.RightBottom:
  382. //生成小尾巴在右侧的图形和底色
  383. cg = CreateGeometryTailAtRight();
  384. brush = CreateFillBrush();
  385. break;
  386. case EnumPlacement.TopLeft:
  387. case EnumPlacement.TopCenter:
  388. case EnumPlacement.TopRight:
  389. //生成小尾巴在右侧的图形和底色
  390. cg = CreateGeometryTailAtTop();
  391. brush = CreateFillBrush();
  392. break;
  393. case EnumPlacement.BottomLeft:
  394. case EnumPlacement.BottomCenter:
  395. case EnumPlacement.BottomRight:
  396. //生成小尾巴在右侧的图形和底色
  397. cg = CreateGeometryTailAtBottom();
  398. brush = CreateFillBrush();
  399. break;
  400. default:
  401. break;
  402. }
  403. GuidelineSet guideLines = new GuidelineSet();
  404. drawingContext.PushGuidelineSet(guideLines);
  405. drawingContext.DrawGeometry(brush, pen, cg);
  406. }
  407. }
  408. #endregion 方法重写
  409. #region 私有方法
  410. private Geometry CreateGeometryTailAtRight()
  411. {
  412. CombinedGeometry result = new CombinedGeometry();
  413. this.TailHeight = 12;
  414. this.TailWidth = 6;
  415. switch (this.Placement)
  416. {
  417. case EnumPlacement.RightTop:
  418. //不做任何处理
  419. break;
  420. case EnumPlacement.RightBottom:
  421. this.TailVerticalOffset = this.ActualHeight - this.TailHeight - this.TailVerticalOffset;
  422. break;
  423. case EnumPlacement.RightCenter:
  424. this.TailVerticalOffset = (this.ActualHeight - this.TailHeight) / 2;
  425. break;
  426. }
  427. #region 绘制三角形
  428. Point arcPoint1 = new Point(this.ActualWidth - TailWidth, TailVerticalOffset);
  429. Point arcPoint2 = new Point(this.ActualWidth, TailVerticalOffset + TailHeight / 2);
  430. Point arcPoint3 = new Point(this.ActualWidth - TailWidth, TailVerticalOffset + TailHeight);
  431. LineSegment as1_2 = new LineSegment(arcPoint2, false);
  432. LineSegment as2_3 = new LineSegment(arcPoint3, false);
  433. PathFigure pf1 = new PathFigure();
  434. pf1.IsClosed = false;
  435. pf1.StartPoint = arcPoint1;
  436. pf1.Segments.Add(as1_2);
  437. pf1.Segments.Add(as2_3);
  438. PathGeometry pg1 = new PathGeometry();
  439. pg1.Figures.Add(pf1);
  440. #endregion 绘制三角形
  441. #region 绘制矩形边框
  442. RectangleGeometry rg2 = new RectangleGeometry(new Rect(0, 0, this.ActualWidth - TailWidth, this.ActualHeight)
  443. , CornerRadius.TopLeft, CornerRadius.BottomRight, new TranslateTransform(0.5, 0.5));
  444. #endregion 绘制矩形边框
  445. #region 合并两个图形
  446. result.Geometry1 = pg1;
  447. result.Geometry2 = rg2;
  448. result.GeometryCombineMode = GeometryCombineMode.Union;
  449. #endregion 合并两个图形
  450. return result;
  451. }
  452. private Geometry CreateGeometryTailAtLeft()
  453. {
  454. CombinedGeometry result = new CombinedGeometry();
  455. this.TailHeight = 12;
  456. this.TailWidth = 6;
  457. switch (this.Placement)
  458. {
  459. case EnumPlacement.LeftTop:
  460. //不做任何处理
  461. break;
  462. case EnumPlacement.LeftBottom:
  463. this.TailVerticalOffset = this.ActualHeight - this.TailHeight - this.TailVerticalOffset;
  464. break;
  465. case EnumPlacement.LeftCenter:
  466. this.TailVerticalOffset = (this.ActualHeight - this.TailHeight) / 2;
  467. break;
  468. }
  469. #region 绘制三角形
  470. Point arcPoint1 = new Point(TailWidth, TailVerticalOffset);
  471. Point arcPoint2 = new Point(0, TailVerticalOffset + TailHeight / 2);
  472. Point arcPoint3 = new Point(TailWidth, TailVerticalOffset + TailHeight);
  473. LineSegment as1_2 = new LineSegment(arcPoint2, false);
  474. LineSegment as2_3 = new LineSegment(arcPoint3, false);
  475. PathFigure pf = new PathFigure();
  476. pf.IsClosed = false;
  477. pf.StartPoint = arcPoint1;
  478. pf.Segments.Add(as1_2);
  479. pf.Segments.Add(as2_3);
  480. PathGeometry g1 = new PathGeometry();
  481. g1.Figures.Add(pf);
  482. #endregion 绘制三角形
  483. #region 绘制矩形边框
  484. RectangleGeometry g2 = new RectangleGeometry(new Rect(TailWidth, 0, this.ActualWidth - this.TailWidth, this.ActualHeight)
  485. , CornerRadius.TopLeft, CornerRadius.BottomRight);
  486. #endregion 绘制矩形边框
  487. #region 合并两个图形
  488. result.Geometry1 = g1;
  489. result.Geometry2 = g2;
  490. result.GeometryCombineMode = GeometryCombineMode.Union;
  491. #endregion 合并两个图形
  492. return result;
  493. }
  494. private Geometry CreateGeometryTailAtTop()
  495. {
  496. CombinedGeometry result = new CombinedGeometry();
  497. switch (this.Placement)
  498. {
  499. case EnumPlacement.TopLeft:
  500. break;
  501. case EnumPlacement.TopCenter:
  502. this.TailHorizontalOffset = (this.ActualWidth - this.TailWidth) / 2;
  503. break;
  504. case EnumPlacement.TopRight:
  505. this.TailHorizontalOffset = this.ActualWidth - this.TailWidth - this.TailHorizontalOffset;
  506. break;
  507. }
  508. #region 绘制三角形
  509. Point anglePoint1 = new Point(this.TailHorizontalOffset, this.TailHeight);
  510. Point anglePoint2 = new Point(this.TailHorizontalOffset + (this.TailWidth / 2), 0);
  511. Point anglePoint3 = new Point(this.TailHorizontalOffset + this.TailWidth, this.TailHeight);
  512. LineSegment as1_2 = new LineSegment(anglePoint2, true);
  513. LineSegment as2_3 = new LineSegment(anglePoint3, true);
  514. PathFigure pf = new PathFigure();
  515. pf.IsClosed = false;
  516. pf.StartPoint = anglePoint1;
  517. pf.Segments.Add(as1_2);
  518. pf.Segments.Add(as2_3);
  519. PathGeometry g1 = new PathGeometry();
  520. g1.Figures.Add(pf);
  521. #endregion 绘制三角形
  522. #region 绘制矩形边框
  523. RectangleGeometry g2 = new RectangleGeometry(new Rect(0, this.TailHeight, this.ActualWidth, this.ActualHeight - this.TailHeight)
  524. , CornerRadius.TopLeft, CornerRadius.BottomRight);
  525. #endregion 绘制矩形边框
  526. #region 合并
  527. result.Geometry1 = g1;
  528. result.Geometry2 = g2;
  529. result.GeometryCombineMode = GeometryCombineMode.Union;
  530. #endregion 合并
  531. return result;
  532. }
  533. private Geometry CreateGeometryTailAtBottom()
  534. {
  535. CombinedGeometry result = new CombinedGeometry();
  536. switch (this.Placement)
  537. {
  538. case EnumPlacement.BottomLeft:
  539. break;
  540. case EnumPlacement.BottomCenter:
  541. this.TailHorizontalOffset = (this.ActualWidth - this.TailWidth) / 2;
  542. break;
  543. case EnumPlacement.BottomRight:
  544. this.TailHorizontalOffset = this.ActualWidth - this.TailWidth - this.TailHorizontalOffset;
  545. break;
  546. }
  547. #region 绘制三角形
  548. Point anglePoint1 = new Point(this.TailHorizontalOffset, this.ActualHeight - this.TailHeight);
  549. Point anglePoint2 = new Point(this.TailHorizontalOffset + this.TailWidth / 2, this.ActualHeight);
  550. Point anglePoint3 = new Point(this.TailHorizontalOffset + this.TailWidth, this.ActualHeight - this.TailHeight);
  551. LineSegment as1_2 = new LineSegment(anglePoint2, true);
  552. LineSegment as2_3 = new LineSegment(anglePoint3, true);
  553. PathFigure pf = new PathFigure();
  554. pf.IsClosed = false;
  555. pf.StartPoint = anglePoint1;
  556. pf.Segments.Add(as1_2);
  557. pf.Segments.Add(as2_3);
  558. PathGeometry g1 = new PathGeometry();
  559. g1.Figures.Add(pf);
  560. #endregion 绘制三角形
  561. #region 绘制矩形边框
  562. RectangleGeometry g2 = new RectangleGeometry(new Rect(0, 0, this.ActualWidth, this.ActualHeight - this.TailHeight)
  563. , CornerRadius.TopLeft, CornerRadius.BottomRight);
  564. #endregion 绘制矩形边框
  565. #region 合并
  566. result.Geometry1 = g1;
  567. result.Geometry2 = g2;
  568. result.GeometryCombineMode = GeometryCombineMode.Union;
  569. #endregion 合并
  570. return result;
  571. }
  572. private Brush CreateFillBrush()
  573. {
  574. Brush result = null;
  575. GradientStopCollection gsc = new GradientStopCollection();
  576. gsc.Add(new GradientStop(((SolidColorBrush)this.Background).Color, 0));
  577. LinearGradientBrush backGroundBrush = new LinearGradientBrush(gsc, new Point(0, 0), new Point(0, 1));
  578. result = backGroundBrush;
  579. return result;
  580. }
  581. /// <summary>
  582. /// 根据三角形方向设置消息框的水平位置,偏左还是偏右
  583. /// </summary>
  584. /// <param name="d"></param>
  585. /// <param name="e"></param>
  586. public static void OnDirectionPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
  587. {
  588. AngleBorder angleBorder = d as AngleBorder;
  589. if (angleBorder != null)
  590. {
  591. switch ((EnumPlacement)e.NewValue)
  592. {
  593. case EnumPlacement.LeftTop:
  594. case EnumPlacement.LeftBottom:
  595. case EnumPlacement.LeftCenter:
  596. case EnumPlacement.RightTop:
  597. case EnumPlacement.RightBottom:
  598. case EnumPlacement.RightCenter:
  599. angleBorder.TailWidth = 6;
  600. angleBorder.TailHeight = 12;
  601. break;
  602. case EnumPlacement.TopLeft:
  603. case EnumPlacement.TopCenter:
  604. case EnumPlacement.TopRight:
  605. case EnumPlacement.BottomLeft:
  606. case EnumPlacement.BottomCenter:
  607. case EnumPlacement.BottomRight:
  608. angleBorder.TailWidth = 12;
  609. angleBorder.TailHeight = 6;
  610. break;
  611. default:
  612. break;
  613. }
  614. }
  615. }
  616. #endregion 私有方法
  617. }
  618. public class PopTip : Popup
  619. {
  620. #region private fields
  621. private bool mIsLoaded = false;
  622. private AngleBorder angleBorder;
  623. #endregion private fields
  624. #region DependencyProperty
  625. #region PlacementEx
  626. public EnumPlacement PlacementEx
  627. {
  628. get { return (EnumPlacement)GetValue(PlacementExProperty); }
  629. set { SetValue(PlacementExProperty, value); }
  630. }
  631. public static readonly DependencyProperty PlacementExProperty =
  632. DependencyProperty.Register("PlacementEx", typeof(EnumPlacement), typeof(PopTip)
  633. , new PropertyMetadata(EnumPlacement.TopLeft, PlacementExChangedCallback));
  634. private static void PlacementExChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
  635. {
  636. PopTip poptip = d as PopTip;
  637. if (poptip != null)
  638. {
  639. EnumPlacement placement = (EnumPlacement)e.NewValue;
  640. switch (placement)
  641. {
  642. case EnumPlacement.LeftTop:
  643. break;
  644. case EnumPlacement.LeftBottom:
  645. break;
  646. case EnumPlacement.LeftCenter:
  647. break;
  648. case EnumPlacement.RightTop:
  649. break;
  650. case EnumPlacement.RightBottom:
  651. break;
  652. case EnumPlacement.RightCenter:
  653. break;
  654. case EnumPlacement.TopLeft:
  655. break;
  656. case EnumPlacement.TopCenter:
  657. poptip.Placement = PlacementMode.Top;
  658. break;
  659. case EnumPlacement.TopRight:
  660. break;
  661. case EnumPlacement.BottomLeft:
  662. break;
  663. case EnumPlacement.BottomCenter:
  664. break;
  665. case EnumPlacement.BottomRight:
  666. break;
  667. default:
  668. break;
  669. }
  670. }
  671. }
  672. #endregion PlacementEx
  673. #region Background
  674. public Brush Background
  675. {
  676. get { return (Brush)GetValue(BackgroundProperty); }
  677. set { SetValue(BackgroundProperty, value); }
  678. }
  679. public static readonly DependencyProperty BackgroundProperty =
  680. DependencyProperty.Register("Background", typeof(Brush), typeof(PopTip), new PropertyMetadata(new SolidColorBrush(Color.FromRgb(109, 129, 154))));
  681. #endregion Background
  682. #region BorderThickness
  683. public Thickness BorderThickness
  684. {
  685. get { return (Thickness)GetValue(BorderThicknessProperty); }
  686. set { SetValue(BorderThicknessProperty, value); }
  687. }
  688. public static readonly DependencyProperty BorderThicknessProperty =
  689. DependencyProperty.Register("BorderThickness", typeof(Thickness), typeof(PopTip), new PropertyMetadata(new Thickness(1)));
  690. #endregion BorderThickness
  691. #region BorderBrush
  692. public Brush BorderBrush
  693. {
  694. get { return (Brush)GetValue(BorderBrushProperty); }
  695. set { SetValue(BorderBrushProperty, value); }
  696. }
  697. public static readonly DependencyProperty BorderBrushProperty =
  698. DependencyProperty.Register("BorderBrush", typeof(Brush), typeof(PopTip), new PropertyMetadata(new SolidColorBrush(Color.FromRgb(204, 206, 219))));
  699. #endregion BorderBrush
  700. #region CornerRadius
  701. public CornerRadius CornerRadius
  702. {
  703. get { return (CornerRadius)GetValue(CornerRadiusProperty); }
  704. set { SetValue(CornerRadiusProperty, value); }
  705. }
  706. public static readonly DependencyProperty CornerRadiusProperty =
  707. DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(PopTip), new PropertyMetadata(new CornerRadius(5)));
  708. #endregion CornerRadius
  709. #endregion DependencyProperty
  710. #region Override
  711. protected override void OnInitialized(EventArgs e)
  712. {
  713. base.OnInitialized(e);
  714. this.AllowsTransparency = true;
  715. //this.StaysOpen = false;
  716. UIElement element = this.Child;
  717. this.Child = null;
  718. Grid root = new Grid()
  719. {
  720. Margin = new Thickness(10),
  721. };
  722. angleBorder = new AngleBorder()
  723. {
  724. Background = this.Background,
  725. CornerRadius = this.CornerRadius,
  726. BorderThickness = this.BorderThickness,
  727. BorderBrush = this.BorderBrush,
  728. };
  729. switch (this.PlacementEx)
  730. {
  731. case EnumPlacement.LeftTop:
  732. angleBorder.Placement = EnumPlacement.RightTop;
  733. break;
  734. case EnumPlacement.LeftBottom:
  735. angleBorder.Placement = EnumPlacement.RightBottom;
  736. break;
  737. case EnumPlacement.LeftCenter:
  738. angleBorder.Placement = EnumPlacement.RightCenter;
  739. break;
  740. case EnumPlacement.RightTop:
  741. angleBorder.Placement = EnumPlacement.LeftTop;
  742. break;
  743. case EnumPlacement.RightBottom:
  744. angleBorder.Placement = EnumPlacement.LeftBottom;
  745. break;
  746. case EnumPlacement.RightCenter:
  747. angleBorder.Placement = EnumPlacement.LeftCenter;
  748. break;
  749. case EnumPlacement.TopLeft:
  750. angleBorder.Placement = EnumPlacement.BottomLeft;
  751. break;
  752. case EnumPlacement.TopCenter:
  753. angleBorder.Placement = EnumPlacement.BottomCenter;
  754. break;
  755. case EnumPlacement.TopRight:
  756. angleBorder.Placement = EnumPlacement.BottomRight;
  757. break;
  758. case EnumPlacement.BottomLeft:
  759. angleBorder.Placement = EnumPlacement.TopLeft;
  760. break;
  761. case EnumPlacement.BottomCenter:
  762. angleBorder.Placement = EnumPlacement.TopCenter;
  763. break;
  764. case EnumPlacement.BottomRight:
  765. angleBorder.Placement = EnumPlacement.TopRight;
  766. break;
  767. default:
  768. break;
  769. }
  770. //在原有控件基础上,最外层套一个AngleBorder
  771. angleBorder.Child = element;
  772. root.Children.Add(angleBorder);
  773. this.Child = root;
  774. }
  775. protected override void OnOpened(EventArgs e)
  776. {
  777. base.OnOpened(e);
  778. if (this.mIsLoaded)
  779. {
  780. return;
  781. }
  782. FrameworkElement targetElement = this.PlacementTarget as FrameworkElement;
  783. FrameworkElement child = this.Child as FrameworkElement;
  784. if (targetElement == null || child == null) return;
  785. switch (this.PlacementEx)
  786. {
  787. case EnumPlacement.LeftTop:
  788. this.Placement = PlacementMode.Left;
  789. break;
  790. case EnumPlacement.LeftBottom:
  791. this.Placement = PlacementMode.Left;
  792. this.VerticalOffset = targetElement.ActualHeight - child.ActualHeight;
  793. break;
  794. case EnumPlacement.LeftCenter:
  795. this.Placement = PlacementMode.Left;
  796. this.VerticalOffset = this.GetOffset(targetElement.ActualHeight, child.ActualHeight);
  797. break;
  798. case EnumPlacement.RightTop:
  799. this.Placement = PlacementMode.Right;
  800. break;
  801. case EnumPlacement.RightBottom:
  802. this.Placement = PlacementMode.Right;
  803. this.VerticalOffset = targetElement.ActualHeight - child.ActualHeight;
  804. break;
  805. case EnumPlacement.RightCenter:
  806. this.Placement = PlacementMode.Right;
  807. this.VerticalOffset = this.GetOffset(targetElement.ActualHeight, child.ActualHeight);
  808. break;
  809. case EnumPlacement.TopLeft:
  810. this.Placement = PlacementMode.Top;
  811. break;
  812. case EnumPlacement.TopCenter:
  813. this.Placement = PlacementMode.Top;
  814. this.HorizontalOffset = this.GetOffset(targetElement.ActualWidth, child.ActualWidth);
  815. break;
  816. case EnumPlacement.TopRight:
  817. this.Placement = PlacementMode.Top;
  818. this.HorizontalOffset = targetElement.ActualWidth - child.ActualWidth;
  819. break;
  820. case EnumPlacement.BottomLeft:
  821. this.Placement = PlacementMode.Bottom;
  822. break;
  823. case EnumPlacement.BottomCenter:
  824. this.Placement = PlacementMode.Bottom;
  825. this.HorizontalOffset = this.GetOffset(targetElement.ActualWidth, child.ActualWidth);
  826. break;
  827. case EnumPlacement.BottomRight:
  828. this.Placement = PlacementMode.Bottom;
  829. this.HorizontalOffset = targetElement.ActualWidth - child.ActualWidth;
  830. break;
  831. }
  832. this.mIsLoaded = true;
  833. }
  834. #endregion Override
  835. #region private function
  836. private double GetOffset(double targetSize, double poptipSize)
  837. {
  838. if (double.IsNaN(targetSize) || double.IsNaN(poptipSize))
  839. {
  840. return 0;
  841. }
  842. return (targetSize / 2.0) - (poptipSize / 2.0);
  843. }
  844. #endregion private function
  845. }
  846. }