Search code examples
c#wpfxamlcsvdatagrid

WPF DataGrid to csv, exporting only the visible values in the grid


I'm fairly new to WPF and was wondering whether a possibility exists to simply export a WPF DataGrid to a csv file. I've tried using reflections to get the values needed, though that works to some extent I was wondering whether it's possible with an attached property to get the displayed values, these do not necessarily correspond to the values of the item source. The below attached property works as long as I use a static string, or a static resource of a string etc. If I try to use the columns bindings I just get a the default string.empty

    public static readonly DependencyProperty ExportStringProperty =
        DependencyProperty.RegisterAttached("ExportString", //name of    attached property
        typeof(string), //type of attached property
        typeof(ExportBehaviour), //type of this owner class
        new PropertyMetadata(string.Empty)); //the default value of the attached property

    public static string GetExportString(DataGridColumn column)
    {
        return (string)column.GetValue(ExportStringProperty);
    }

    public static void SetExportString(DataGridColumn column, string value)
    {
        column.SetValue(ExportStringProperty, value);
    }

Is there a similar way to get the binding value from xaml in a way like:

    <DataGridTextColumn Header="Name" Binding="{Binding (datagridexcel:Product.Name)}" datagridexcel:ExportBehaviour.ExportString="{Binding (datagridexcel:Product.Name)}"/>

as said, the above earlier works for a static typed string and not for the binding. It has to be said that working with the item source in this case should be avoided and the only thing I'm interested in is the datagrid and the values shown there.


Solution

  • I made this simple app to demonstrate a way of getting CSV from DataGrid. You have the DataGrid:

    <DataGrid x:Name="MyDataGrid" Grid.Row="0" ItemsSource="{Binding Rows}" />
    

    In this example it's bound to the following property in the viewmodel:

    private IEnumerable<RowViewModel> _rows;
    public IEnumerable<RowViewModel> Rows
    {
        get { return _rows; }
        set
        {
            _rows = value;
            OnPropertyChanged("Rows");
        }
    }
    

    Rows are set to the following sample data:

    Rows = new List<RowViewModel>
    {
        new RowViewModel { FirstName = "John", LastName = "Doe", DateOfBirth = new DateTime(1988, 12, 19) },
        new RowViewModel { FirstName = "Lara", LastName = "Croft", DateOfBirth = new DateTime(1975, 5, 3) },
        new RowViewModel { FirstName = "Sam", LastName = "Fisher", DateOfBirth = new DateTime(1967, 2, 9) }
    };
    

    Under the DataGrid I have a Button:

    <Button Grid.Row="1" Content="Copy values as CSV" Command="{Binding CopyAsCsvCommand}" CommandParameter="{Binding ElementName=MyDataGrid}" />
    

    It's bound to a Command in the viewmodel and the CommandParameter is the whole DataGrid.

    CopyAsCsvCommand = new DelegateCommand<DataGrid>(CopyAsCsvHandler);
    

    The Command's handler method where the actual copying happens:

    private void CopyAsCsvHandler(DataGrid dg)
    {
        dg.SelectAllCells();
        dg.ClipboardCopyMode = DataGridClipboardCopyMode.IncludeHeader;
        ApplicationCommands.Copy.Execute(null, dg);
        dg.UnselectAllCells();
        LivePreviewText = (string)Clipboard.GetData(DataFormats.CommaSeparatedValue);
    }
    

    This is equivalent to selecting all the cells with CTRL+A and pressing CTRL+C.

    Example

    Example image

    Now that you have the CSV content you can just save it down to a file with CSV extension. I hope this helps and this is what you were looking for.