NARROWED DOWN SOLUTION 我比较接近,但不知道如何应用XAML来改变datacontext值。请根据需要查看以下原始问题的背景信息。
我的问题是我有一个ViewModel类作为窗口的datacontext。在这个视图模型中,我有一个“DataTable”对象(带有列,只有一行用于测试)。当我尝试将文本框“TEXT”绑定到数据表的列时,它不起作用。我最终发现的是,无论我给它什么“来源”或“路径”,它都不会合作。但是,只是通过玩弄场景,我说它很糟糕。我们看看吧。 Textbox控件具有自己的“DataContext”属性。所以,在代码中,我只是强制使用textbox.DataContext =“MyViewModel.MyDataTableObject”并将路径留给它应该代表“MyDataColumn”的列,并且它有效。
那就是说,如何为文本框控件编写XAML,以便将“DataContext”属性设置为窗口视图模型的数据表对象的属性,但无法正确显示。例如:
<TextBox Name="myTextBox"
Width="120"
DataContext="THIS IS WHAT I NEED" --- to represent
Text="{Binding Path=DataName,
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged }" />
此文本框的DataContext应反映下面的XAML详细信息并获取
(ActualWindow)(DDT = View Model)(oPerson =视图模型上存在的DataTable) CurrentWindow.DDT.oPerson
我坚持使用绑定的东西。我想将数据表的列绑定到文本框控件。听起来很简单,但我错过了一些东西。简单的场景首先。如果我有我的窗口并将数据上下文设置为“MyDataTable”,并且文本框PATH = MyDataColumn,则一切正常,没有问题,包括数据验证(错误上的红色边框)。
现在,问题。如果我直接在我的Window类上有一个与公共相同的“MyDataTable”(但是如果我在实际的ViewModel对象上使用它,但是用于简化级别引用的窗口),我无法使用它直接XAML源。我知道我必须设置“SOURCE = MyDataTable”,但是只有列的路径不起作用。
<TextBox Name="myTextBox"
Text="{Binding Source=DDT, Path=Rows[0][DataName],
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged }" />
但是,从其他测试中,如果我将路径(在代码隐藏中)设置为
object txt = FindName("myTextBox");
Binding oBind = new Binding("DataName");
oBind.Source = DDT;
oBind.Mode = BindingMode.TwoWay;
oBind.ValidatesOnDataErrors = true;
oBind.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
((TextBox)txt).SetBinding(TextBox.TextProperty, oBind);
它可以工作(当数据表在窗口(或视图模型)中作为公共可用时)
我错过了什么。
更新:这里是我在这里申请的示例代码的完整帖子。
using System.ComponentModel;
using System.Data;
namespace WPFSample1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public DerivedDataTable DDT;
public MainWindow()
{
InitializeComponent();
// hook up to a Data Table
DDT = new DerivedDataTable();
DataContext = this;
// with THIS part enabled, the binding works.
// DISABLE this IF test, and binding does NOT.
// but also note, I tried these same settings manually via XAML.
object txt = FindName("myTextBox");
if( txt is TextBox)
{
Binding oBind = new Binding("DataName");
oBind.Source = DDT;
oBind.Mode = BindingMode.TwoWay;
oBind.ValidatesOnDataErrors = true;
oBind.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
((TextBox)txt).SetBinding(TextBox.TextProperty, oBind);
}
}
}
// Generic class with hooks to enable error trapping at the data table
// level via ColumnChanged event vs IDataErrorInfo of individual properties
public class MyDataTable : DataTable
{
public MyDataTable()
{
// hook to column changing
ColumnChanged += MyDataColumnChanged;
}
protected void MyDataColumnChanged(object sender, DataColumnChangeEventArgs e)
{ ValidationTest( e.Row, e.Column.ColumnName); }
// For any derived datatable to just need to define the validation method
protected virtual string ValidationTest(DataRow oDR, string ColumnName)
{ return ""; }
}
public class DerivedDataTable : MyDataTable
{
public DerivedDataTable()
{
// simple data table, one column, one row and defaulting the value to "X"
// so when the window starts, I KNOW its properly bound when the form shows
// "X" initial value when form starts
Columns.Add( new DataColumn("DataName", typeof(System.String)) );
Columns["DataName"].DefaultValue = "X";
// Add a new row to the table
Rows.Add(NewRow());
}
protected override string ValidationTest(DataRow oDR, string ColumnName)
{
string error = "";
switch (ColumnName.ToLower())
{
case "dataname" :
if ( string.IsNullOrEmpty(oDR[ColumnName].ToString() )
|| oDR[ColumnName].ToString().Length < 4 )
error = "Name Minimum 4 characters";
break;
}
// the datarow "SetColumnError" is what hooks the "HasErrors" validation
// in similar fashion as IDataErrorInfo.
oDR.SetColumnError(Columns[ColumnName], error);
return error;
}
}
}
这是XAML。任何全新的表单,这是窗口默认“网格”中的唯一控件。
尝试以下版本,只定义行[0] [列]
<TextBox Name="myTextBox"
Width="120"
Text="{Binding Path=Rows[0][DataName],
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged }" />
包括“DDT”的来源,因为它是公开的窗口
<TextBox Name="myTextBox"
Width="120"
Text="{Binding Source=DDT, Path=Rows[0][DataName],
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged }" />
甚至是grantnz提供的建议
答案 0 :(得分:0)
我认为你的xaml将源设置为字符串&#34; DDT&#34;当你期望它在当前窗口中成为DDT属性时。
您是否在Visual Studio的输出窗口中看到错误,如:
System.Windows.Data Error: 40 : BindingExpression path error:
'Rows' property not found on 'object' ''String' (HashCode=1130459074)'.
BindingExpression:Path=Rows[0][DataName]; DataItem='String' (HashCode=1130459074);
target element is 'TextBox' (Name=''); target property is 'Text' (type 'String')
如果将窗口DataContext设置为此(从代码DataContext = this;或xaml),您可以使用:
Text="{Binding Path=DDT.Rows[0][DataName],
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged }" />
或者您可以将DataContext保留为null并使用:
<TextBox Name="myTextBox"
Text="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}},Path=DDT.Rows[0][DataName],
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged }" />
以上假设您在设置绑定之前设置了DDT属性。如果在配置绑定后设置了DDT,则需要实现INotifyPropertyChanged。
这里是工作版本的来源(实现了从XAML和INotifyPropertyChanged设置的DataContext)。如果你注释掉这行
,它就不起作用OnPropertyChanged(new PropertyChangedEventArgs("DDT"));
如果您忽略XAML中的以下内容,则绑定第二个TextBox
DataContext="{Binding RelativeSource={RelativeSource Self}}"
CODE
public partial class MainWindow : Window, INotifyPropertyChanged
{
public DataTable DDT { get; set; }
public String SP { get; set; }
public MainWindow()
{
InitializeComponent();
DDT = new DerivedDataTable();
OnPropertyChanged(new PropertyChangedEventArgs("DDT"));
SP = "String prop";
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
PropertyChanged(this, e);
}
}
XAML
<Window x:Class="BindingTest.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"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<StackPanel>
<TextBox
Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},Path=DDT.Rows[0][DataName],
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged }" />
<TextBox
Text="{Binding Path=DDT.Rows[0][DataName],
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged }" />
<TextBox
Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},Path=SP}" />
</StackPanel>
</Window>
答案 1 :(得分:0)
已解决,但是什么是PITA ......执行MVVM模式的示例中的大多数内容都将在视图模型上具有属性,从而暴露出您想要挂接的内容。处理绑定到DATATABLE(或类似视图等)时,您绑定到所述表(或视图)的COLUMN。
当从任何后端查询表时,填充数据列的模式将始终强制列名称为UPPER CASE。
因此,如果表中有一列“InvoiceTotal”,则在查询时,列名称将为“INVOICETOTAL”。
如果您尝试绑定到
Path="InvoiceTotal" ... it will fail
Path="INVOICETOTAL" ... it WILL WORK
但是,如果您直接在.Net(我使用C#)工作,以下将同时从行中获取值
double SomeValue = (double)MyTable.Rows[0]["InvoiceTotal"];
or
double SomeValue = (double)MyTable.Rows[0]["INVOICETotal"];
or
double SomeValue = (double)MyTable.Rows[0]["invoicetotal"];
所有,无论列名称是否区分大小写。
因此,现在其余的绑定,表,行或列级别可用的错误触发器可以在GUI中正确地反映给用户。
我确定希望这可以拯救别人在这方面经历的头痛和研究......