1
0

MyTextBox.vb 12 KB

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