2


絞り込まれたソリューション私ははるかに近づいていますが、XAML を適用してdatacontext値を変更する方法がわかりません。必要に応じて、以下の元の質問のコンテキストを確認してください。

私の問題は、ウィンドウへのデータコンテキストとして ViewModel クラスがあることです。このビュー モデルには、"DataTable" オブジェクトがあります (列とテスト用の 1 行のみ)。テキストボックスの「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 を持っている場合、データ検証 (エラーの赤い境界線) を含め、すべて問題なく動作します。

さて、問題です。これが私のウィンドウクラスのパブリックと同じ「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 があります。まったく新しいフォームであり、これがウィンドウのデフォルトの「グリッド」にある唯一のコントロールです。

Rows[0][Column] を定義するだけで、次のバージョンを試しました

<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 が提供する提案も

4

2 に答える 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をthisに設定した場合(コード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から以下を除外すると、2番目の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>
于 2012-04-29T06:00:01.757 に答える
0

解決済みですが、なんとPITA... MVVMパターンを実行するサンプル内のほとんどのものには、フックしたいものを公開するビューモデルのプロパティがあります。DATATABLE (または同様のビューなど) へのバインドを扱う場合、そのテーブル (またはビュー) の COLUMN にバインドしています。

テーブルがバックエンドからクエリされると、データ列を設定するスキーマは常に列名を強制的に大文字にします。

そのため、テーブルに「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 に適切に反映されてユーザーに表示されます。

これにより、私がこれまでに経験した頭痛と研究が他の誰かを救うことを願っています....

于 2012-05-01T19:13:01.957 に答える