IntegerSpinner.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using System.Windows;
  7. using System.Windows.Controls;
  8. using System.Windows.Input;
  9. namespace FWindSoft.Wpf.Controls
  10. {
  11. /// <summary>
  12. /// 输入文本生效方式
  13. /// </summary>
  14. public enum InputTextForceType
  15. {
  16. /// <summary>
  17. /// 实时
  18. /// </summary>
  19. Time,
  20. /// <summary>
  21. /// 按键
  22. /// </summary>
  23. KeyDown
  24. }
  25. [TemplatePart(Name = PART_TextBox, Type = typeof(TextBox))]
  26. [TemplatePart(Name = PART_Up, Type = typeof(Button))]
  27. [TemplatePart(Name = PART_Down, Type = typeof(Button))]
  28. public class IntegerSpinner:Control
  29. {
  30. static IntegerSpinner()
  31. {
  32. DefaultStyleKeyProperty.OverrideMetadata(typeof(IntegerSpinner), new FrameworkPropertyMetadata(typeof(IntegerSpinner)));
  33. }
  34. public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(IntegerSpinner), new FrameworkPropertyMetadata(false));
  35. public bool IsReadOnly
  36. {
  37. get
  38. {
  39. return (bool)GetValue(IsReadOnlyProperty);
  40. }
  41. set
  42. {
  43. SetValue(IsReadOnlyProperty, value);
  44. }
  45. }
  46. #region 最大值最小值限定相关
  47. public static readonly RoutedEvent ValueChangedEvent=EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler<int>), typeof(IntegerSpinner));
  48. public static readonly DependencyProperty MinimumProperty=DependencyProperty.Register("Minimum", typeof(int), typeof(IntegerSpinner), new FrameworkPropertyMetadata(1, new PropertyChangedCallback(OnMinimumChanged)));
  49. public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register("Maximum", typeof(int), typeof(IntegerSpinner), new FrameworkPropertyMetadata(100, new PropertyChangedCallback(OnMaximumChanged), new CoerceValueCallback(CoerceMaximum)));
  50. public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(int), typeof(IntegerSpinner), new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal, new PropertyChangedCallback(OnValueChanged), new CoerceValueCallback(ConstrainToRange)));
  51. public static readonly DependencyProperty StepProperty = DependencyProperty.Register("Step", typeof(int), typeof(IntegerSpinner), new FrameworkPropertyMetadata(1));
  52. public event RoutedPropertyChangedEventHandler<int> ValueChanged
  53. {
  54. add
  55. {
  56. base.AddHandler(ValueChangedEvent, value);
  57. }
  58. remove
  59. {
  60. base.RemoveHandler(ValueChangedEvent, value);
  61. }
  62. }
  63. public int Minimum
  64. {
  65. get
  66. {
  67. return (int)base.GetValue(MinimumProperty);
  68. }
  69. set
  70. {
  71. base.SetValue(MinimumProperty, value);
  72. }
  73. }
  74. public int Maximum
  75. {
  76. get
  77. {
  78. return (int)base.GetValue(MaximumProperty);
  79. }
  80. set
  81. {
  82. base.SetValue(MaximumProperty, value);
  83. }
  84. }
  85. public int Value
  86. {
  87. get
  88. {
  89. return (int)base.GetValue(ValueProperty);
  90. }
  91. set
  92. {
  93. base.SetValue(ValueProperty, value);
  94. }
  95. }
  96. public int Step
  97. {
  98. get
  99. {
  100. return (int)base.GetValue(StepProperty);
  101. }
  102. set
  103. {
  104. base.SetValue(StepProperty, value);
  105. }
  106. }
  107. #region 最小值处理
  108. private static void OnMinimumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  109. {
  110. IntegerSpinner rangeBase = (IntegerSpinner)d;
  111. rangeBase.CoerceValue(MaximumProperty);
  112. rangeBase.CoerceValue(ValueProperty);
  113. rangeBase.OnMinimumChanged((int)e.OldValue, (int)e.NewValue);
  114. }
  115. protected virtual void OnMinimumChanged(int oldMinimum, int newMinimum)
  116. {
  117. }
  118. #endregion
  119. #region 最大值处理
  120. private static object CoerceMaximum(DependencyObject d, object value)
  121. {
  122. IntegerSpinner rangeBase = (IntegerSpinner)d;
  123. int minimum = rangeBase.Minimum;
  124. if ((int)value < minimum)
  125. {
  126. return minimum;
  127. }
  128. return value;
  129. }
  130. private static void OnMaximumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  131. {
  132. IntegerSpinner rangeBase = (IntegerSpinner)d;
  133. rangeBase.CoerceValue(ValueProperty);
  134. rangeBase.OnMaximumChanged((int)e.OldValue, (int)e.NewValue);
  135. }
  136. protected virtual void OnMaximumChanged(int oldMaximum, int newMaximum)
  137. {
  138. }
  139. #endregion
  140. #region 值处理
  141. internal static object ConstrainToRange(DependencyObject d, object value)
  142. {
  143. IntegerSpinner rangeBase = (IntegerSpinner)d;
  144. int minimum = rangeBase.Minimum;
  145. int num = (int)value;
  146. if (num < minimum)
  147. {
  148. //加入这个显示,可以强制的更新到绑定的数据源,但是会执行两次,这样保证了Value值,最后的正确性
  149. //但失去焦点,同样会达成这种效果
  150. //rangeBase.Value = minimum;
  151. return minimum;
  152. }
  153. int maximum = rangeBase.Maximum;
  154. if (num > maximum)
  155. {
  156. //
  157. //rangeBase.Value = maximum;
  158. return maximum;
  159. }
  160. return value;
  161. }
  162. private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  163. {
  164. IntegerSpinner rangeBase = (IntegerSpinner)d;
  165. rangeBase.OnValueChanged((int)e.OldValue, (int)e.NewValue);
  166. }
  167. protected virtual void OnValueChanged(int oldValue, int newValue)
  168. {
  169. ValueToText(newValue);//value值变换更新Text值
  170. base.RaiseEvent(new RoutedPropertyChangedEventArgs<int>(oldValue, newValue)
  171. {
  172. RoutedEvent = IntegerSpinner.ValueChangedEvent
  173. });
  174. }
  175. #endregion
  176. #endregion
  177. #region 临时值存储相关
  178. protected TextBox TextBox
  179. {
  180. get; private set;
  181. }
  182. protected Button UpButton
  183. { get; private set; }
  184. protected Button DownButton
  185. { get; private set; }
  186. public static readonly DependencyProperty ForceTypeProperty = DependencyProperty.Register("ForceType", typeof(InputTextForceType), typeof(IntegerSpinner), new FrameworkPropertyMetadata(InputTextForceType.Time));
  187. public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(IntegerSpinner), new FrameworkPropertyMetadata(null, Text_Changed));
  188. public string Text
  189. {
  190. get { return (string)GetValue(TextProperty); }
  191. set
  192. {
  193. SetValue(TextProperty,value);
  194. }
  195. }
  196. /// <summary>
  197. /// 输入文本生效类型
  198. /// </summary>
  199. public InputTextForceType ForceType
  200. {
  201. get { return (InputTextForceType)GetValue(ForceTypeProperty); }
  202. set
  203. {
  204. SetValue(ForceTypeProperty, value);
  205. }
  206. }
  207. private bool m_OutChanged=true;//外部赋值引起的Text变化
  208. private bool m_UiChanged = false;//Ui引起的Text值发生变化
  209. public static void Text_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
  210. {
  211. IntegerSpinner spinner = d as IntegerSpinner;
  212. if (spinner == null)
  213. return;
  214. spinner.TextChanged(e);
  215. }
  216. private void TextChanged(DependencyPropertyChangedEventArgs e)
  217. {
  218. //如果是内部修改直接退出
  219. //var test = this.IsInitialized;
  220. if (!m_OutChanged)
  221. return;
  222. var newValue = (string) e.NewValue;
  223. if (ForceType == InputTextForceType.KeyDown)
  224. {
  225. return;
  226. }
  227. if (!m_UiChanged)
  228. {
  229. //外部修改,将Text转换成Value
  230. TextToValue(newValue);
  231. //通过约束的Value,返回TextBox
  232. ValueToText(this.Value);
  233. SetTextBoxText(TextBox, this);
  234. }
  235. else
  236. {
  237. //不回写TextBox,不强制计算
  238. TextToValue(newValue);
  239. }
  240. }
  241. #region Text和value交互
  242. /*
  243. 1、Text 的值外接赋值,需要强制刷新界面TextBox的值
  244. 2、存在将Value转换成Text的函数
  245. 3、Text只与Value发生交互
  246. */
  247. private void ValueToText(int value)
  248. {
  249. m_OutChanged = false;
  250. try
  251. {
  252. this.Text = value.ToString();
  253. }
  254. finally
  255. {
  256. m_OutChanged = true;
  257. }
  258. }
  259. /// <summary>
  260. /// 转换成Value
  261. /// </summary>
  262. /// <param name="text"></param>
  263. /// <returns>是否给Value赋值</returns>
  264. private void TextToValue(string text)
  265. {
  266. if (string.IsNullOrEmpty(text))
  267. return ;
  268. int result = 0;
  269. if (Int32.TryParse(text, out result))
  270. {
  271. this.Value = result;
  272. }
  273. }
  274. #region Text和TextBox交互
  275. private bool m_UseTextBoxChangeEvent = true;
  276. private void SetTextBoxText(TextBox textBox, IntegerSpinner spinner)
  277. {
  278. try
  279. {
  280. m_UseTextBoxChangeEvent = false;
  281. if (textBox == null)
  282. return;
  283. textBox.Text = spinner.Text;
  284. }
  285. finally
  286. {
  287. m_UseTextBoxChangeEvent = true;
  288. }
  289. }
  290. private void GetTextBoxText(TextBox textBox, IntegerSpinner spinner)
  291. {
  292. if (textBox == null)
  293. return;
  294. try
  295. {
  296. m_UiChanged = true;
  297. spinner.Text = textBox.Text;
  298. }
  299. finally
  300. {
  301. m_UiChanged = false;
  302. }
  303. }
  304. #endregion
  305. #endregion
  306. #endregion
  307. #region 键盘控制相关
  308. public const string PART_TextBox = "PART_TextBox";
  309. public const string PART_Up = "PART_Up";
  310. public const string PART_Down = "PART_Down";
  311. protected override void OnAccessKey(AccessKeyEventArgs e)
  312. {
  313. if (TextBox != null)
  314. TextBox.Focus();
  315. base.OnAccessKey(e);
  316. }
  317. #region 子空间事件相关
  318. public override void OnApplyTemplate()
  319. {
  320. base.OnApplyTemplate();
  321. Detaching();
  322. TextBox = GetTemplateChild(PART_TextBox) as TextBox;
  323. UpButton = GetTemplateChild(PART_Up) as Button;
  324. DownButton = GetTemplateChild(PART_Down) as Button;
  325. Attached();
  326. }
  327. private void Attached()
  328. {
  329. if (TextBox != null)
  330. {
  331. SetTextBoxText(TextBox, this);
  332. TextBox.TextChanged += new TextChangedEventHandler(TextBox_TextChanged);
  333. }
  334. if (UpButton != null)
  335. {
  336. UpButton.Click += UpButton_Click;
  337. }
  338. if (DownButton != null)
  339. {
  340. DownButton.Click += DownButton_Click;
  341. }
  342. }
  343. private void Detaching()
  344. {
  345. if (TextBox != null)
  346. {
  347. TextBox.TextChanged -= new TextChangedEventHandler(TextBox_TextChanged);
  348. }
  349. if (UpButton != null)
  350. {
  351. UpButton.Click -= UpButton_Click;
  352. }
  353. if (DownButton != null)
  354. {
  355. DownButton.Click-= DownButton_Click;
  356. }
  357. }
  358. private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
  359. {
  360. if (!m_UseTextBoxChangeEvent)
  361. return;
  362. GetTextBoxText((TextBox)sender, this);
  363. }
  364. private void DownButton_Click(object sender, RoutedEventArgs e)
  365. {
  366. this.Value = this.Value - Step;
  367. SetTextBoxText(TextBox, this);
  368. }
  369. private void UpButton_Click(object sender, RoutedEventArgs e)
  370. {
  371. this.Value = this.Value + Step;
  372. SetTextBoxText(TextBox, this);
  373. }
  374. #endregion
  375. //输入生效模式为KeyDown时,当按下enter,或控件失去焦点,则执行强制提交。这是才修改Value值
  376. //Time时,是实时修改value值
  377. protected override void OnKeyDown(KeyEventArgs e)
  378. {
  379. switch (e.Key)
  380. {
  381. case Key.Enter:
  382. {
  383. this.CommitInput();
  384. if (TextBox != null)
  385. TextBox.Select((this.Text ?? string.Empty).Length, 0);
  386. break;
  387. }
  388. }
  389. }
  390. public void CommitInput()
  391. {
  392. TextToValue(this.Text);
  393. //通过约束的Value,返回TextBox
  394. ValueToText(this.Value);
  395. SetTextBoxText(TextBox, this);
  396. }
  397. #endregion
  398. public IntegerSpinner()
  399. {
  400. this.LostFocus += IntegerSpinner_LostFocus;
  401. this.Loaded += IntegerSpinner_Loaded;
  402. }
  403. private void IntegerSpinner_Loaded(object sender, RoutedEventArgs e)
  404. {
  405. this.Loaded -= IntegerSpinner_Loaded;
  406. this.CommitInput();
  407. }
  408. private void IntegerSpinner_LostFocus(object sender, RoutedEventArgs e)
  409. {
  410. //点击按钮也会发生焦点变化,这个暂时不需要过滤
  411. this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Background, new Action(() =>
  412. {
  413. CommitInput();
  414. }));
  415. }
  416. protected override void OnLostFocus(RoutedEventArgs e)
  417. {
  418. //不知道重写Overide为什么不执行
  419. }
  420. }
  421. }