我有一个简单的WPF应用程序,有3个文本框,2个文本框输入数字,第三个文本框显示单击另一个按钮时的输入总和。
我来自WinForms和MFC背景,对我来说,直观的做法是右键单击textBoxes,打开它们的属性并指定局部变量来从框中读取数据。例如,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
}
}
答案 0 :(得分:1)
从技术上讲,这不在App.xaml
中(这是WPF中的特殊文件)。
那就是说,是你可以做到。您可以在代码中设置绑定,如下所示:
textBox1.Text = new Binding("SomeProperty");
...
好的,这真的很烦人,所以我们只是在XAML中这样做:
<TextBox Text="{Binding SomeProperty}"/>
两段代码都执行相同的操作,但是当您进入更高级的绑定时,XAML语法更容易使用。此外,它更明显地来自文本的来源,而不是必须打开两个文件。
答案 1 :(得分:1)
您的TextBox未更新,因为您尚未在绑定后面设置数据源(通常为DataContext
)。
写作时
<TextBox Text="{Binding dataModel.Value1}" />
您真正说的是&#34;从TextBox.DataContext.dataModel.Value1
&#34;中提取此字段的值。如果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绑定以从中删除dataModel.
<TextBox Text="{Binding Value1}" />
这会将整个表单的DataContext设置为_data
对象,并且在OnAdd方法中,我们可以更新_data
对象属性以便更新UI。
我喜欢在博客上写一些关于初学WPF的东西,你可能有兴趣查看一些解释这些概念的帖子:
答案 2 :(得分: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的Text属性(一个TextBlock控件)绑定到MyDataProperty。
MyData myDataObject = new MyData(DateTime.Now);
Binding myBinding = new Binding("MyDataProperty");
myBinding.Source = myDataObject;
myText.SetBinding(TextBlock.TextProperty, myBinding);