WPF如何将混合交互触发器添加到样式资源 [英] WPF How to add blend interaction trigger to style resource

查看:113
本文介绍了WPF如何将混合交互触发器添加到样式资源的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用VS 2012和WPF 4.5

I am using VS 2012, with 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.

具体来说,我想使用MVVM-Light框架随附的EventToCommand,并将其插入文本框样式并附加到文本框的LostFocus事件.我打算使用此标记来标记带有 ValidationStyle 的某些文本框,该文本框将触发绑定命令(在视图模型中)到Textbox的LostFocus事件.这种验证样式将使用IDataErrorInfo通过UI向用户显示错误.

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 ValidationStyle that 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.

此问题与以下问题类似(但它们没有完整的解决方案):

This question is similar to the following questions (but they do not have the total solution):

按钮样式的EventToCommand

如何添加混合样式设置器中的行为

问题: 如何将混合EventToCommand添加到绑定到viewmodel datacontext中的命令的文本框lostfocus(我不想使用背后的代码或附加属性,我希望在XAML中完全定义它)?

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)?

推荐答案

所以我必须承认在撰写本文时我有一个有效的答案,但是花了我很长时间才弄清楚,所以我希望将其发布在这里即使是非常特殊的情况,它也可以帮助其他人.

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.

我在我的应用程序中使用了MVVM模型,所以我不想在xaml页面后面添加代码.我还想要一种将文本框绑定到IDataErrorInfo属性的方法,在该属性中,通过文本框的lostfocus事件触发对该文本框的验证.此事件将绑定到视图模型上的中继命令,该命令将验证适用对象并添加实际错误.

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.

因此,我需要让文本框lostfocus event命令将文本框名称(与数据库中的列名称匹配)作为命令参数.

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.

以下是我要完成的操作的屏幕截图

Here is a screen shot of what I am trying to accomplish

这是我的操作方式:

首先,我在视图模型上定义了命令:

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

这是使用IDataErrorInfo进行的属性验证(我省去了IDataErrorInfo的基本实现以节省空间,如果要我发布它,请发表评论)

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)

 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的乐趣在于:

The hard part was figuring out how to define the style. The style is long, sorry, the joys of "readable" 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"

所有魔术都发生在定义控件模板的template属性中.您不能在控件模板本身中包装i:interaction,它必须包含在派生对象中,几乎包括边框,scrollviewer,wrappanel等几乎所有内容.然后,设置通风口触发器和命令属性.它们应该很容易遵循,我将文本框名称作为命令参数传递.您在屏幕快照中看到的客户端框"是一个网格,其数据上下文设置为父视图模型的新客户端对象属性.因此,为了访问父视图模型中的命令,我必须引用父视图的datacontext并调用command属性.

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.

同样,我意识到这是一个非常特殊的情况,但是我认为它有一些可以帮助其他人的例子.现在,我可以为应用程序中所有要输入数据且要触发基本验证过程的文本框定义一种样式.这将使我不必在所有这些文本框上分别定义自定义命令行为,而这一切都在xaml中完成,而没有代码.

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.

欢呼

这篇关于WPF如何将混合交互触发器添加到样式资源的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆