zoukankan      html  css  js  c++  java
  • 《Programming WPF》翻译 第4章 1.不使用数据绑定

    考虑一个非常简单的应用程序:遍及一个人的名字和年龄,正如图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) {
        
    ifthis.PropertyChanged != null ) {
          PropertyChanged(
    thisnew 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值初始化了NameAge的文本框,订阅了随属性改变的事件,用来保持当Person对象改变时文本框仍然是最新的。在这段代码的恰当位置,birthday按钮的click事件句柄不需要手动更新文本框,当Tom的年龄改变的时候;代替的,更新Age属性引起层叠式事件保持年龄的文本框是最新的,随着Person对象的改变,正如图4-3所示。

    4-3


    步骤如下:

    1.用户点击按钮,引起Click的事件被激活

    2.Click句柄从Person对象获得年龄:9

    3.Click句柄将Person对象的年龄为10

    4.PersonAge属性设置器激发了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;
        
    ifint.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按钮事件句柄,是所有的改变同步,当我们显示消息框的时候。然而,容易想象到,当对象的数量增加或者对象属性的数量增加时,这段代码很快就会失去控制。加上,这看起来就像一件相当普通的事情,以至于有人一定事先就提供了一种更简单的方法来做这件事。事实上,这种方法被称为“数据绑定”。

  • 相关阅读:
    c# 子窗体打开或者切换就最大化
    TreeView失去焦点时亮显选中状态
    解决 RecursionError: maximum recursion depth exceeded
    CentOS7安装jdk1.8
    CentOS7安装mysql5.7
    CentOS7安装redis3.2.3
    CentOS7安装nginx1.8
    centos7设置redis开机自启动
    Linux下执行sh脚本报错:$' ': command not found
    Linux下使用unhide查看隐藏文件
  • 原文地址:https://www.cnblogs.com/Jax/p/1137279.html
Copyright © 2011-2022 走看看