2

I have DataGrid, which binds to DataTable, because i don't know the amount of columns at compile time.

XAML

<Window.Resources>
    <wpfApplication1:MainWindowViewModel x:Key="ViewModel" />
</Window.Resources>

<Grid DataContext="{StaticResource ViewModel}">
    <DataGrid x:Name="MyDataGrid" 
              ItemsSource="{Binding Path=DataTable}" />
</Grid>

MainWindowViewModel.cs

class MainWindowViewModel : INotifyPropertyChanged
{
    private DataTable dt;
    public event PropertyChangedEventHandler PropertyChanged;

    public MainWindowViewModel()
    {
        DataTable table = new DataTable();
        table.Columns.Add("Foo", typeof(int));
        table.Columns.Add("Bar", typeof(string));
        table.Rows.Add(1, "first");
        table.Rows.Add(2, null);
        DataTable = table;
    }

    private void OnPropertyChanged(string s)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(s));
        }
    }

    public DataTable DataTable
    {
        get { return dt; }
        set
        {
            dt = value;
            OnPropertyChanged("DataTable");
        }
    }
}

The result is:

simple table

There is nothing in the cell with null value. My question is: How can i change all the cells with null value to '-' without changing the source DataTable.

I can't use converter in binding because it will try to convert all the DataTable, not the values. I had no luck with DataGridCell styles also because i don't know how to get the data value and change it.

Edit

My final solution is based on one of the answers. I create MyDataGrid class and override OnAutoGeneratingColumn method.

class MyDataGrid : DataGrid
{
    protected override void OnAutoGeneratingColumn(DataGridAutoGeneratingColumnEventArgs e)
    {
        var textColumn = e.Column as DataGridTextColumn;

        if (textColumn != null)
        {
            var binding = textColumn.Binding;
            binding.TargetNullValue = "-";
        }

        base.OnAutoGeneratingColumn(e);
    }
}
Anatoliy Nikolaev
  • 22,370
  • 15
  • 69
  • 68
Dmitrii Dovgopolyi
  • 6,231
  • 2
  • 27
  • 44

4 Answers4

2

Try to change the null value in datatable as shown below:

if (dt != null)
{
    foreach (DataRow row in dt.Tables[0].Rows)
    {
        foreach (DataColumn col in dt.Tables[0].Columns)
        {
            string type = col.DataType.Name;

            if (row.IsNull(col))
            {
                if (type == "String") //for string values
                {
                    row.SetField(col, "-");
                }
                else // for integer
                {
                    row.SetField(col, 0);
                }
            }

        }
    }
}
Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
Saritha.S.R
  • 800
  • 1
  • 6
  • 19
2

You can try use this function:

private string SomeString = null;

...

table.Rows.Add(2, ReturnNullValue(SomeString, "---"));

private string ReturnNullValue(string Value, string NullValue) 
{
    if (String.IsNullOrEmpty(Value) == true)
    {
        return NullValue;
    }

    return Value;
}

Or you can add the property with additional logic:

private string _someString = "";

public string SomeString
{
    get
    {
        return _someString;
    }

    set
    {
        if (String.IsNullOrEmpty(value) == false)
        {
            _someString = value;
        }
        else
        {
            _someString = "---";
        }
    }
}

And use like this:

SomeString = "";
table.Rows.Add(2, SomeString);

I think we can do it without editing DataTable, that is no simple and quick solution, because Converter, Style and TargetNullValue powerless here.

Anatoliy Nikolaev
  • 22,370
  • 15
  • 69
  • 68
1

You could subscribe to the AutoGeneratingColumn-Event and replace the Binding with one, which has FallbackValue/TargetNullValue set to your liking. For cloning i used this.

    private void OnAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
    {
        DataGridTextColumn textColumn;
        if ((textColumn = e.Column as DataGridTextColumn) != null)
        {
            var binding = textColumn.Binding;
            var clone = binding.CloneBinding();
            clone.FallbackValue = "-----";
            clone.TargetNullValue = "-----";
            textColumn.Binding = clone;
        }
    }
Community
  • 1
  • 1
user3411327
  • 1,031
  • 8
  • 14
  • That was a great idea, thanks. I create `MyDataTable:DataTable` and modified `OnAutoGeneratingColumn` method based on your approach. – Dmitrii Dovgopolyi Apr 02 '14 at 09:00
  • 1
    Yet i have one question: why do you clone the binding object? – Dmitrii Dovgopolyi Apr 02 '14 at 09:06
  • 1
    Ah, right. In this scenario this is not necessary. If the binding was obtained by BindingOperations.GetBinding() it would throw an exception when trying to change a value of the binding. Since the binding here is just set to a CLR-Property you don't need to clone it. – user3411327 Apr 02 '14 at 09:42
0

Maybe you can create a ViewModel class that inherits DataTable or just keeps a reference of that DataTable class. That ViewModel class is what you bind to the DataGrid's ItemSource. The ViewModel should then know what data it should or shouldn't convert (nulls to "-") and return to the DataGrid. That way the DataTable remains unchanged.

The Heist
  • 1,444
  • 1
  • 16
  • 32
bjutus
  • 11
  • 3