2013-02-20 31 views
1

我使用VS 2012,与WPF 4.5WPF如何融合互动触发添加到样式资源

我希望能够到混合交互触发添加到一个样式资源,这样我可以有一个定义放置(资源字典)并在我的应用程序中的许多地方使用。

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

这个问题是类似于下面的问题(但他们不具备整体解决方案):

EventToCommand in button style

How to add a Blend Behavior in a Style Setter

问题: 我怎样才能添加混合EventToCommand到在视图模型datacontext中绑定到命令的文本框lostFocus(我不想使用后面的代码或附加属性,我希望它在XAML中完全定义)?

回答

2

所以我必须承认,当我写这篇文章的时候,我有一个可行的答案,但是花了很长时间才弄明白,所以我在这里发布它,希望它能帮助别人,即使它是一个非常具体的场景。

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

所以我需要有文本框lostfocus eventcommand将文本框名称(与数据库中的列名匹配)作为命令参数。

这里是什么,我试图完成 WPF Screen Shot of Textbox Validation STyle

下面的屏幕截图是我是如何做的:

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中完成的,并且没有代码。

干杯

相关问题