WPF通常不是我的区域,所以我是一个新手,我在弄清楚如何在WPF中实现某些东西时遇到了一些麻烦,这在WinForms中是件小事。我似乎无法找到本论坛中的正确主题或正确的YouTube教程,它引导我找到答案。我有一个问题,让一个简单的DataBinding到WPF TextBox正常工作。我想要实现的行为是对TextBox所做的任何更改都会立即反映在源类DataSet中。这是一个简单的显示/编辑方案,我确信答案非常简单。
这就是我在WinForms中的表现....
表单代码:
public partial class Form1 : Form
{
private DATARECORD CURRENTUSER;
public Form1()
{
InitializeComponent();
CURRENTUSER = new DATARECORD(@"Data Source=C:\Users\rr187718\Documents\Personal\Programming\DynamicBackup\DynamicBackup\bin\Debug\Data\dbData.sdf");
CURRENTUSER.FncBind(CtlCopiesToKeep, "Value", "tblUser.CopiesToKeep");
}
//Test code to display the value in the DataSet
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show(CURRENTUSER.copiesToKeep.ToString());
}
}
课程代码:
public class DATARECORD
{
private string ConnectionString;
private DataSet CurrentRecord;
public int copiesToKeep { get { return Int32.Parse(CurrentRecord.Tables["tblUser"].Rows[0]["CopiesToKeep"].ToString()); } }
public DATARECORD(string connectionString)
{
ConnectionString = connectionString;
CurrentRecord = new DataSet();
SQL SQL = new SQL(2);
DataTable userTable = SQL.fncSelectAsTable(ConnectionString, "tblUser", "USERID=2");
userTable.TableName = "tblUser";
CurrentRecord.Tables.Add(userTable);
userTable.Dispose();
}
public void FncBind(Control c, string type, string field)
{
c.DataBindings.Add(type, CurrentRecord, field, true, DataSourceUpdateMode.OnPropertyChanged);
}
}
然后我在主窗体上只有简单的TextBox,名为" CtlCopiesToKeep"和"测试"按钮。
有没有人知道一个很好的,简单的例子,可以说明如何做到这一点?
非常感谢, 戴夫
编辑:
Hello Noel。非常感谢您花时间解释所有这些。我完全放了它,但绑定似乎有些错误,因为当我更改TextBox中的值时,它不会更新DataSet。这是代码和XAML。如果有人能指出我正确的方向,那将非常感激。
更新的主要代码
public partial class MainWindow : Window
{
public DATARECORD SELECTEDUSER;
private string ConnectionString = @"Data Source=C:\Users\rr187718\Documents\Personal\Programming\DynamicBackup\DynamicBackup\bin\Debug\Data\dbData.sdf";
public MainWindow()
{
InitializeComponent();
SELECTEDUSER = new DATARECORD(ConnectionString);
GrdMain.DataContext = SELECTEDUSER;
}
private void button1_Click(object sender, RoutedEventArgs e)
{
SELECTEDUSER.fncShowVals("BasePath");
}
}
更新的类代码
public class DATARECORD : INotifyPropertyChanged
{
private string ConnectionString;
private DataSet currentRecord = new DataSet();
private string BasePath = null;
public string basePath
{
get
{
return currentRecord.Tables["tblStorage"].Rows[0]["BasePath"].ToString() ;
}
set
{
BasePath = value;
OnPropertyChanged("BasePath");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public DATARECORD(string connectionString)
{
ConnectionString = connectionString;
SQL SQL = new SQL(ConnectionString, SQLVersion.CE);
DataTable storageTable = SQL.fncSelectAsTable(ConnectionString, "tblStorage", "USERID=2");
storageTable.TableName = "tblStorage";
currentRecord.Tables.Add(storageTable);
storageTable.Dispose();
}
public void fncShowVals(string test)
{
MessageBox.Show(currentRecord.Tables["tblStorage"].Rows[0][test].ToString());
}
protected void OnPropertyChanged(string value)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(value));
}
}
}
TextBox的XAML
<Window x:Class="WpfBind.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 Name="GrdMain">
<TextBox Text="{Binding basePath, Mode=TwoWay, UpdateSourceTrigger =PropertyChanged}" Height="23" HorizontalAlignment="Left" Margin="124,70,0,0" Name="CtlBaseFolder" VerticalAlignment="Top" Width="120" />
<Label Content="BaseFolder" Height="28" HorizontalAlignment="Left" Margin="41,69,0,0" Name="label2" VerticalAlignment="Top" />
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="263,142,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
</Grid>
更新02/04/2015
我现在有这个,但我不明白它是如何引用DataSet的?此代码生成一个空白文本框,如果更改了值,则不会更新DataSet:
`private string ___basePath = null;
protected string _basePath
{
get
{
return ___basePath;
}
set
{
___basePath = value;
OnPropertyChanged("basePath");
}
}
public string basePath
{ //<- Bind to this property
get
{
return ___basePath;
}
set
{
_basePath = value;
}
}`
基础DataSet值存储在此处:
currentRecord.Tables["tblStorage"].Rows[0]["BasePath"].ToString();
非常感谢Dave。
更新 - 2015年4月2日 - 2
Hello Noel,我已经应用了你的代码,但遗憾的是它还没有工作(如果点击&#34; test&#34;按钮,DataSet不能反映TextBox中的变化)。这是整个代码。顺便说一句,我非常感谢你的时间,非常感谢!
public partial class MainWindow : Window
{
private string ConnectionString = @"Data Source=C:\Users\rr187718\Documents\Personal\Programming\DynamicBackup\DynamicBackup\bin\Debug\Data\dbData.sdf";
private readonly DATARECORD _data = null;
public DATARECORD Data
{
get
{
return _data;
}
}
public MainWindow()
{
InitializeComponent();
_data = new DATARECORD(ConnectionString);
DataContext = Data; //All controls connected to this class will now look for their value in 'Data' (DataContext inherits and must be a property because you can only bind to properties)
}
private void button1_Click(object sender, RoutedEventArgs e)
{
Data.fncShowVals("BasePath");
}
}
public class DATARECORD : INotifyPropertyChanged
{
private string ConnectionString;
private DataSet currentRecord = new DataSet();
private string ___basePath = null;
private string _basePath
{
get
{
if (___basePath == null)
{
//We only access the currentRecord if we did not yet stored the value
// otherwise it would read the currentRecord every time you type a char
// in the textbox.
// Also: Pay attention to multiple possible NullReferenceExceptions and IndexOutOfBoundsExceptions
___basePath = currentRecord.Tables["tblStorage"].Rows[0]["BasePath"].ToString();
}
return (___basePath == String.Empty) ? null : ___basePath;
}
set
{
___basePath = (value == null) ? String.Empty : value;
NotifyPropertyChanged("BasePath");
}
}
protected void PushBasePathToDataBase()
{
//Save the value of ___basePath to the database
}
public string BasePath
{ //The Binding recieves/sets the Data from/to this property
get
{
return _basePath;
}
set
{
_basePath = value;
}
}
public event PropertyChangedEventHandler PropertyChanged;
public DATARECORD(string connectionString)
{
ConnectionString = connectionString;
SQL SQL = new SQL(ConnectionString, SQLVersion.CE);
DataTable storageTable = SQL.fncSelectAsTable(ConnectionString, "tblStorage", "USERID=2");
storageTable.TableName = "tblStorage";
currentRecord.Tables.Add(storageTable);
storageTable.Dispose();
___basePath = currentRecord.Tables["tblStorage"].Rows[0]["BasePath"].ToString();
}
public void fncShowVals(string test)
{
MessageBox.Show(currentRecord.Tables["tblStorage"].Rows[0][test].ToString());
}
protected void NotifyPropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
答案 0 :(得分:0)
很高兴您使用绑定来分离视觉效果中的数据。因为在winforms中真的不可能。为了使绑定工作,您必须执行以下操作:
textBox必须将其DataContext设置为包含绑定值的类的实例。 DataContext = MyDataInstance;
您可以在文本框本身或任何父级上设置它。
要绑定的值和DataContext 必须是公共属性。 F.e:
private string _name = null;
public string Name{
get{
return _name;
}
set{
_name = value;
NotifyPropertyChanged("Name");
}
}
数据类必须实现INotifyPropertyChanged
如果全部设置完毕,您可以在xaml中编写文本框:
<TextBlock Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
此Binding绑定到DataContext中指定的实例的Name属性。它可以从属性中检索值,并可以将数据写入其中。
NotifyPropertyChanged("Name");
Mode
设置为TwoWay
而UpdateSourceTrigger
设置为PropertyChanged
) 编辑(关于您的其他内容)
我注意到您想要通知名为"BasePath"
的私人字段。
您必须通知财产"basePath"
而不是其背后的字段。
这就是我推荐严格命名约定的原因。
我将私人和受保护的字段命名为_privateOrProtected
(1下划线)
我命名通过绑定属性(如___someData
(3个下划线)和绑定属性(如SomeData
)访问的私有或受保护字段。原因是,您通常不希望直接设置私有字段,除了绑定属性的setter。直接设置它不会调用NotifyPropertyChanged();
,这显然不是你想要的几乎在所有情况下。如果你在整个应用程序中保留3个下划线 - 每个熟悉绑定的人都应该很快理解其含义。
对于更复杂的数据,您可能具有访问私有/受保护属性访问私有字段的绑定属性。我会这样解决:SomeData
,_someData
,___someData
。您只需要明确可以设置属性或字段以更新绑定,否则有人可能会更改___someData
的值并想知道为什么绑定没有更新。
由于这是每个WPF应用程序中非常重要的一点,我真的希望您理解它。以下是上述内容的示例:
private bool ___thisIsAwesome = true;
protected bool _thisIsAwesome{
get{
return ___thisIsAwesome;
}
set{
___thisIsAwesome = value;
NotifyPropertyChanged("ThisIsAwesome");
}
}
public bool ThisIsAwesome{ //<- Bind to this property
get{
return ___thisIsAwesome;
}
/*set{
_thisIsAwesome = value;
} NOTE: The setter is not accessable from outside of this class
because nobody can tell me that this is not awesome - it just is.
However I still want to be able to set the property correctly
from within my class (in case I change my mind), that is why I
added the protected property.
If you omit a getter/setter like this one make sure your
<br>Binding Mode</b> does not try to access the omited accessors.
Also check the output window too find possible binding errors
which never throw exceptions.
*/
}
在此代码中,您现在应该认识到设置ThisIsAwesome
和_thisIsAwesome
都会更新绑定。但要注意设置___thisIsAwesome
,因为它不会更新Binding。 ThisIsAwesome
的setter目前无法使用(无论什么原因),这就是我添加受保护属性的原因。你明白我想用它实现什么目标吗?
EDIT2(因为您的代码仍无效)
public partial class MainWindow : Window {
private readonly MyData _data = null;
public MyData Data{
get{
return _data;
}
}
public MainWindow() {
_data = new MyData();
DataContext = Data; //All controls connected to this class will now look for their value in 'Data' (DataContext inherits and must be a property because you can only bind to properties)
}
}
public class MyData : INotifyPropertyChanged {
private string ___basePath = null;
private string _basePath {
get {
if (___basePath == null) {
//We only access the currentRecord if we did not yet stored the value
// otherwise it would read the currentRecord every time you type a char
// in the textbox.
// Also: Pay attention to multiple possible NullReferenceExceptions and IndexOutOfBoundsExceptions
___basePath = currentRecord.Tables["tblStorage"].Rows[0]["BasePath"].ToString();
}
return (___basePath == String.Empty) ? null : ___basePath;
}
set {
___basePath = (value == null) ? String.Empty : value;
NotifyPropertyChanged("BasePath");
}
}
protected void PushBasePathToDataBase() {
//Save the value of ___basePath to the database
}
public string BasePath{ //The Binding recieves/sets the Data from/to this property
get{
return _basePath;
}
set{
_basePath = value;
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string PropertyName){
if(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
#endregion INotifyPropertyChanged
}
最后是MainWindow的xaml中的文本框:
<TextBlock Text="{Binding BasePath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>