在 WPF 中指定数据绑定
Specifying data bindings in WPF
我有一个带有 3 个文本框的简单 WPF 应用程序,其中 2 个文本框输入数字,第三个文本框显示单击另一个按钮时输入的总和。
我有 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
}
}
从技术上讲,这不在 App.xaml
中(这是 WPF 中的一个特殊文件)。
也就是说,是的你可以做到。您可以像这样在代码中设置绑定:
textBox1.Text = new Binding("SomeProperty");
...
好吧,那真的很烦人所以我们就在 XAML:
<TextBox Text="{Binding SomeProperty}"/>
这两段代码做的事情相同,但是当您进入更高级的绑定时,XAML 语法更易于使用。另外,您的文本来自何处更明显,而不必打开两个文件。
FrameworkElement class 和 FrameworkContentElement class 都公开了一个 SetBinding 方法。如果您绑定的元素继承了这两个 class 中的任何一个,您可以直接调用 SetBinding 方法。
以下示例创建一个名为 class 的 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);
您的文本框未更新,因为您尚未在绑定后设置数据源(DataContext
通常)。
当你写
<TextBox Text="{Binding dataModel.Value1}" />
你说的是真的"pull the value for this field from 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 绑定以从中删除 dataModel.
<TextBox Text="{Binding Value1}" />
这会将整个表单的 DataContext 设置为 _data
对象,并且在您的 OnAdd 方法中我们可以更新 _data
对象属性以便更新 UI。
我喜欢写一些关于 WPF 初学者的博客,您可能有兴趣查看其中解释这些概念的几篇文章:
我有一个带有 3 个文本框的简单 WPF 应用程序,其中 2 个文本框输入数字,第三个文本框显示单击另一个按钮时输入的总和。
我有 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
}
}
从技术上讲,这不在 App.xaml
中(这是 WPF 中的一个特殊文件)。
也就是说,是的你可以做到。您可以像这样在代码中设置绑定:
textBox1.Text = new Binding("SomeProperty");
...
好吧,那真的很烦人所以我们就在 XAML:
<TextBox Text="{Binding SomeProperty}"/>
这两段代码做的事情相同,但是当您进入更高级的绑定时,XAML 语法更易于使用。另外,您的文本来自何处更明显,而不必打开两个文件。
FrameworkElement class 和 FrameworkContentElement class 都公开了一个 SetBinding 方法。如果您绑定的元素继承了这两个 class 中的任何一个,您可以直接调用 SetBinding 方法。 以下示例创建一个名为 class 的 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);
您的文本框未更新,因为您尚未在绑定后设置数据源(DataContext
通常)。
当你写
<TextBox Text="{Binding dataModel.Value1}" />
你说的是真的"pull the value for this field from 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 绑定以从中删除 dataModel.
<TextBox Text="{Binding Value1}" />
这会将整个表单的 DataContext 设置为 _data
对象,并且在您的 OnAdd 方法中我们可以更新 _data
对象属性以便更新 UI。
我喜欢写一些关于 WPF 初学者的博客,您可能有兴趣查看其中解释这些概念的几篇文章: