首页 文章

WPF将DataTable行列绑定到文本框

提问于
浏览
1

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 =视图模型)(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);

它可以工作(当数据表在窗口(或视图模型)中作为公共可用时)

我错过了什么 .

UPDATE: HERE IS A FULL POST of the sample code I'm applying here.

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提供的建议

2 回答

  • 0

    我认为你的xaml正在将源设置为字符串“DDT”,当你期望它是当前窗口中的属性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设置DataContext并实现INotifyPropertyChanged) . 如果您注释掉该行,它将无效

    OnPropertyChanged(new PropertyChangedEventArgs("DDT"));
    

    如果从XAML中省略以下内容,则绑定第二个TextBox

    DataContext="{Binding RelativeSource={RelativeSource Self}}"
    

    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>
    
  • 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中正确地反映给用户 .

    我确实希望这可以节省别人头痛和我在这方面经历的研究....

相关问题