I've been working on this little program, that needs to take file inputs of CSV files, store them in a listbox, and then update automatically the datagrid. When there is added more files, the datagrid needs to expand with the new file data and add next to it.
In DataGridViewModel is where my csv merge code is:
public class DataGridViewModel
{
static public DataGridModel _dataGridModel = new DataGridModel();
public static void ReturnDataTableForGridView()
{
DataTable mainTable = new DataTable();
//-- #3 Test merge
foreach (var item in SidePanelViewModel.GetPathFileList())
{
DataTable dataTable = new DataTable();
try
{
string[] Lines = File.ReadAllLines(item.Filepath);
string[] Fields;
Fields = Lines[0].Split(new char[] { ';' });
int Cols = Fields.GetLength(0);
//1st row skal være kolonne navn;
for (int X = 0; X < Cols; X++)
dataTable.Columns.Add(Fields[X].ToLower(), typeof(string));
DataRow Row;
for (int T = 1; T < Lines.GetLength(0); T++)
{
Fields = Lines[T].Split(new char[] { ';' });
Row = dataTable.NewRow();
for (int f = 0; f < Cols; f++)
Row[f] = Fields[f];
dataTable.Rows.Add(Row);
}
//-- Merges every files(tables) into one.
mainTable.Merge(dataTable);
}
catch (Exception)
{
return null;
}
}
//-- Sets the datatablemerger which raises the propertychanged
_dataGridModel.DatatableMerger = mainTable;
}
}
The DataGridModel class
public class DataGridModel : INotifyPropertyChanged
{
DataTable _dataTableMerger { get; set; } = new DataTable();
public DataTable DatatableMerger
{
get
{
return _dataTableMerger;
}
set
{
_dataTableMerger = value;
OnPropertychanged("DatatableMerger");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertychanged([CallerMemberName] string caller = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(caller));
}
}
The dataGridview class
public DataGridView()
{
InitializeComponent();
}
The XAML code:
<DataGrid x:Name="MainDataGrid" Grid.Row="1" VerticalAlignment="Stretch" Height="auto" ItemsSource="{Binding Path=DatatableMerger, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}" >
</DataGrid>
</Grid>
<UserControl.DataContext>
<Model:DataGridModel/>
</UserControl.DataContext>
Currently output:
Wished output:
My friend of mine helped me out with an example.
Following ansvar:
Your DataContext in the XAML is wrong. Your view must cummunicate with your ViewModel and not your Model.
If you run MVVM, then it's wrong to use button click events in your Views codebehind. It must be done in the ViewModel.
Besides this, your CSV parser is wrong implementet. It will break as soon as a field contains semikolon, new lines, or if komma is used as a fieldseparator instead of semikolon.
Read more about etc. RFC specifikation about CSV files HERE or use a libary that can parse the files correctly. ex. ExcelDataReader ANd if you want it into your DataTable; ExcelDataReader.DataSet
Regarding an example of functional and correct binding follow below examble:
MainWindow.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
The XAML
<Window.DataContext>
<local:DataGridViewModel/>
</Window.DataContext>
<Grid>
<DataGrid Margin="0,0,0,35" ItemsSource="{Binding DatatableMerger}"/>
<Button Content="Add rows" Margin="0,0,10,10" Command="{Binding AddRowsButtonClickCommand}" Height="20" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="75"/>
</Grid>
The DataGridModel.cs
public class DataGridModel
{
public DataTable DatatableMerger { get; set; }
}
The DataGridViewModel.cs
public class DataGridViewModel : INotifyPropertyChanged
{
private readonly DataGridModel _dataGridModel = new DataGridModel();
public DataTable DatatableMerger => _dataGridModel.DatatableMerger;
public ICommand AddRowsButtonClickCommand => new DelegateCommand(o => ReturnDataTableForGridView());
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
public void ReturnDataTableForGridView()
{
var dt = new DataTable();
dt.Columns.Add("Foo");
dt.Columns.Add("Bar");
dt.Columns.Add("Baz");
for (var i = 0; i < 5; i++)
dt.Rows.Add($"Value {i}", i, DateTime.Now.AddSeconds(i));
_dataGridModel.DatatableMerger = dt;
OnPropertyChanged(nameof(DatatableMerger));
}
}
The delegate commands
public class DelegateCommand : ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
public event EventHandler CanExecuteChanged;
public DelegateCommand(Action<object> execute) : this(execute, null) { }
public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (_canExecute is null)
return true;
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
And for the merge funktion. Call the function with YourMainTableName = Mergetables(Table1, table2);
public DataTable MergeTables(DataTable Table1, DataTable Table2)
{
DataTable Mergetable = new DataTable();
foreach (DataColumn d in Table1.Columns)
{
Mergetable.Columns.Add(d.ColumnName);
}
foreach (DataColumn d in Table2.Columns)
{
Mergetable.Columns.Add(d.ColumnName);
}
int Table1Cols = Table1.Columns.Count;
int Table1Rows = Table1.Rows.Count;
int Table2Cols = Table2.Columns.Count;
int Table2Rows = Table2.Rows.Count;
DataRow row2;
bool end = false;
int RowCount = 0;
while (!end)
{
end = true;
if (RowCount < Table1Rows || RowCount < Table2Rows)
{
end = false;
row2 = Mergetable.NewRow();
if (RowCount < Table1Rows)
{
for (int col = 0; col < Table1Cols; col++)
{
row2[col] = Table1.Rows[RowCount][col];
}
}
if (RowCount < Table2Rows)
{
for (int col = 0; col < Table2Cols; col++)
{
row2[col + Table1Cols] = Table2.Rows[RowCount][col];
}
}
Mergetable.Rows.Add(row2);
}
RowCount++;
}
return Mergetable;
}