2015-04-28 3 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; } 
     } 
    } 

} 

EDIT: Добавление реализации для 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 текстовых поля к свойствам в модели данных, и в datamodel убедитесь, что «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

Честно говоря, я по-прежнему нахожу WinForms немного легче на моем мозгу, насколько способ создания пользовательских интерфейсов. Тем не менее, я бы порекомендовал вникать в MVVM, если вы еще не пробовали - вы, безусловно, получите максимальную отдачу от своего XAML! – Craig

ответ

1

Ваш TextBox не обновляется, потому что вы не установили источник данных (DataContext обычно) за креплениями.

Когда вы пишете

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

То, что вы действительно говорите «тянуть значение для этого поля от TextBox.DataContext.dataModel.Value1». Если TextBox.DataContext имеет значение NULL, то ничего не будет отображаться.

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 привязок, чтобы удалить dataModel. из них

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

Это устанавливает DataContext из всей формы к объекту _data, а в методе OnAdd мы можем обновить свойства объекта _data, чтобы обновить пользовательский интерфейс.

Мне нравится блог немного о начинающей вещи 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', чтобы сообщить об этом пользовательскому интерфейсу. – BradleyDotNET

+0

спасибо Брэдли. Я прочитал «INotifyPropertyChanged». Я добавляю код для 'INotifyPropertyChanged', но' textBox3 'до сих пор не обновляет его содержимое при изменении соответствующего значения value3. – 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 для привязки свойства Text myText, который является элементом управления TextBlock, к MyDataProperty.

MyData myDataObject = new MyData(DateTime.Now); 
Binding myBinding = new Binding("MyDataProperty");  
myBinding.Source = myDataObject; 
myText.SetBinding(TextBlock.TextProperty, myBinding); 
Смежные вопросы