[英]DataTable bound to WPF DataGrid does not update
我有绑定到DataTable的WPF DataGrid。 我不喜欢这样做,但是数据来自定界的文本文件,并且我不知道该表将包含多少个字段(列)。从编程上讲,这似乎是完成此操作的最简单方法(使用MVVM并避免后面的代码),但是鉴于我想要两种方式的绑定,也许这行不通。
在视图中,DataGrid的定义如下:
<DataGrid x:Name="dataGrid" ItemsSource="{Binding FileTable, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Stretch" Margin="0,60,0,0" VerticalAlignment="Stretch">
</DataGrid>
ViewModel通过读取文本文件来设置数据表,并在每行的末尾添加两个布尔值。 我希望布尔值映射到DataGrid中的复选框,但它们不这样做,并且当更改值时,在ViewModel中没有任何事件。 我认为我需要更改数据表,如在其他相关问题中所见,但是它们都响应于更改视图的viewmodel(例如添加列的按钮),而不是使更改来自于内部的datagrid。视图。
对于上下文,这是我的ViewModel中的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;
}
您需要使用行为动态创建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();
}
}
并创建数据表,绑定如下,
//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>
虽然我尽量避免使用代码,但我认为我在此解决方案中的使用是可以接受的,因为视图只是准备显示具有动态大小(或形状)的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; }
}
在我看来,这是最简单的方法。 。 。 我尚未对其进行全面测试,但到目前为止它似乎可以正常工作。 如果发现其他问题将更新。 。 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.