我读到,WPF选项卡控件的“功能”是在更改选项卡时卸载该选项卡的所有内容,因此不会保留该选项卡上控件的视觉状态。但是我知道,只有在使用标签控件的ItemsSource
时,此“功能”才有效。
现在,我有了一个带有显式TabItem
的标签控件(不使用ItemsSource
),并且该标签控件仍然无法保留视觉状态。
我的代码中是否存在错误或缺少某些内容,还是选项卡控件的这种怪异的“功能”?如何在我的情况下保留控件的视觉状态?
这是我的示例代码:
ViewModelBase.vb (实现INotifyPropertyChanged
和INotifyDataErrorInfo
)
Imports System.ComponentModel
Public Class ViewModelBase
Implements INotifyPropertyChanged
Implements INotifyDataErrorInfo
Private _lock As Object = New Object
Private ReadOnly _errors As Dictionary(Of String, List(Of String)) = New Dictionary(Of String, List(Of String))
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Event ErrorsChanged As EventHandler(Of DataErrorsChangedEventArgs) Implements INotifyDataErrorInfo.ErrorsChanged
Protected Overridable Sub OnErrorsChanged(propertyName As String)
RaiseEvent ErrorsChanged(Me, New DataErrorsChangedEventArgs(propertyName))
End Sub
Protected Overridable Sub OnPropertyChanged(propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
Me.ValidateAsync()
End Sub
Public ReadOnly Property HasErrors As Boolean Implements INotifyDataErrorInfo.HasErrors
Get
Return _errors.Any(Function(x As KeyValuePair(Of String, List(Of String))) (x.Value IsNot Nothing) AndAlso (x.Value.Count > 0))
End Get
End Property
Public Function GetErrors(propertyName As String) As IEnumerable Implements INotifyDataErrorInfo.GetErrors
If Not String.IsNullOrEmpty(propertyName) AndAlso _errors.ContainsKey(propertyName) Then
Return _errors(propertyName)
End If
Return Nothing
End Function
Protected Sub SetErrors(ByVal propertyName As String, ByVal errors As List(Of String))
If String.IsNullOrEmpty(propertyName) Then
Exit Sub
End If
If (errors Is Nothing) OrElse (errors.Count = 0) Then
If _errors.ContainsKey(propertyName) Then
_errors.Remove(propertyName)
Call Me.OnErrorsChanged(propertyName)
End If
Else
If _errors.ContainsKey(propertyName) Then
_errors(propertyName) = errors
Else
_errors.Add(propertyName, errors)
End If
Call Me.OnErrorsChanged(propertyName)
End If
End Sub
Public Function ValidateAsync() As Task(Of Boolean)
Return Task(Of Boolean).Run(Of Boolean)(AddressOf Me.ValidateInternal)
End Function
Public Function Validate() As Boolean
SyncLock _lock
Return Me.ValidateInternal()
End SyncLock
End Function
Protected Overridable Function ValidateInternal() As Boolean
Return False
End Function
End Class
MainViewModel.vb
Public Class MainViewModel
Inherits ViewModelBase
Private _textTab1 As String
Private _textTab2 As String
Public Property TextTab1 As String
Get
Return _textTab1
End Get
Set(value As String)
_textTab1 = value
MyBase.OnPropertyChanged(NameOf(Me.TextTab1))
End Set
End Property
Public Property TextTab2 As String
Get
Return _textTab2
End Get
Set(value As String)
_textTab2 = value
MyBase.OnPropertyChanged(NameOf(Me.TextTab2))
End Set
End Property
Protected Overrides Function ValidateInternal() As Boolean
Dim result As Boolean
Dim textTab1Errors As List(Of String)
Dim textTab2Errors As List(Of String)
result = MyBase.ValidateInternal()
textTab1Errors = New List(Of String)
textTab2Errors = New List(Of String)
If Not String.IsNullOrEmpty(Me.TextTab1) AndAlso (Me.TextTab1.Length > 30) Then
textTab1Errors.Add("The text on tab 1 is longer than 30 characters.")
result = True
End If
MyBase.SetErrors(NameOf(Me.TextTab1), textTab1Errors)
If Not String.IsNullOrEmpty(Me.TextTab2) AndAlso (Me.TextTab2.Length > 40) Then
textTab2Errors.Add("The text on tab 2 is longer than 40 characters.")
result = True
End If
MyBase.SetErrors(NameOf(Me.TextTab2), textTab2Errors)
Return result
End Function
End Class
MainWindow.xaml
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TabTest"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=local:MainViewModel, IsDesignTimeCreatable=True}"
Title="MainWindow" Height="200" Width="400">
<Window.DataContext>
<local:MainViewModel TextTab1="This is just a test." TextTab2="And this is another test." />
</Window.DataContext>
<TabControl IsSynchronizedWithCurrentItem="True">
<TabItem Header="Tab 1">
<TextBox AcceptsReturn="True" VerticalScrollBarVisibility="Auto" Text="{Binding TextTab1, UpdateSourceTrigger=PropertyChanged}" />
</TabItem>
<TabItem Header="Tab 2">
<TextBox AcceptsReturn="True" VerticalScrollBarVisibility="Auto" Text="{Binding TextTab2, UpdateSourceTrigger=PropertyChanged}" />
</TabItem>
</TabControl>
</Window>
如果运行程序,您将看到带有两个选项卡的选项卡控件。第一个标签上的文字限制为30个字符,第二个标签上的文字限制为40个字符。
如果您继续在第一个标签上的文本中添加字符,则超过30个字符时,会出现红色边框。然后,当您切换到第二个选项卡并返回到第一个选项卡时,红色边框消失了,尽管仍然存在错误原因(超过30个字符)。
即使切换选项卡,我怎么办仍能保持红色边框?