1

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.

  1. What works is adding files to the listbox with the binding in the XAML code and codebehind.
  2. What doesn't work is merging the data to display dynamically with the binding to the datagrid. Property changed are raised, but grid aren't updating.

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:

Currently output - Picture

Wished output:

Wished output - picture

Propertychanged getting raised

Lowrater
  • 35
  • 10
  • I don't see where you're refreshing the `DataGrid` content after adding a file. There's a `DataGridViewModel.ReturnDataTableForGridView();` in the add button event handler, but that's not going to update the `DataGrid`. No `PropertyChanged` or `CollectionChanged` events are being raised. – redcurry Feb 07 '19 at 14:16
  • @redcurry I'm not sure how to resolve this correctly. I've saw another example and added everything in the datagridviewmodel. Here i've added a dataview that could raise the propertychanged event with return of the datatable, and binded it to the XAML. Now i see it raise the event, but the grid doesn't update. Some used = delegate {} but nothing happens. I've then saw that datatables can't raise by propertychanged, is why i've changed it to dataview. I've also saw this post for help by kiwipom: [LINK](https://stackoverflow.com/questions/5785265/using-inotifypropertychanged-in-wpf). – Lowrater Feb 10 '19 at 18:22
  • If, when you expect the data to refresh, you look at the Output window in Visual Studio, do you see any binding errors? – redcurry Feb 11 '19 at 15:15
  • @redcurry Nothing at all. I've updated the whole code now, and added the propertychanged getting raised, so you can see specific what's in the data flow. I've also found ekstra information regarding binding here: [LINK](https://stackoverflow.com/questions/12329208/wpf-datagrid-not-updating-on-propertychanged) from syned. Still nothing after the XAML getting changed. – Lowrater Feb 11 '19 at 16:26
  • @redcurry All test with this post [LINK](https://stackoverflow.com/questions/1512627/propertychanged-event-always-null) doens't resolve that the propertychanged always is null either, and shows the data as wished. – Lowrater Feb 11 '19 at 17:16
  • Do you have the code online somewhere, so I can open it in Visual Studio? – redcurry Feb 12 '19 at 14:52
  • @redcurry Yes. You can get it directly from here [Link](https://drive.google.com/open?id=1zLp7MKq04AIpVpqhYIHOvIw01v_DtBkQ) Thank you for taking a look. – Lowrater Feb 13 '19 at 18:41
  • 1
    @redcurry Hi. I'm just got an answer to resolve my issue. Thank you a lot for your time. - Answar is posted. – Lowrater Feb 14 '19 at 09:07

1 Answers1

0

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;
    }
Community
  • 1
  • 1
Lowrater
  • 35
  • 10