wpf WPF如何将混合交互触发器添加到样式资源
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14986045/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WPF How to add blend interaction trigger to style resource
提问by J King
I am using VS 2012, with WPF 4.5
我正在使用 VS 2012,WPF 4.5
I want to be able to add a blend interaction trigger to a style resource so that I can have that defined in one place (resource dictionary) and use in many places throughout my app.
我希望能够向样式资源添加混合交互触发器,以便我可以在一个地方(资源字典)定义它并在我的应用程序中的许多地方使用。
Specifically, I want to use the EventToCommand that comes with the MVVM-Light framework and have it inserted into a textbox style and attached to the LostFocus event of the textbox. I am planning on using this to mark certain textboxes with a ValidationStylethat triggers a bound command (in a viewmodel) to the LostFocus event of the Textbox. This validation style will use the IDataErrorInfo to display errors to the user through the UI.
具体来说,我想使用 MVVM-Light 框架附带的 EventToCommand 并将其插入到文本框样式中并附加到文本框的 LostFocus 事件。我计划使用它来标记某些带有ValidationStyle 的文本框,该样式触发一个绑定命令(在视图模型中)到文本框的 LostFocus 事件。此验证样式将使用 IDataErrorInfo 通过 UI 向用户显示错误。
This question is similar to the following questions (but they do not have the total solution):
这个问题类似于以下问题(但他们没有完整的解决方案):
EventToCommand in button style
How to add a Blend Behavior in a Style Setter
QUESTION:How can I add a blend EventToCommand to the textbox lostfocus that is bound to a command in the viewmodel datacontext (I don't want to use code behind or attached property, I want it to be totally defined in XAML)?
问题:如何将混合 EventToCommand 添加到文本框 lostfocus 绑定到视图模型数据上下文中的命令(我不想使用代码隐藏或附加属性,我希望它完全在 XAML 中定义)?
回答by J King
So I must admit that I had a working answer when I wrote this, but it took me a long time to figure it out so I am posting it here hoping it helps someone else even though it is a very specific scenario.
所以我必须承认,当我写这篇文章时,我有一个可行的答案,但我花了很长时间才弄明白,所以我把它张贴在这里,希望它能帮助别人,即使这是一个非常具体的场景。
I am using the MVVM model for my application so I don't want to have code behind the xaml pages. I also wanted a way to have a textbox bind to the IDataErrorInfo properties where the validation for that textbox is triggered through the lostfocus event of the textbox. This event will be bound to a relay command on the viewmodel that will validate the applicable object and add realted errors.
我正在为我的应用程序使用 MVVM 模型,所以我不想在 xaml 页面后面有代码。我还想要一种将文本框绑定到 IDataErrorInfo 属性的方法,其中该文本框的验证是通过文本框的 lostfocus 事件触发的。此事件将绑定到视图模型上的中继命令,该命令将验证适用对象并添加相关错误。
So i needed to have the textbox lostfocus eventcommand take the textbox name (which matches the column names from the database) as a command parameter.
所以我需要让文本框 lostfocus eventcommand 将文本框名称(与数据库中的列名称匹配)作为命令参数。
Here is a screen shot of what I am trying to accomplish

这是我正在尝试完成的屏幕截图

Here is how I did it:
这是我如何做到的:
First I defined the command on the view model:
首先我在视图模型上定义了命令:
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
here is the property validation using IDataErrorInfo (I left out he basic implementation of IDataErrorInfo to save space, leave a comment if you want me to post it)
这是使用 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
The hard part was figuring out how to define the style. The style is long, sorry, the joys of "readable" xml:
困难的部分是弄清楚如何定义风格。样式很长,抱歉,“可读” 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>
all the magic happens in the property template. THe following must be included in the top declarations of your resource dictionary:
所有的魔法都发生在属性模板中。以下内容必须包含在资源字典的顶部声明中:
> xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
> xmlns:cmd="http://www.galasoft.ch/mvvmlight"
all the magic happens in the template property that defines the control template. You can not wrap a i:interaction in the control template itself, it must be contained within a derived object, almost anything really, border, scrollviewer, wrappanel etc... Then you set the vent trigger and the command properties. They should be easy enough to follow, I pass the textbox name as the command parameter. The client "box" you see in the screen shot is a grid with its data context set to a new client object property of the parent viewmodel. SO in order to access the command in the parent viewmodel, I had to reference the parent's datacontext and call the command property.
所有的魔法都发生在定义控件模板的模板属性中。您不能将 ai:interaction 包装在控件模板本身中,它必须包含在派生对象中,几乎任何东西,边框、滚动查看器、包装面板等......然后设置通风触发器和命令属性。它们应该很容易理解,我将文本框名称作为命令参数传递。您在屏幕截图中看到的客户端“框”是一个网格,其数据上下文设置为父视图模型的新客户端对象属性。所以为了访问父视图模型中的命令,我必须引用父视图的数据上下文并调用命令属性。
Again, I realize that this is a very specific scenario, but I thought it has some examples that might be able to help others. I am now able to define one style for all textboxes in the application that are data-entry and that I want to trigger basic validation procedures. It will save me having to define the custom command behaviour on all those text boxes individually, and this is all accomplished in xaml, with out code behind.
再次,我意识到这是一个非常具体的场景,但我认为它有一些可能能够帮助其他人的例子。我现在可以为应用程序中的所有文本框定义一种样式,这些文本框是数据输入并且我想触发基本验证过程。这将使我不必在所有这些文本框上单独定义自定义命令行为,而这一切都是在 xaml 中完成的,没有代码。
Cheers
干杯

