所以我必須承認,當我寫這篇文章的時候,我有一個可行的答案,但是花了很長時間才弄明白,所以我在這裏發佈它,希望它能幫助別人,即使它是一個非常具體的場景。
我爲我的應用程序使用MVVM模型,所以我不想在xaml頁面後面有代碼。我還想要一種方法將文本框綁定到IDataErrorInfo屬性,其中通過文本框的lostfocus事件觸發對該文本框的驗證。此事件將綁定到視圖模型上的中繼命令,該命令將驗證適用的對象並添加實際的錯誤。
所以我需要有文本框lostfocus eventcommand將文本框名稱(與數據庫中的列名匹配)作爲命令參數。
這裏是什麼,我試圖完成
下面的屏幕截圖是我是如何做的:
Imports GalaSoft.MvvmLight.Command
Private _LostFocusValidateCommand As RelayCommand(Of String)
Public ReadOnly Property LostFocusValidateCommand() As RelayCommand(Of String)
Get
If _LostFocusValidateCommand Is Nothing Then
_LostFocusValidateCommand = New RelayCommand(Of String)(AddressOf LostFocusValidateExecute)
End If
Return _LostFocusValidateCommand
End Get
End Property
Private Sub LostFocusValidateExecute(sParam As String)
NewClient.PropertyValitaion(False, sParam)
End Sub
:
首先,我在視圖模型中定義的命令
這裏是使用IDataErrorInfo的屬性驗證(我遺漏了他IDataErrorInfo的基本實現來節省空間,如果你想讓我發佈它,留下評論)
Public Sub PropertyValitaion(bAllProperties As Boolean, Optional sProperty As String = "")
'initialize validation helper
Dim vhelper As New ValidationHelper
If bAllProperties Or sProperty = "chrCompany" Then
If String.IsNullOrEmpty(chrCompany) Then
AddError("chrCompany", "You must enter a Company Name")
Else
RemoveError("chrCompany")
End If
End If
If bAllProperties Or sProperty = "chrFirst" Then
If String.IsNullOrEmpty(chrFirst) Then
AddError("chrFirst", "You must enter a First Name")
Else
RemoveError("chrFirst")
End If
End If
If bAllProperties Or (sProperty = "chrPhone1" Or sProperty = "chrPhone1Ext") Then
If String.IsNullOrEmpty(Trim(chrPhone1Ext)) = False And String.IsNullOrEmpty(Trim(chrPhone1)) Then
Me.AddError("chrPhone1", "Provide a phone number or remove extension")
Else
RemoveError("chrPhone1")
End If
If String.IsNullOrEmpty(Trim(chrPhone1)) = False Then
If vhelper.CheckPhoneNumber(Me.chrPhone1) = False Then
Me.AddError("chrPhone1", "Phone 1 format invalid")
Else
RemoveError("chrPhone1")
End If
End If
End If
End Sub
困難的部分是搞清楚如何定義樣式。風格很長,對不起,「可讀」xml的樂趣:
<Style x:Key="FTC_ValidateTextBox" BasedOn="{x:Null}" TargetType="{x:Type TextBox}">
<Style.Setters>
<Setter Property="FontFamily" Value="Open Sans Condensed"/>
<Setter Property="FontSize" Value="19" />
<Setter Property="Margin" Value="3,3,15,6"/>
<Setter Property="Padding" Value="10,3"/>
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Background" Value="{StaticResource DetailTextBox}" />
<Setter Property="BorderBrush" Value="{StaticResource MediumGray}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border Name="Bd" SnapsToDevicePixels="true" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
<ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="LostFocus">
<cmd:EventToCommand Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},Path=DataContext.LostFocusValidateCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TextBox}},Path=Name}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ScrollViewer>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="{StaticResource MediumRed}" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<AdornedElementPlaceholder Name="parentTextBox" />
<TextBlock Grid.Row="1" Style="{StaticResource FTC_DetailError}"
Text="{Binding ElementName=parentTextBox, Path=AdornedElement.(Validation.Errors).CurrentItem.ErrorContent}"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
<Setter Property="BorderBrush" Value="{StaticResource MediumRed}"/>
<Setter Property="Foreground" Value="{StaticResource MediumRed}"/>
<Setter Property="Margin" Value="3,3,15,31"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="FTC_DetailError" TargetType="TextBlock">
<Style.Setters>
<Setter Property="FontFamily" Value="Open Sans Condensed"/>
<Setter Property="Control.FontWeight" Value="Light" />
<Setter Property="Foreground" Value="{StaticResource TitleWhite}"/>
<Setter Property="FontSize" Value="15" />
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="10,3"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Background" Value="{StaticResource MediumRed}"/>
</Style.Setters>
</Style>
所有的魔法都發生在屬性模板中。下列情況必須包含在資源字典的頂部聲明:
> xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
> xmlns:cmd="http://www.galasoft.ch/mvvmlight"
所有魔術發生在定義的控件模板的模板屬性。你不能在控制模板本身中包裝一個i:交互,它必須包含在一個派生的對象中,幾乎任何東西,邊界,scrollviewer,wrappanel等......然後你設置通風觸發器和命令屬性。它們應該很容易遵循,我將文本框名稱作爲命令參數傳遞。您在屏幕截圖中看到的客戶端「框」是一個網格,其數據上下文設置爲父視圖模型的新客戶端對象屬性。所以爲了訪問父視圖模型中的命令,我必須引用父級的datacontext並調用命令屬性。
同樣,我意識到這是一個非常具體的場景,但我認爲它有一些可以幫助其他人的例子。現在我可以爲應用程序中的所有文本框定義一種樣式,它們是數據輸入,並且我想要觸發基本的驗證過程。這將使我不必單獨在所有文本框上定義自定義命令行爲,而這一切都是在xaml中完成的,並且沒有代碼。
乾杯