silverlighter下MVVM模式中利用Behavior和TargetedTriggerAction實現文字框的一些特效

雲霏霏發表於2014-05-22

  在silverlight一般開發模式中,給文字框新增一些事件是輕而易舉的,然而MVVM開發模式中,想要給文字框新增一些事件並非那麼容易,因為MVVM模式中,只有ICommand介面,而且也只有Button中才有Command屬性,通過ViewModel可以將方法繫結到Button上面,卻無法繫結到文字框和其他一些控制元件。、

  Behavior的出現解決了這一難題,下面就來說一下具體的實現方法:

  例項一:在使用者登入視窗,使用者點選Reset按鈕後,讓使用者名稱輸入框自動獲取焦點。

  首先要先將ViewModel繫結到我們的控制元件上面,我們一步一步來做,第一步先寫Model,下面是Model的程式碼:

  

using System;
using System.Net;
using System.Runtime.Serialization;
using System.ComponentModel;

namespace BookModel
{
   [DataContract]
   public class UserModel : INotifyPropertyChanged
   {
      public event PropertyChangedEventHandler PropertyChanged;

      private string userName = String.Empty;
      private string passWord = String.Empty;

      [DataMember]
      public string UserName
      {
         get { return userName; }
         set { userName = value; OnPropertyChanged("UserName"); }
      }

      [DataMember]
      public string PassWord
      {
         get { return passWord; }
         set { passWord = value; OnPropertyChanged("PassWord"); }
      }

      /// <summary>
      /// Call the event PropertyChanged.
      /// </summary>
      /// <param name="PropertyName"></param>
      public void OnPropertyChanged(string PropertyName)
      {
         if (PropertyChanged != null)
         {
            PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
         }
      }
   }
}

 

  寫完了Model,下一步就是寫ViewModel了,在ViewModel中引用Model的命名控制元件,下面是ViewModel的程式碼,例子比較簡單,就不多解釋了。

  

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using BookModel;
using System.Windows.Browser;

namespace BookViewModel
{
   public class VM_User
   {
      /// <summary>
      /// In the Constructors create the instance of User.
      /// </summary>
      public VM_User()
      {
         user = new UserModel();
         user.UserName = "Jack";
      }

      public UserModel user { get; set; }
      public LoginCommand Login
      {
         get { return new LoginCommand(); }
      }
      public ResteCommand Reset
      {
         get { return new ResteCommand(); }
      }
   }

   /// <summary>
   /// Login Button's method
   /// </summary>
   public class LoginCommand:ICommand
   {
      public bool CanExecute(object parameter)
      {
         return true;
      }

      public void Execute(object parameter)
      {
         VM_User VMUser = parameter as VM_User;
         if (VMUser.user.UserName.Equals("admin") && VMUser.user.PassWord.Equals("123"))
         {
            MessageBox.Show("Login success!", "System Info", MessageBoxButton.OK);
         }
         else
         {
            MessageBox.Show("Login failed!", "System Info", MessageBoxButton.OK);
         }
      }

      public event EventHandler CanExecuteChanged;
   }

   /// <summary>
   /// Reset buttom's method
   /// </summary>
   public class ResteCommand : ICommand
   {
      public bool CanExecute(object parameter)
      {
         return true;
      }

      public void Execute(object parameter)
      {
         VM_User VMUser = parameter as VM_User;
         VMUser.user.UserName = "";
         VMUser.user.PassWord = "";
      }
      public event EventHandler CanExecuteChanged;
   }
}
View Code

  這裡就不連線資料庫了,那樣程式碼量太大,也不方便大家檢視。這裡只做一個簡單的驗證,至此MVVM中的M和VM就都建好了,下面寫前端的顯示層,新增兩個TextBlock和兩個TextBox,兩個按鈕。用作登入用,分別為使用者名稱,密碼,登入和重置。

  下面是繫結程式碼:

  

<UserControl x:Class="BookLibrary.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:BookViewModel;assembly=BookViewModel"
    mc:Ignorable="d"
    d:DesignHeight="600" d:DesignWidth="1000" 
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation">

   <UserControl.Resources>
      <local:VM_User x:Key="User" />
   </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="White" DataContext="{Binding Source={StaticResource User}}" Loaded="LayoutRoot_Loaded">
      <telerik:RadBusyIndicator Name="BusyIndicator">
         <Border>
            <Border.Background>
               <LinearGradientBrush EndPoint="0.5 1" StartPoint="0.5 0">
                  <GradientStop Color="#0369a9" Offset="0" />
                  <GradientStop Color="#046daf" Offset="0.5" />
                  <GradientStop Color="#2fa2e5" Offset="1" />
               </LinearGradientBrush>
            </Border.Background>
            <Border VerticalAlignment="Center" HorizontalAlignment="Center">
               <Border.Effect>
                  <DropShadowEffect BlurRadius="10" Opacity="0.4"/>
               </Border.Effect>
               <Border.Background>
                  <RadialGradientBrush>
                     <GradientStop Color="#0B7AC1" Offset="0" />
                     <GradientStop Color="#1182c7" Offset="1" />
                  </RadialGradientBrush>
               </Border.Background>
               <Grid>
                  <Grid.RowDefinitions>
                     <RowDefinition Height="60"/>
                     <RowDefinition Height="15"/>
                     <RowDefinition Height="45"/>
                     <RowDefinition Height="45"/>
                     <RowDefinition Height="60"/>
                  </Grid.RowDefinitions>
                  <Grid.ColumnDefinitions>
                     <ColumnDefinition Width="185"/>
                     <ColumnDefinition Width="90"/>
                     <ColumnDefinition Width="160"/>
                     <ColumnDefinition Width="100"/>
                  </Grid.ColumnDefinitions>
                  <TextBlock VerticalAlignment="Center" HorizontalAlignment="Left" 
                             Grid.Row="0" Grid.Column="0" Margin="20,0,0,0" FontWeight="Bold"   
                             Text="School System |" FontSize="16" Foreground="White"/>
                  <TextBlock Grid.Row="0" Grid.Column="1" HorizontalAlignment="Left" 
                             VerticalAlignment="Center" Text="User Login" FontSize="14"
                             Foreground="#b4e6ec"/>
                  <Image Source="Images/LoginPicture.png" Grid.Row="1" Grid.Column="0" Grid.RowSpan="3" />
                  <TextBlock FontSize="14" Grid.Row="2" Grid.Column="1" Text="UserName:" 
                             Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Left" />
                  <TextBlock FontSize="14" Grid.Row="3" Grid.Column="1" Text="PassWord:" 
                             Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Left" />
                  <TextBox Name="txtName"  Text="{Binding user.UserName,Mode=TwoWay}" HorizontalAlignment="Left" FontSize="14" Grid.Row="2" Grid.Column="2" Width="150" Height="26" />
                  <PasswordBox Password="{Binding user.PassWord,Mode=TwoWay}" HorizontalAlignment="Left" Grid.Row="3" Grid.Column="2" Width="150" Height="26" />
                  <Button Command="{Binding Login}" CommandParameter="{Binding}" HorizontalAlignment="Left" Width="80" Height="26" Grid.Row="2" Grid.Column="3" Content="Login" Click="Button_Click_1" />
                  <Button Command="{Binding Reset}" CommandParameter="{Binding}" HorizontalAlignment="Left" Width="80" Height="26" Grid.Row="3" Grid.Column="3" Content="Reset" Click="Button_Click" />
                  <CheckBox HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,10,0,0" Grid.Row="4" Grid.Column="2" Content="Save State" Foreground="White" />

               </Grid>
            </Border>
         </Border>
      </telerik:RadBusyIndicator>

   </Grid>
</UserControl>

  這個時候,就實現登入和重置功能了,但是重置的時候使用者名稱文字框並不會獲得焦點,下面我們來講實現方法:

  第一種方法:TargetedTriggerAction繫結

  先寫一個類,叫做TextBoxGetFocusBahavior,程式碼如下:

  

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Interactivity;

namespace BookLibrary
{
   public class TextBoxGetFocusBahavior:TargetedTriggerAction<TextBox>
   {
      public void GotFocusAction()
      {

      }
      protected override void Invoke(object parameter)
      {
         TextBox targetTextBox = Target;
         targetTextBox.SelectAll();
      }
   }
}
View Code

  然後在前臺繫結,繫結方法如下:

  

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
<TextBox Height="23" Name="textBox" HorizontalAlignment="Left" Margin="36,28,0,0" VerticalAlignment="Top" Width="198"> <i:Interaction.Triggers> <i:EventTrigger EventName="GotFocus"> <local:TextBoxGetFocusBahavior TargetName="textBox" /> </i:EventTrigger> </i:Interaction.Triggers> </TextBox>

  注意我標紅的地方,就是要引入名稱空間。

 

  第二種方法Behavior方法,和上面基本一樣,不過我感覺這種方法比較靈活,推薦使用這種方法。新加一個類,AutoSetFocusBehavior,程式碼如下:

  

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Interactivity;

namespace BookLibrary
{
   public class AutoSetFocusBehavior:Behavior<TextBox>
   {
      protected override void OnAttached()
      {
         base.OnAttached();
         AssociatedObject.TextChanged += OnTextChanged;
      }

      public void OnTextChanged(object sender, EventArgs e)
      {
         if(AssociatedObject.Text.Equals(""))
            AssociatedObject.Focus();
      }
   }
}
View Code

  這裡前端繫結方法為:

  

 <TextBox Text="{Binding user.UserName}" x:Name="txtName" Height="27" HorizontalAlignment="Left" Margin="36,76,0,0" VerticalAlignment="Top" Width="332">
         <i:Interaction.Behaviors>
            <local:AutoSetFocusBehavior />
         </i:Interaction.Behaviors>
      </TextBox>

  注意,同樣要引入上面的名稱空間。

  其實,說了這麼多,就是給TextBox加了個OnTextChanged事件,當內容被清空時,判斷內容是否為空,為空則設定焦點。

  希望這篇文章能給大家一點幫助。不足之處,還請賜教。

 

相關文章