C# – WPF Binding DataTable column to a textbox

cdata-bindingdatatabletextboxwpf

I'm getting started with WPF and finding it difficult to get even the most simple binding working. Here's some givens…

I have an object that queries an existing database and returns a "datatable" object, the data comes back (and for test purposes, only a single row and single column called "MyTextColumn")

The data table is not "Strongly typed" as I've read in other places trying to force the issue of strongly typed objects. I want to understand the underlying mechanisms from the code-behind perspective, AND not from the XAML perspective. From reading, apparently you can't bind directly to a data table, but you can to the "DefaultView" of a DataTable object (makes no sense to me since they point to same record (or set of records, with exception of say a filter of some type).

So, in the XAML portion of the window,

<src:MyWindow blah, blah >
  <Grid Name="grdTesting">
    <Grid.RowDefinitions>
      <RowDefinition />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition />
      <ColumnDefinition />
    </Grid.ColumnDefinitions>

    <Label Name="lblMyTextColumn" 
        Content="Just the label" 
        Grid.Row="0" Grid.Column="0 "/>

    <TextBox Name="txtMyTextColumn"
        Grid.Row="0" Grid.Column="1"
        Width="120" />
  </Grid>
</src:MyWindow>

So now, I'm in the code-behind, what I've read is you have to have a BindingListCollectionView oView;

public partial class MyWindow : Window
{
  BindingListCollectionView oView;
  MyQueryingManager oTestMgr;

  public MyWindow()
  {
    InitializeComponent();

    oTestMgr = new MyQueryingManager();
    DataTable oResults = oTestMgr.GetTheData();

    // Data context for the form bound to the Table retrieved
    oView = new BindingListCollectionView( oResults.DefaultView );

    // One place said the Window should get the binding context
    DataContext = oView;

    // another indicated the grid...  Just need to know which I SHOULD be using
    grdTesting.DataContext = oView;


    // Now, for my binding preparation...
    Binding bindMyColumn = new Binding();
    bindMyColumn.Source = oView;
    bindMyColumn.Path = new PropertyPath("MyTextColumn");
    txtMyTextColumn.SetBinding( TextBox.TextProperty, bindMyColumn );
  }
}

So… what am I missing here… Should be simple, nothing complex, I have a data table, with a record, that has a value. Run the form (no matter binding context to the Window or the Grid), and the record value does not show in the textbox control. Once I understand the behavior on a single textbox, I can go on with all the other elements (validation, input mask, formatting, etc), but am stuck right at the gate on this one.

Thanks

Best Answer

First, you can bind to a DataTable but you can also use the default view (which is a DataView) etc.

Usually, you bind a DataTable to an ItemsControl or a control that derives from it, such as ListBox, DataGrid etc. Then each container will get a DataRow (or DataRowView) and the binding will be easy.

Since you are binding it directly to a TextBox inside a Grid you would have to specify both Row and Column in the binding. The correct path to bind to the column named "MyTextColumn" in the first row is Rows[0][MyTextColumn]

Try this

Binding bindMyColumn = new Binding();
bindMyColumn.Source = oResults;
bindMyColumn.Path = new PropertyPath("Rows[0][MyTextColumn]");
txtMyTextColumn.SetBinding(TextBox.TextProperty, bindMyColumn);

A problem if you're binding directly to the DataTable is that it doesn't implement INotifyPropertyChanged so the UI won't know that the value has changed if it is changed from some other source. In this case, you can use a DataView instead. The binding syntax will be a little different here since you access the DataRowViews directly with the index operator.

dataView = oResults.DefaultView;
// Now, for my binding preparation...
Binding bindMyColumn = new Binding();
bindMyColumn.Source = dataView;
bindMyColumn.Path = new PropertyPath("[0][MyTextColumn]");
txtMyTextColumn.SetBinding(TextBox.TextProperty, bindMyColumn);