我有一个绑定到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中的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 :(得分: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();
}
}
创建数据表,按如下方式绑定到它,
//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>