将视觉状态保持在标签控件中

时间:2019-05-12 10:45:22

标签: wpf

我读到,WPF选项卡控件的“功能”是在更改选项卡时卸载该选项卡的所有内容,因此不会保留该选项卡上控件的视觉状态。但是我知道,只有在使用标签控件的ItemsSource时,此“功能”才有效。

现在,我有了一个带有显式TabItem的标签控件(不使用ItemsSource),并且该标签控件仍然无法保留视觉状态。

我的代码中是否存在错误或缺少某些内容,还是选项卡控件的这种怪异的“功能”?如何在我的情况下保留控件的视觉状态?

这是我的示例代码:

ViewModelBase.vb (实现INotifyPropertyChangedINotifyDataErrorInfo

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个字符)。

即使切换选项卡,我怎么办仍能保持红色边框?

0 个答案:

没有答案