23
votes

When I select (by clicking or by keyboard) blank row on my DataGrid (when I want to add new row), unexpected validation error occurs (but with no exception) - the border of datagrid changes to red color, as you can see on the image below. When I click second time on blank row, the red border dissapears. Everything other works fine, the new row is added. Besides, I don't have any validation rules. And when I make a row with empty text, value is valid.

I don't want this behavior and this red border, anybody knows, why this happens and how to fix it? Why and where some validation fails?

enter image description here

Below I append some source code:

DataGrid definition in xaml:

    <DataGrid IsSynchronizedWithCurrentItem="True" DisplayMemberPath="Name" 
 ItemsSource="{Binding Path=ConfigFiles}" SelectedItem="{Binding Path=SelectedConfigFile}" 
              Grid.Column="1" Height="87" Margin="0,26,11,32" Style="{DynamicResource DataGridStyle}">
        <DataGrid.Columns>
            <DataGridTextColumn Width="1*" Binding="{Binding Name}" />
        </DataGrid.Columns>
    </DataGrid>

My ViewModel's part:

public class ManageModulesVM : BaseVM  // Implements INotifyPropertyChanged
{
    // ...

    public ObservableCollection<ConfigFile> ConfigFiles
    {
        get { return selectedModule == null ? null : selectedModule.ConfigFiles; }
        set
        {
            selectedModule.ConfigFiles = value;
            OnPropertyChanged(() => ConfigFiles);
        }
    }

    public ConfigFile SelectedConfigFile
    {
        get { return selectedModule == null ? null : selectedModule.SelectedConfigFile; }
        set
        {
            if (value != null)
            {
                selectedModule.SelectedConfigFile = value;
            }
            OnPropertyChanged(() => SelectedConfigFile);
            OnPropertyChanged(() => Parameters);
        }
    }

    // ...
}

ConfigFile class:

public class ConfigFile
{
    public string Name { get; set; }
    public IList<Parameter> Parameters { get; set; }

    public ConfigFile() { Name = ""; Parameters = new List<Parameter>(); }
}

Edit: After further investigation I know, that SelectedItem Binding is causing problems (when I remove this binding, validation error stops to appear), but I still don't know why and how to fix this.

5
I don't know that this is your problem but you have dual references to binding to Name. I would remove the DisplayMemberPath. You can also attached a pass thru converter (string to string) on name and that way you can catch the Exception. And I would explicitly put mode = twoway on the binding.paparazzo
@BalamBalam good point, It didn't solve my problem, but was helpful too.Łukasz Wiatrak

5 Answers

23
votes

I've found my own solution to this question. I've written a value converter and tied it to the binding:

(SelectedItem="{Binding Path=SelectedConfigFile,Converter={StaticResource configFileConverter}}")

The converter class:

namespace Converters
{
    public class SelectedConfigFileConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if(value is ConfigFile)
                return value;
            return null;
        }
    }
}

Define resource in resources.xaml file (or in any other resources place):

<ResourceDictionary (...) xmlns:conv="clr-namespace:Converters" >
    <conv:SelectedConfigFileConverter x:Key="configFileConverter" />
</ResourceDictionary>

The advantage of this solution is that the SelectedConfigFile property's type did't changed (to the general object type) so it is still strongly typed.

9
votes

To get the reason, when you click the new row of DataGrid in Debug mode, please see the debug window. There are first exception messages which will give you the idea why your problem is occurred.

Yes, the problem is from type casting. You need to modify the type of SelectedItem to object type as below.

public class ManageModulesVM : BaseVM  // Implements INotifyPropertyChanged
{
    // ...

    public object SelectedConfigFile
    {
        get { return selectedModule == null ? null : selectedModule.SelectedConfigFile; }
        set
        {
            if (value != null)
            {
                selectedModule.SelectedConfigFile = value;
            }
            OnPropertyChanged(() => SelectedConfigFile);
            OnPropertyChanged(() => Parameters);
        }
    }

    // ...
}
6
votes

Here's a general-purpose converter you can use for any DataGrid, binding any kind of item:

    public class DataGridItemConverter : MarkupExtension, IValueConverter
    {
    static DataGridItemConverter converter;

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
        return value;
        }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
        return (value != null && value.GetType() == targetType) ? value : null;
        }

    public override object ProvideValue(IServiceProvider serviceProvider)
        {
        if (converter == null)
            converter = new DataGridItemConverter();
        return converter;
        }
    }

Since it implements MarkupExtension you don't even need to define a static resource, you can just reference it like this:

SelectedItem="{Binding SelectedThing,Converter={conv:DataGridItemConverter}}"
4
votes

You can just add this line to your DataGrid:

<DataGrid  Validation.ErrorTemplate="{x:Null}" />
1
votes

You can just add this line to your DataGrid:

<DataGrid  Validation.ErrorTemplate="{x:Null}" />

It will solve the problem