2016-11-24 215 views
0

我有一个绑定到DataTable的WPF DataGrid。我不喜欢这样做,但数据来自分隔文本文件,我不知道表中包含多少个字段(列)。以编程方式,这似乎是实现此目的的最简单方法(使用MVVM和避免代码背后),但鉴于我想要双向绑定,也许这不会工作。绑定到WPF DataGrid的DataTable不更新

数据网格定义像这样的观点:

 <DataGrid x:Name="dataGrid" ItemsSource="{Binding FileTable, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
       HorizontalAlignment="Stretch" Margin="0,60,0,0" VerticalAlignment="Stretch"> 
    </DataGrid> 

的视图模型通过读取文本文件建立的数据表,并将两个布尔值到每一行的末尾。我想要布尔值映射到DataGrid中的复选框,但它们不这样做,并且在更改值时,我没有在视图模型中获取任何事件。我想我需要更换数据表,如其他相关问题所见,但它们都是响应改变视图的视图模型(如添加一列的按钮),而不是从数据网格视图。

对于上下文这里是的FileTable成员在我的视图模型:

private DataTable _fileTable; 
public DataTable FileTable 
{ 
    get 
    { 
     return _fileTable; 
    } 
    set 
    { 
     if (value != _fileTable) 
     { 
      _fileTable = value; 
      NotifyPropertyChanged("FileTable"); 
     } 
    } 
} 

这里是从文本文件创建数据表的代码:

public DataTable ParseFileToTable(Document doc, string PlaceHolders) 
{ 
    if (dt == null) 
    { 
     dt = new DataTable(); 
    } 
    else dt.Clear(); 

    if (filepath == null) 
    { 
     OpenFileDialog dlg = new OpenFileDialog(); 
     dlg.DefaultExt = ".txt"; // Default file extension 
     dlg.Filter = "Text documents (.txt)|*.txt"; // Filter files by extension 

     Nullable<bool> result = dlg.ShowDialog(); 
     if (result != true) return null; 

     filepath = dlg.FileName; 
     StreamReader r = new StreamReader(filepath); 
     string line = r.ReadLine(); // First Line is Column Names 
     string[] h_line = line.Split('\t'); // tab delimeter is hardcoded for now 
     for(int i = 0; i < h_line.Count(); i++) 
     { 
      dt.Columns.Add(h_line[i]); 
     } 
     dt.Columns.Add(new DataColumn("Exists", typeof(bool))); 
     dt.Columns.Add(new DataColumn("Placeholder", typeof(bool))); 


     //read the rest of the file 
     while (!r.EndOfStream) 
     { 
      line = r.ReadLine(); 
      string [] a_line = line.Split('\t'); 
      DataRow nRow = dt.NewRow(); 
      for(int i = 0; i < h_line.Count(); i++) 
      { 
       nRow[h_line[i]] = a_line[i]; 
      } 
      nRow["Exists"] = DoesSheetExist(doc, h_line[0], a_line[0]); 
      nRow["Placeholder"] = IsAPlaceholder(a_line[0], PlaceHolders); 
      dt.Rows.Add(nRow); 
     } 
    } 
    return dt; 
} 
+0

你似乎有一堆XAML丢失 - 你没有显示你的列。 – slugster

+0

我没有定义列,因为我不知道有多少....这就是为什么我绑定到数据表。这是xaml的程度。它的工作原理除了双向绑定不能返回到数据表。 –

回答

0

您需要创建DatagridColumns动态使用行为

/// <summary> 
    /// Creating dymanic columns to the datagrid 
    /// </summary> 
    public class ColumnsBindingBehaviour : Behavior<DataGrid> 
    { 
     public ObservableCollection<DataGridColumn> Columns 
     { 
      get { return (ObservableCollection<DataGridColumn>)base.GetValue(ColumnsProperty); } 
      set { base.SetValue(ColumnsProperty, value); } 
     } 
     public static readonly DependencyProperty ColumnsProperty = DependencyProperty.Register("Columns", 
      typeof(ObservableCollection<DataGridColumn>), typeof(ColumnsBindingBehaviour), 
       new PropertyMetadata(OnDataGridColumnsPropertyChanged)); 
     private static void OnDataGridColumnsPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e) 
     { 
      var context = source as ColumnsBindingBehaviour; 
      var oldItems = e.OldValue as ObservableCollection<DataGridColumn>; 
      if (oldItems != null) 
      { 
       foreach (var one in oldItems) 
        context._datagridColumns.Remove(one); 
       oldItems.CollectionChanged -= context.collectionChanged; 
      } 
      var newItems = e.NewValue as ObservableCollection<DataGridColumn>; 
      if (newItems != null) 
      { 
       foreach (var one in newItems) 
        context._datagridColumns.Add(one); 
       newItems.CollectionChanged += context.collectionChanged; 
      } 
     } 
     private ObservableCollection<DataGridColumn> _datagridColumns = new ObservableCollection<DataGridColumn>(); 
     protected override void OnAttached() 
     { 
      base.OnAttached(); 
      this._datagridColumns = AssociatedObject.Columns; 
     } 
     private void collectionChanged(object sender, NotifyCollectionChangedEventArgs e) 
     { 
      switch (e.Action) 
      { 
       case NotifyCollectionChangedAction.Add: 
        if (e.NewItems != null) 
         foreach (DataGridColumn one in e.NewItems) 
          _datagridColumns.Add(one); 
        break; 
       case NotifyCollectionChangedAction.Remove: 
        if (e.OldItems != null) 
         foreach (DataGridColumn one in e.OldItems) 
          _datagridColumns.Remove(one); 
        break; 
       case NotifyCollectionChangedAction.Move: 
        _datagridColumns.Move(e.OldStartingIndex, e.NewStartingIndex); 
        break; 
       case NotifyCollectionChangedAction.Reset: 
        _datagridColumns.Clear(); 
        if (e.NewItems != null) 
         foreach (DataGridColumn one in e.NewItems) 
          _datagridColumns.Add(one); 
        break; 
      } 
     } 
    } 

视图模型属性如下

 //Datagrid Column collection in Viewmodel 
     private ObservableCollection<DataGridColumn> dataGridColumns; 
     public ObservableCollection<DataGridColumn> DataGridColumns 
     { 
      get 
      { 
       return dataGridColumns; 
      } 
      set 
      { 
       dataGridColumns = value; 
       OnPropertyChanged(); 
      } 
     } 

,并创建数据表,绑定到它,如下所示,

//Getting column names from datatable 
    string[] columnNames = (from dc in dt.Columns.Cast<DataColumn>() select dc.ColumnName).ToArray(); 

    //Add two of your columns 
    dt.Columns.Add(new DataColumn("Exists", typeof(bool))); 
    dt.Columns.Add(new DataColumn("Placeholder", typeof(bool))); 


    //Create DataGrid Column and bind datatable fields 
    foreach (string item in columnNames) 
    { 
         if (item.Equals("your Normal Column")) 
         { 
          DataGridColumns.Add(new DataGridTextColumn() { Header = "Normal Column", Binding = new Binding("Normal Column Name"), Visibility = Visibility.Visible}); 
         } 

         else if (!item.Contains("your Bool column")) 
         { 
          //Creating checkbox control 

          FrameworkElementFactory checkBox = new FrameworkElementFactory(typeof(CheckBox)); 
         checkBox.SetValue(CheckBox.HorizontalAlignmentProperty, HorizontalAlignment.Center); 

          checkBox.Name = "Dynamic name of your check box"; 
          //Creating binding 
          Binding PermissionID = new Binding(item); 

          PermissionID.Mode = BindingMode.TwoWay; 

          PermissionID.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; 


          checkBox.SetBinding(CheckBox.TagProperty, BindingVal); 

          DataTemplate d = new DataTemplate(); 
          d.VisualTree = checkBox; 
          DataGridTemplateColumn dgTemplate = new DataGridTemplateColumn(); 
          dgTemplate.Header = item; 
          dgTemplate.CellTemplate = d; 
          DataGridColumns.Add(dgTemplate); 
         } 

        } 

最后 命名空间在XAML

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
xmlns:vm="clr-namespace:YourProject.ViewModels" 
<DataGrid AutoGenerateColumns="False" 
         ItemsSource="{Binding Datatable, 
UpdateSourceTrigger=PropertyChanged, Mode=TwoWay,IsAsync=True}"> 
       <i:Interaction.Behaviors> 
        <vm:ColumnsBindingBehaviour Columns="{Binding DataContext.DataGridColumns, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource AncestorType=DataGrid}}" /> 
       </i:Interaction.Behaviors> 
      </DataGrid> 
+0

好像我不需要数据表,而只是做这个。 –

+0

我还没有时间来测试这个答案,但我重做我的逻辑,以便我不使用数据表,但会动态设置视图响应ViewModel数据的列(以及可观察到的两个布尔值和数组的集合文本项目)。我不认为这会打破MVVM,但需要一些代码,最重要的是让我摆脱绑定到数据表的不便。 –

+0

我发现一个简单的解决方案,试图解析这个答案让我,但没有实际上每一个完全测试或grook这个答案。但我会接受,因为我认为这让我走上了似乎奏效的道路。为了完整性,我会在下面发布我的解决方案。 –

0

虽然我尽量避免后面的代码,我认为我在这个解决方案中的使用是可以接受的,因为视图只是准备显示具有动态大小(或形状)的ViewModel。

我读到一个包含字符串和两个布尔值列表的对象,而不是读取DataTable。在我的ViewModel中,我有一个可观察的这个对象的集合。该数据网格在后面的代码这样的初始化(只显示的字符串列表,这两个复选框列不要求循环):

public MainWindow(FileParametersViewModel vm) 
    { 
     InitializeComponent(); 
     DataContext = vm; 
     dataGrid.ItemsSource = vm.lParams; 
     for (int i = 0; i < vm.ParamNames.Count(); i++) 
     { 
      DataGridTextColumn col = new DataGridTextColumn(); 
      col.Header = vm.ParamNames[i]; 
      string path = String.Format("pArray[{0}]", i); 
      col.Binding = new Binding(path); 
      dataGrid.Columns.Add(col); 
     } 
    } 

我的收藏对象:

public class FileSheetParameters 
{ 
    public FileSheetParameters() 
    { 
     SheetExists = false; 
     IsPlaceholder = false; 
     pArray = new List<string>(); 
    } 

    public bool SheetExists { get; set; } 
    public bool IsPlaceholder { get; set; } 
    public List<string> pArray { get; set; } 
} 

这在我看来是最简单的方法。 。 。我还没有完全测试它,但它似乎工作到目前为止。如果我发现其他东西无法正常工作,将会更新。 。