选择空行时,DataGrid 出现意外的红色边框(验证错误)

22
当我在DataGrid上选择(通过点击或键盘)空行(当我想要添加新行时),出现意外的验证错误(但没有异常)- datagrid的边框变为红色,如下图所示。当我第二次单击空行时,红色边框消失。除此之外,其他所有功能都正常,新行已添加。此外,我没有任何验证规则。当我创建一个空文本行时,值是有效的。
我不希望出现这种行为和红色边框,请问有人知道为什么会发生这种情况以及如何解决?为什么在哪里某些验证失败?

enter image description here

以下是一些源代码:

xaml中的DataGrid定义:

    <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>

我的ViewModel的部分:

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);
        }
    }

    // ...
}

配置文件类:

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

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

编辑: 经过进一步调查,我知道选定项绑定会导致问题(当我删除此绑定时,验证错误停止出现),但我仍然不知道为什么以及如何修复它。


我不确定这是否是你的问题,但你对绑定到Name有双重引用。我建议移除DisplayMemberPath。你也可以在name上附加一个传递转换器(字符串到字符串),这样你就可以捕获异常。并且我会在绑定上明确地设置mode = twoway。 - paparazzo
1
@BalamBalam 好观点,虽然它没有解决我的问题,但还是很有帮助的。 - Łukasz Wiatrak
5个回答

22

对于这个问题,我已经找到了自己的解决方案。我编写了一个值转换器,并将其绑定到了选择项:

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

转换器类:

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;
        }
    }
}

resources.xaml文件中(或任何其他资源位置)定义资源:

<ResourceDictionary (...) xmlns:conv="clr-namespace:Converters" >
    <conv:SelectedConfigFileConverter x:Key="configFileConverter" />
</ResourceDictionary>
这种解决方案的优点在于,SelectedConfigFile 属性的类型没有改变(仍为强类型),因此更加稳健。

2
你应该将这个标记为答案,因为它是更正确的解决方案。请参考http://blog.spencen.com/2009/04/30/problems-binding-to-selectedvalue-with-microsoftrsquos-wpf-datagrid.aspx以获取类似的解决方案。 - yagni

11

如果您在Debug模式下点击DataGrid的新行时想要获取原因,请查看调试窗口。第一个异常消息会给您一个思路,让您知道为什么出现了问题。

是的,这个问题是由类型转换引起的。您需要将SelectedItem的类型修改为对象类型,如下所示。

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);
        }
    }

    // ...
}

1
谢谢,它起作用了 :) 是的,在Visual Studio的调试输出窗口中有异常(无法将'{NewItemPlaceholder}'从类型'NamedObject'转换为类型'ConfigFile'),我是WPF的新手,我不知道这样有价值的信息会被写在那里。 - Łukasz Wiatrak
这篇文章实际上是关于如何调试,而不是为OP提供解决方案。请参阅上面标记的Łukasz Wiatrak的答案。 - user585968
1
@MickyDuncan,你可能是对的,但是正如你所看到的,我的答案提供了基本解决方案,这将让@Łukasz Wiatrak创建“SelectedConfigFileConverter”。你可以认为@Łukasz Wiatrak的答案更好,但我不能同意“这篇文章真正关于如何调试而不是为OP提供解决方案”,因为我已经展示了上面的解决方案来解决这个问题。 - Jin-Wook Chung
1
@Jin-WookChung,你的虚拟机不仅不具备类型安全性,而且还存在一个会导致崩溃的错误。此外,你声称你的“基本解决方案”会神奇地引导OP创建SelectedConfigFileConverter,但没有证据支持这一点。你甚至没有提到IValueConverter - user585968

6
这是一个通用的转换器,您可以将其用于任何DataGrid,并绑定任何类型的项目:
    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;
        }
    }

由于它实现了MarkupExtension,因此您甚至不需要定义静态资源,只需像这样引用它:

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

4
你可以将以下代码添加到你的 DataGrid 中:

code here

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

1
这不是一个好的解决方案,因为它会删除验证模板,那么我们该如何自定义验证? - Hisham

1
您可以将此行代码添加到您的DataGrid中:

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

它会解决这个问题。

抑制错误以摆脱它们从来不是一个好主意,这可能会在未来导致调试噩梦。找到错误的原因并修复它。 - Richard Moore

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接