程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 《Programming WPF》翻譯 第4章 1.不使用數據綁定

《Programming WPF》翻譯 第4章 1.不使用數據綁定

編輯:關於.NET

考慮一個非常簡單的應用程序:遍及一個人的名字和年齡,正如圖4-1所示:

圖4-1

圖4-1可以實現為一個簡單的xaml如示例4-1。

示例4-1

<!-- Window1.xaml -->
<Window >
  <Grid>
   
    <TextBlock >Name:</TextBlock>
    <TextBox x:Name="nameTextBox"  />
    <TextBlock >Age:</TextBlock>
    <TextBox x:Name="ageTextBox"  />
    <Button x:Name="birthdayButton" >Birthday</Button>
  </Grid>
</Window>

在這個簡單應用程序中顯示的數據,可以被一個簡單的類表現,如示例4-2所 示。

示例4-2

public class Person {
  string name;
  public string Name {
    get { return this.name; }
    set { this.name = value; }
  }

  int age;
  public int Age {
    get { return this.age; }
    set { this.age = value; }
  }

  public Person(  ) {}
  public Person(string name, int age) {
    this.name = name;
    this.age = age;
  }
}

通過這個類,可以自然的實現我們的應用程序行為,如示例4-3所示:

示例4-3

// Window1.xaml.cs

public class Person {}

public partial class Window1 : Window {
  Person person = new Person("Tom", 9);

  public Window1(  ) {
    InitializeComponent(  );

    // Fill initial person fields
    this.nameTextBox.Text = person.Name;
    this.ageTextBox.Text = person.Age.ToString(  );

    this.birthdayButton.Click += birthdayButton_Click;
  }

  void birthdayButton_Click(object sender, RoutedEventArgs e) {
    ++person.Age;
    MessageBox.Show(
      string.Format(
        "Happy Birthday, {0}, age {1}!",
        person.Name,
        person.Age),
      "Birthday");
  }
}

示例4-3的代碼創建了一個Person對象,並且用Person對象的屬性初始化了文 本框。當Birthday按鈕按下時,Person對象的Age屬性值會增加,同時在一個消 息框中顯示更新後的Person數據,如圖4-2所示。

圖4-2

我們的簡單應用程序實現,事實上,非常的簡單。Person對象的Age屬性在改 變後,顯示在消息框中,但是不會顯示在主窗體中。一個保持應用程序UI是最新 的辦法是,編寫代碼使得無論一個Person對象何時更新,將會同時間手動更新UI ,正如示例4-4所示。

示例4-4

void birthdayButton_Click(object sender, RoutedEventArgs e) {
  ++person.Age;

  // Manually update the UI
  this.ageTextBox.Text = person.Age.ToString(  );

  MessageBox.Show(
    string.Format(
      "Happy Birthday, {0}, age {1}!",
      person.Name,
      person.Age),
    "Birthday");
}

僅僅一行代碼,我們就“修復”了這個應用程序。這是一個誘人而且流行的 方法,然而不能隨著應用程序變得復雜而伸縮,並且需要更多這樣的“單行”代 碼。我們需要一個更好的方法,超越於最簡單的應用程序之上。

4.1.1對象的改變

對於UI,一個更健壯的跟蹤對象改變的方法是,當對象改變的時候為這個對 象激發一個事件。從.NET2.0時開始,正確的方法是為這個對象實現 INotifyPropertyChanged接口,正如示例4-5。

示例4-5

using System.ComponentModel; // INotifyPropertyChanged

public class Person : INotifyPropertyChanged {
  // INotifyPropertyChanged Members
  public event PropertyChangedEventHandler PropertyChanged;
  protected void OnPropertyChanged(string propName) {
    if( this.PropertyChanged != null ) {
      PropertyChanged(this, new PropertyChangedEventArgs (propName));
    }
  }

  string name;
  public string Name {
    get { return this.name; }
    set {
      this.name = value;
      OnPropertyChanged("Name");
    }
  }

  int age;
  public int Age {
    get { return this.age; }
    set {
      this.age = value;
      OnPropertyChanged("Age");
    }
  }

  public Person(  ) {}
  public Person(string name, int age) {
    this.name = name;
    this.age = age;
  }
}

在示例4-5中,當Person對象的任意一個屬性改變時(如由Birthday按鈕激活 引起的實現),就會激活該對象的PropertyChanged事件。我們可以使用這個事 件保持UI同步於Person的屬性值,正如示例4-6。

示例4-6

// Window1.xaml.cs

public class Person : INotifyPropertyChanged {}
public partial class Window1 : Window {
  Person person = new Person("Tom", 9);

  public Window1(  ) {
    InitializeComponent(  );

    // Fill initial person fields
    this.nameTextBox.Text = person.Name;
    this.ageTextBox.Text = person.Age.ToString(  );

    // Watch for changes in Tom's properties
    person.PropertyChanged += person_PropertyChanged;

    this.birthdayButton.Click += birthdayButton_Click;
  }

  void person_PropertyChanged(
    object sender,
    PropertyChangedEventArgs e) {

    switch( e.PropertyName ) {
      case "Name":
      this.nameTextBox.Text = person.Name;
      break;

      case "Age":
      this.ageTextBox.Text = person.Age.ToString(  );
      break;
    }
  }

  void birthdayButton_Click(object sender, RoutedEventArgs e) {
    ++person.Age; // person_PropertyChanged will update ageTextBox
    MessageBox.Show(
      string.Format(
        "Happy Birthday, {0}, age {1}!",
        person.Name,
        person.Age),
      "Birthday");
  }
}

示例4-6顯示了一個單獨的Person示例,創建於主窗體第一次開始出現,使用 Person值初始化了Name和Age的文本框,訂閱了隨屬性改變的事件,用來保持當 Person對象改變時文本框仍然是最新的。在這段代碼的恰當位置,birthday按鈕 的click事件句柄不需要手動更新文本框,當Tom的年齡改變的時候;代替的,更 新Age屬性引起層疊式事件保持年齡的文本框是最新的,隨著Person對象的改變 ,正如圖4-3所示。

圖4-3

步驟如下:

1.用戶點擊按鈕,引起Click的事件被激活

2.Click句柄從Person對象獲得年齡:9

3.Click句柄將Person對象的年齡為10

4.Person的Age屬性設置器激發了PropertyChanged事件

5. PropertyChanged事件傳遞到UI代碼的事件句柄

6.UI代碼更新年齡的文本框,從9改為10

7.按鈕的Click事件句柄顯示一個消息框,顯示新的年齡:10

在消息框顯示Tom的新年齡之前,在表單中,年齡的文本框已經更新了,如圖 4-4所示

圖4-4

隨著處理了InotifyPropertyChanged的事件,當對象的數據改變時,UI將更 新以反映這種改變。然而,這僅僅解決了問題的一半;我們仍然需要處理如何將 UI中的改變反映到對象中。

4.1.2 控件的改變

不考慮跟蹤UI改變並將其反映到對象的特定方法,我們可以容易地以一個例 子告終(想改變一個人的名字),顯示對象(正如點擊Birthday按鈕時所發的) ,以及期望改變已經發生,僅僅對圖4-5有所失望。

圖4-5

注意到圖4-5,表單中,名字是“Thomsen Federick”,而消息框中是“Tom ”,這顯示了UI的一部分已經改變,而底層的對象並未改變。為了修復這個問題 ,我們觀察文本框對象的Text屬性的改變,相應的更新Person對象,如示例4-7 。

示例4-7

public partial class Window1 : Window {
  Person person = new Person("Tom", 9);

  public Window1(  ) {
    InitializeComponent(  );

    // Fill initial person fields
    this.nameTextBox.Text = person.Name;
    this.ageTextBox.Text = person.Age.ToString(  );

    // Watch for changes in Tom's properties
    person.PropertyChanged += person_PropertyChanged;

    // Watch for changes in the controls
    this.nameTextBox.TextChanged += nameTextBox_TextChanged;
    this.ageTextBox.TextChanged += ageTextBox_TextChanged;

    this.birthdayButton.Click += birthdayButton_Click;
  }

 

  void nameTextBox_TextChanged(object sender, TextChangedEventArgs e) {
    person.Name = nameTextBox.Text;
  }

  void ageTextBox_TextChanged(object sender, TextChangedEventArgs e) {
    int age = 0;
    if( int.TryParse(ageTextBox.Text, out age) ) {
      person.Age = age;
    }
  }

  void birthdayButton_Click(object sender, RoutedEventArgs e) {
    ++person.Age;

    // nameTextBox_TextChanged and ageTextBox_TextChanged
    // will make sure the Person object is up to date
    MessageBox.Show(
      string.Format(
        "Happy Birthday, {0}, age {1}!",
        person.Name,
        person.Age),
      "Birthday");
  }
}

現在,不論數據如何改變,Person對象和顯示Person對象的UI會保持同步。 圖4-6顯示了UI中名字的改變,正確傳到了Person對象。

圖4-6

盡管我們得到了想要的功能,仍需要寫相當多的代碼使之發生:

Window1代碼重構,設置控件的初始值

Window1代碼重構,使用PropertyChanged事件鉤子,對Person對象的屬性更 改進行跟蹤。

PropertyChanged事件句柄從Person對象獲取更新過的數據,將數據轉換為適 當的字符串

Window1代碼重構,使用TextBox對象的TextChanged事件鉤子,跟蹤UI的改變

TextChanged事件句柄將更新過的TextBox數據傳入Person對象,將數據適當 的轉換

這段代碼允許我們安全的寫自己的birthday按鈕事件句柄,是所有的改變同 步,當我們顯示消息框的時候。然而,容易想象到,當對象的數量增加或者對象 屬性的數量增加時,這段代碼很快就會失去控制。加上,這看起來就像一件相當 普通的事情,以至於有人一定事先就提供了一種更簡單的方法來做這件事。事實 上,這種方法被稱為“數據綁定”。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved