2013-02-20 32 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中完成的,並且沒有代碼。

乾杯

相關問題