2016-11-24 2 views
0

У меня есть DataGrid WPF, который я привязываю к DataTable. Мне не нравится это делать, но данные поступают из текстового файла с разделителями, и я не знаю, сколько полей (столбцов) будет содержать эта таблица. Программно это, по-видимому, самый простой способ выполнить это (используя MVVM и избегая кода позади), но учитывая, что я хочу двухстороннюю привязку, возможно, это не сработает.DataTable привязан к WPF DataGrid не обновляется

DataGrid определяется как это в представлении:

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

ViewModel устанавливает DataTable путем чтения текстового файла, и добавляет два логических значения в конце каждой строки. Я хочу, чтобы логические значения отображались в флажках в DataGrid, но они этого не делают, и я не получаю никаких событий в модели viewmodel при изменении значений. Я думаю, что мне нужно изменить данные, как видно из других связанных вопросов, но все они реагируют на viewmodel, изменяя представление (например, кнопку, которая добавляет столбец), вместо того, чтобы иметь изменения из набора данных в пределах Посмотреть.

Для контекста здесь является членом FileTable в моем ViewModel:

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

А вот код, который создает DataTable из текстового файла:

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; 
      } 
     } 
    } 

ViewModel собственности следующим

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

и создать DataTable, привязаться к нему, как следует,

//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

У меня не было времени, чтобы протестировать этот ответ, но переделаю свою логику, чтобы я не использовал datatable, но динамически настраивал столбцы в представлении, отвечающие на данные ViewModel (и наблюдаемый набор из двух логических элементов и массива текстовых элементов). Я не думаю, что это нарушит MVVM, но потребует немного кода и, что самое важное, заставит меня преодолеть неудобства привязки к данным. –

+0

Я нашел простое решение, что попытка разобрать этот ответ привела меня, но без всякого полного тестирования или ответа на этот ответ. Но я соглашусь, потому что, по-моему, это привело меня к следующему пути. Я приложу свое решение ниже для полноты. –

0

В то время как я стараюсь избегать кода позади, я думаю, что мое использование в этом решении приемлемо, поскольку представление просто готовит для отображения ViewModel, который имеет динамический размер (или форму).

Вместо того, чтобы читать DataTable, я читаю объект, содержащий список строк и два булева. В моей модели ViewModel у меня есть наблюдаемая коллекция этого объекта. DataGrid инициализируется в коде позади, как это (просто список строк изображенном два Флажок колонки не требуют цикла):

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; } 
} 

Это кажется мне самым простым методом. , , Я не тестировал его полностью, но, похоже, он работает до сих пор. Будет обновлен, если я найду что-то еще не работающее. , ,

Смежные вопросы