绑定到WPF DataGrid的DataTable不更新
我有一个绑定到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;
}
您需要创建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>
好像我不需要数据表,而只是做这个。 –
我还没有时间来测试这个答案,但我重做我的逻辑,以便我不使用数据表,但会动态设置视图响应ViewModel数据的列(以及可观察到的两个布尔值和数组的集合文本项目)。我不认为这会打破MVVM,但需要一些代码,最重要的是让我摆脱绑定到数据表的不便。 –
我发现一个简单的解决方案,试图解析这个答案让我,但没有实际上每一个完全测试或grook这个答案。但我会接受,因为我认为这让我走上了似乎奏效的道路。为了完整性,我会在下面发布我的解决方案。 –
虽然我尽量避免后面的代码,我认为我在这个解决方案中的使用是可以接受的,因为视图只是准备显示具有动态大小(或形状)的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; }
}
这在我看来是最简单的方法。 。 。我还没有完全测试它,但它似乎工作到目前为止。如果我发现其他东西无法正常工作,将会更新。 。
你似乎有一堆XAML丢失 - 你没有显示你的列。 – slugster
我没有定义列,因为我不知道有多少....这就是为什么我绑定到数据表。这是xaml的程度。它的工作原理除了双向绑定不能返回到数据表。 –