MyTextBox.vb 13 KB

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