2015-04-28 39 views
1

我有一個簡單的WPF應用程序,包含3個文本框,2個文本框輸入數字,第三個文本框顯示單擊另一個按鈕時的輸入總和。在WPF中指定數據綁定

我來自WinForms和MFC背景,對我來說,直觀的做法是右鍵單擊文本框,打開它們的屬性並指定局部變量以從框中讀取數據。例如,MFC具有這種DDX機制。

但是,在WPF中,指定綁定的唯一方法似乎是將XAML代碼直接添加到App.XAML中,如here on MSDN所示。有沒有辦法創建一個綁定,而無需手動將其編碼到XAML中? XAML編碼對我來說似乎有點令人生畏,因爲我對它很陌生。

我的WPF形式如下:

<Window x:Class="SimpleAdd.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="350" Width="525"> 
    <Grid> 
     <TextBox HorizontalAlignment="Left" Height="23" Margin="174,43,0,0" TextWrapping="Wrap" Text="{Binding dataModel.Value1}" VerticalAlignment="Top" Width="120"/> 
     <TextBox HorizontalAlignment="Left" Height="23" Margin="174,84,0,0" TextWrapping="Wrap" Text="{Binding dataModel.Value2}" VerticalAlignment="Top" Width="120"/> 
     <TextBox HorizontalAlignment="Left" Height="23" Margin="174,127,0,0" TextWrapping="Wrap" Text="{Binding dataModel.Value3}" VerticalAlignment="Top" Width="120"/> 
     <Button Content="Add" HorizontalAlignment="Left" Margin="393,84,0,0" VerticalAlignment="Top" Width="75" Click="OnAdd"/> 
    </Grid> 
</Window> 

我的C#文件如下:

namespace SimpleAdd 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
     } 

     private void OnAdd(object sender, RoutedEventArgs e) 
     { 
      dataModel m1 = new dataModel(); 
      m1.Value3 = m1.Value1 + m1.Value2; // BUG : All Properties are 0 even after updating the boxes. 
     } 
    } 

    public class dataModel 
    { 
     private int val1, val2, val3; 

     public int Value1 
     { 
      get {return val1;} 
      set { val1 = value; } 
     } 
     public int Value2 
     { 
      get { return val2; } 
      set { val2 = value; } 
     } 
     public int Value3 
     { 
      get { return val3; } 
      set { val3 = value; } 
     } 
    } 

} 

編輯:添加實施INotifyPropertyChanged

namespace SimpleAdd 
{ 
    public abstract class ObservableObject : INotifyPropertyChanged 
    { 
     #region Debugging Aides 

     /// <summary> 
     /// Warns the developer if this object does not have 
     /// a public property with the specified name. This 
     /// method does not exist in a Release build. 
     /// </summary> 
     [Conditional("DEBUG")] 
     [DebuggerStepThrough] 
     public virtual void VerifyPropertyName(string propertyName) 
     { 
      // Verify that the property name matches a real, 
      // public, instance property on this object. 
      if (TypeDescriptor.GetProperties(this)[propertyName] == null) 
      { 
       string msg = "Invalid property name: " + propertyName; 

       if (this.ThrowOnInvalidPropertyName) 
        throw new Exception(msg); 
       else 
        Debug.Fail(msg); 
      } 
     } 

     /// <summary> 
     /// Returns whether an exception is thrown, or if a Debug.Fail() is used 
     /// when an invalid property name is passed to the VerifyPropertyName method. 
     /// The default value is false, but subclasses used by unit tests might 
     /// override this property's getter to return true. 
     /// </summary> 
     protected virtual bool ThrowOnInvalidPropertyName { get; private set; } 

     #endregion // Debugging Aides 

     #region INotifyPropertyChanged Members 

     /// <summary> 
     /// Raises the PropertyChange event for the property specified 
     /// </summary> 
     /// <param name="propertyName">Property name to update. Is case-sensitive.</param> 
     public virtual void RaisePropertyChanged(string propertyName) 
     { 
      this.VerifyPropertyName(propertyName); 
      OnPropertyChanged(propertyName); 
     } 

     /// <summary> 
     /// Raised when a property on this object has a new value. 
     /// </summary> 
     public event PropertyChangedEventHandler PropertyChanged; 

     /// <summary> 
     /// Raises this object's PropertyChanged event. 
     /// </summary> 
     /// <param name="propertyName">The property that has a new value.</param> 
     protected virtual void OnPropertyChanged(string propertyName) 
     { 
      this.VerifyPropertyName(propertyName); 

      PropertyChangedEventHandler handler = this.PropertyChanged; 
      if (handler != null) 
      { 
       var e = new PropertyChangedEventArgs(propertyName); 
       handler(this, e); 
      } 
     } 

     #endregion // INotifyPropertyChanged Members 
    } 

} 
+1

正確的WPF方法是將所有3個文本框綁定到數據模型的屬性上,並在數據模型中確保「Value3 = Value1 + Value2」,或創建一個「IMultiValueConverter」,將TextBox1和TextBox2它,並讓它合併它們並返回TextBox3的值。 XAML通常會用於這兩種情況。如果你不想以正確的WPF方式來做事情,那麼你可以使用代碼來執行'TextBox3.Text = int.Parse(TextBox1.Text)+ int.Parse(TextBox2.Text);'或者但是你想要它:) – Rachel

+0

@Rachel謝謝你給我看繩索。然而,我還沒有完成。我想學習如何使用正確的WPF方法,但基於新的實現,我不認爲我的綁定正在工作,因爲我的所有屬性都只有0值。 – user2654449

+0

老實說,就UI的構建方式而言,我仍然發現WinForms在我的大腦上更容易一點。然而,如果你還沒有嘗試過,我會建議鑽研MVVM--你一定會充分利用你的XAML! – Craig

回答

1

你的文本框,因爲你沒有設置綁定背後的數據源(通常DataContext)不被更新。

當你寫

<TextBox Text="{Binding dataModel.Value1}" /> 

你真正在說什麼?「從TextBox.DataContext.dataModel.Value1拉此字段的值」。如果TextBox.DataContext爲空,則不顯示任何內容。

DataContext被自動繼承,所以下面的代碼將工作:

public partial class MainWindow : Window 
{ 
    public dataModel _data { get; set; } 

    public MainWindow() 
    { 
     InitializeComponent(); 

     _data = new dataModel(); 
     this.DataContext = _data; 
    } 

    private void OnAdd(object sender, RoutedEventArgs e) 
    { 
     _data.Value3 = _data.Value1 + _data.Value2; 
    } 
} 

假設你也改變你的文本框綁定從中

<TextBox Text="{Binding Value1}" /> 

刪除dataModel.此設置的DataContext的整個表單到_data對象,並且在您的OnAdd方法中,我們可以更新_data對象屬性以便更新UI。

我喜歡在博客上一些關於初學者WPF的東西,你可能有興趣在檢查出一對夫婦的職位有哪個解釋這些概念:

+0

非常感謝。我正確地得到了_data.Value3 = _data.Value1 + _data.Value2;'。但是'data.Value3'不會被髮布到'TextBox3' – user2654449

+1

@ user2654449我要問你實現INotifyPropertyChanged'的',而是看你已經有了制定出:) – Rachel

+0

從您的博客瞭解到:)謝謝你許多 – user2654449

1

從技術上講,這不是在App.xaml(這是WPF中的一個特殊文件)。

也就是說,是的你可以做到這一點。您可以設置在代碼中像這樣綁定:

textBox1.Text = new Binding("SomeProperty"); 
... 

好了,變得非常討厭,所以我們只是做在XAML:

<TextBox Text="{Binding SomeProperty}"/> 

的代碼兩件做同樣的事情,但是當您使用更高級的綁定時,XAML語法更容易使用。此外,它更明顯的文字來自哪裏,而不必打開兩個文件。

+0

我嘗試了基於你的建議的綁定,但我認爲我做錯了,因爲綁定不起作用。你能否在上面找出錯誤並告訴我正確的實施? – user2654449

+1

@ user2654449對於初學者,您絕不會將窗口的DataContext設置爲您的數據類。另外,如果當屬性從代碼改變時,你需要使用'INotifyPropertyChanged'來讓UI知道。 – BradleyDotNET

+0

感謝布拉德利。我讀了'INotifyPropertyChanged'。我添加了'INotifyPropertyChanged'的代碼,但當相關的'value3'改變時,'textBox3'仍然不刷新它的內容 – user2654449

0

FrameworkElement類和FrameworkContentElement類都暴露了SetBinding方法。如果您綁定了繼承這兩個類中的任何一個的元素,則可以直接調用SetBinding方法。 以下示例創建一個名爲MyData的類,其中包含一個名爲MyDataProperty的屬性。

public class MyData : INotifyPropertyChanged 
{ 
private string myDataProperty; 

public MyData() { } 

public MyData(DateTime dateTime) 
{ 
    myDataProperty = "Last bound time was " + dateTime.ToLongTimeString(); 
} 

public String MyDataProperty 
{ 
    get { return myDataProperty; } 
    set 
    { 
     myDataProperty = value; 
     OnPropertyChanged("MyDataProperty"); 
    } 
} 

public event PropertyChangedEventHandler PropertyChanged; 

private void OnPropertyChanged(string info) 
{ 
    PropertyChangedEventHandler handler = PropertyChanged; 
    if (handler != null) 
    { 
     handler(this, new PropertyChangedEventArgs(info)); 
    } 
} 

}

下面的示例示出了如何創建綁定對象來設定結合的來源。該示例使用SetBinding將myText(TextBlock控件)的Text屬性綁定到MyDataProperty。

MyData myDataObject = new MyData(DateTime.Now); 
Binding myBinding = new Binding("MyDataProperty");  
myBinding.Source = myDataObject; 
myText.SetBinding(TextBlock.TextProperty, myBinding);