MyTextBox.vb 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. Public Class MyTextBox
  2. Inherits TextBox
  3. '自定义属性
  4. Public Property HasAnimation As Boolean = True
  5. Public Property ShowValidateResult As Boolean = True
  6. '额外控件初始化
  7. Private _labWrong As TextBlock = Nothing
  8. Private ReadOnly Property labWrong As TextBlock
  9. Get
  10. If Template Is Nothing Then Return Nothing
  11. If _labWrong Is Nothing Then _labWrong = Template.FindName("labWrong", Me)
  12. Return _labWrong
  13. End Get
  14. End Property
  15. Private _labHint As TextBlock = Nothing
  16. Private ReadOnly Property labHint As TextBlock
  17. Get
  18. If Template Is Nothing Then Return Nothing
  19. If _labHint Is Nothing Then _labHint = Template.FindName("labHint", Me)
  20. Return _labHint
  21. End Get
  22. End Property
  23. Public Overrides Sub OnApplyTemplate()
  24. MyBase.OnApplyTemplate()
  25. If HintText = "" OrElse labHint.Text <> "" Then Exit Sub
  26. labHint.Text = If(Text = "", HintText, "")
  27. End Sub
  28. '事件
  29. Public Uuid As Integer = GetUuid()
  30. Public Shared Event ValidateChanged(sender As Object, e As EventArgs)
  31. Public ChangedEventList As New List(Of RoutedEventHandler)
  32. Public Custom Event ValidatedTextChanged As RoutedEventHandler
  33. AddHandler(value As RoutedEventHandler)
  34. ChangedEventList.Add(value)
  35. End AddHandler
  36. RemoveHandler(value As RoutedEventHandler)
  37. ChangedEventList.Remove(value)
  38. End RemoveHandler
  39. RaiseEvent(sender As Object, e As TextChangedEventArgs)
  40. For Each handler As RoutedEventHandler In ChangedEventList
  41. If Not IsNothing(handler) Then handler.Invoke(sender, e)
  42. Next
  43. End RaiseEvent
  44. End Event
  45. '输入验证
  46. ''' <summary>
  47. ''' 输入验证结果。若为空字符串则无错误,否则为第一个错误原因。
  48. ''' </summary>
  49. Public Property ValidateResult As String
  50. Get
  51. Return GetValue(ValidateResultProperty)
  52. End Get
  53. Set(value As String)
  54. SetValue(ValidateResultProperty, value)
  55. End Set
  56. End Property
  57. Public Shared ReadOnly ValidateResultProperty As DependencyProperty = DependencyProperty.Register("ValidateResult", GetType(String), GetType(MyTextBox), New PropertyMetadata(""))
  58. ''' <summary>
  59. ''' 输入验证的规则。
  60. ''' </summary>
  61. Public Property ValidateRules As ObjectModel.Collection(Of Validate)
  62. Get
  63. Return _ValidateRules
  64. End Get
  65. Set(Value As ObjectModel.Collection(Of Validate))
  66. _ValidateRules = Value
  67. Validate()
  68. End Set
  69. End Property
  70. Private _ValidateRules As New ObjectModel.Collection(Of Validate)
  71. ''' <summary>
  72. ''' 进行输入验证。
  73. ''' </summary>
  74. Public Sub Validate() Handles Me.Loaded
  75. '执行输入验证
  76. ValidateResult = ""
  77. For Each ValidateRule As Validate In ValidateRules
  78. ValidateResult = ValidateRule.Validate(Text)
  79. If IsNothing(ValidateResult) OrElse Not ValidateResult = "" Then Exit For
  80. Next
  81. Dim IsSuccessful As Boolean = ValidateResult = ""
  82. '根据结果改变样式
  83. If ShownValidateResult <> If(IsSuccessful, ValidateState.Success, ValidateState.FailedAndShowDetail) Then
  84. If IsLoaded AndAlso labWrong IsNot Nothing Then
  85. ChangeValidateResult(IsSuccessful, True)
  86. Else
  87. RunInNewThread(Sub()
  88. Thread.Sleep(30)
  89. RunInUi(Sub() ChangeValidateResult(IsSuccessful, False))
  90. End Sub, "DelayedValidate Change")
  91. End If
  92. End If
  93. '更新错误信息
  94. If ShowValidateResult AndAlso Not ValidateResult = "" Then
  95. If IsLoaded AndAlso labWrong IsNot Nothing Then
  96. labWrong.Text = ValidateResult
  97. Else
  98. RunInNewThread(Sub()
  99. Dim IsFinished As Boolean = False
  100. Do Until IsFinished
  101. Thread.Sleep(20)
  102. RunInUiWait(Sub()
  103. If labWrong IsNot Nothing Then
  104. labWrong.Text = ValidateResult
  105. IsFinished = True
  106. End If
  107. If Not IsLoaded Then IsFinished = True
  108. End Sub)
  109. Loop
  110. End Sub, "DelayedValidate Text")
  111. End If
  112. End If
  113. End Sub
  114. ''' <summary>
  115. ''' 强制显示结果为正常,类似尚未输入过文本的状态。不影响实际的检查结果。
  116. ''' </summary>
  117. Public Sub ForceShowAsSuccess()
  118. IsTextChanged = False
  119. ChangeValidateResult(ValidateResult = "", True)
  120. End Sub
  121. Private ShownValidateResult As ValidateState = ValidateState.NotInited
  122. Private Sub ChangeValidateResult(IsSuccessful As Boolean, IsLoaded As Boolean)
  123. If IsLoaded AndAlso AniControlEnabled = 0 AndAlso labWrong IsNot Nothing Then
  124. If IsSuccessful OrElse Not IsTextChanged Then
  125. '变为正确
  126. ShownValidateResult = If(IsSuccessful, ValidateState.Success, ValidateState.FailedButTextNotChanged)
  127. AniStart({
  128. AaOpacity(labWrong, -labWrong.Opacity, 150),
  129. AaHeight(labWrong, -labWrong.Height, 150,, New AniEaseOutFluent),
  130. AaCode(Sub() labWrong.Visibility = Visibility.Collapsed,, True)
  131. }, "MyTextBox Validate " & Uuid)
  132. ElseIf ShowValidateResult Then
  133. '变为错误
  134. ShownValidateResult = ValidateState.FailedAndShowDetail
  135. labWrong.Visibility = Visibility.Visible
  136. AniStart({
  137. AaOpacity(labWrong, 1 - labWrong.Opacity, 170),
  138. AaHeight(labWrong, 21 - labWrong.Height, 300,, New AniEaseOutBack(AniEasePower.Weak))
  139. }, "MyTextBox Validate " & Uuid)
  140. Else
  141. '变为错误,但不显示文本
  142. ShownValidateResult = ValidateState.FailedAndHideDetail
  143. End If
  144. Else
  145. ShownValidateResult = ValidateState.NotLoaded
  146. End If
  147. RefreshColor()
  148. RaiseEvent ValidateChanged(Me, New EventArgs)
  149. End Sub
  150. Private Enum ValidateState
  151. NotInited
  152. Success
  153. FailedButTextNotChanged
  154. FailedAndShowDetail
  155. FailedAndHideDetail
  156. NotLoaded
  157. End Enum
  158. '提示文本
  159. ''' <summary>
  160. ''' 是否已经由用户输入过文本,若尚未输入过,则不显示输入检查的失败。
  161. ''' </summary>
  162. Private IsTextChanged As Boolean = False
  163. Private _HintText As String = ""
  164. Public Property HintText As String
  165. Get
  166. Return _HintText
  167. End Get
  168. Set(value As String)
  169. _HintText = value
  170. If labHint IsNot Nothing Then labHint.Text = If(Text = "", HintText, "")
  171. End Set
  172. End Property
  173. Private Sub MyTextBox_TextChanged(sender As MyTextBox, e As TextChangedEventArgs) Handles Me.TextChanged
  174. Try
  175. '改变提示文本
  176. If labHint IsNot Nothing Then labHint.Text = If(Text = "", HintText, "")
  177. '改变输入记录
  178. IsTextChanged = IsLoaded
  179. '进行输入验证
  180. Validate()
  181. If Not ValidateResult = "" Then Exit Sub
  182. '改变文本
  183. RaiseEvent ValidatedTextChanged(sender, e)
  184. Catch ex As Exception
  185. Log(ex, "进行输入验证时出错", LogLevel.Assert)
  186. End Try
  187. End Sub
  188. '颜色
  189. Private Sub RefreshColor() Handles Me.IsEnabledChanged, Me.MouseEnter, Me.MouseLeave, Me.GotFocus, Me.LostFocus
  190. Try
  191. '手动关闭动画
  192. If Not HasAnimation Then Exit Sub
  193. '不对 ComboBox 从属进行动画
  194. If TemplatedParent IsNot Nothing AndAlso TypeOf TemplatedParent Is MyComboBox Then Exit Sub
  195. '判断当前颜色
  196. Dim ForeColorName As String, BackColorName As String
  197. Dim AnimationTime As Integer
  198. If IsEnabled Then
  199. If ValidateResult = "" OrElse Not IsTextChanged Then
  200. If IsFocused Then
  201. ForeColorName = "ColorBrush3"
  202. BackColorName = "ColorBrush7"
  203. AnimationTime = 10
  204. ElseIf IsMouseOver Then
  205. ForeColorName = "ColorBrush4"
  206. BackColorName = "ColorBrush7"
  207. AnimationTime = 100
  208. Else '未选中
  209. ForeColorName = "ColorBrushBg0"
  210. BackColorName = "ColorBrushHalfWhite"
  211. AnimationTime = 100
  212. End If
  213. Else
  214. ForeColorName = "ColorBrushRedLight"
  215. BackColorName = "ColorBrushRedBack"
  216. AnimationTime = 200
  217. End If
  218. Else
  219. ForeColorName = "ColorBrushGray5"
  220. BackColorName = "ColorBrushGray6"
  221. AnimationTime = 200
  222. End If
  223. '触发颜色动画
  224. If IsLoaded AndAlso AniControlEnabled = 0 Then '防止默认属性变更触发动画
  225. '有动画
  226. AniStart({
  227. AaColor(Me, BorderBrushProperty, ForeColorName, AnimationTime),
  228. AaColor(Me, BackgroundProperty, BackColorName, AnimationTime)
  229. }, "MyTextBox Color " & Uuid)
  230. Else
  231. '无动画
  232. AniStop("MyTextBox Color " & Uuid)
  233. SetResourceReference(BorderBrushProperty, ForeColorName)
  234. SetResourceReference(BackgroundProperty, BackColorName)
  235. End If
  236. Catch ex As Exception
  237. Log(ex, "文本框颜色改变出错")
  238. End Try
  239. End Sub
  240. Private Sub RefreshTextColor() Handles Me.IsEnabledChanged
  241. Dim NewColor As MyColor = If(IsEnabled, ColorGray1, ColorGray4)
  242. If CType(Foreground, SolidColorBrush).Color.R = NewColor.R Then Exit Sub
  243. If IsLoaded AndAlso AniControlEnabled = 0 AndAlso Not Text = "" Then
  244. '有动画
  245. AniStart({AaColor(Me, ForegroundProperty, If(IsEnabled, "ColorBrushGray1", "ColorBrushGray4"), 200)}, "MyTextBox TextColor " & Uuid)
  246. Else
  247. '无动画
  248. AniStop("MyTextBox TextColor " & Uuid)
  249. Foreground = NewColor
  250. End If
  251. End Sub
  252. End Class