C# MVVM数据网格,用户可以在其中添加行。在所有字段都有效之前不要添加

C# MVVM数据网格,用户可以在其中添加行。在所有字段都有效之前不要添加,c#,wpf,mvvm,C#,Wpf,Mvvm,我有一个WPF数据网格,用户可以在其中添加行。我希望它在输入某些必需的单元格之前不添加新行 e、 g 我的数据网格有以下列,名字,姓氏,年龄,职务,薪水 当我通过datagrid输入新行时,我只需输入FirstName,我的集合中就有了一个只设置了FirstName的新模型。在输入名字、姓氏和年龄之前,我不想添加新型号 我的型号: public sealed class Foo { public Foo() { } public Foo(string firstN

我有一个WPF数据网格,用户可以在其中添加行。我希望它在输入某些必需的单元格之前不添加新行

e、 g

我的数据网格有以下列,
名字
姓氏
年龄
职务
薪水

当我通过datagrid输入新行时,我只需输入
FirstName
,我的集合中就有了一个只设置了
FirstName
的新模型。在输入
名字
姓氏
年龄
之前,我不想添加新型号

我的型号:

public sealed class Foo
{
    public Foo()
    {
    }
    public Foo(string firstName, string surname, int age)
    {
        Age = age;
        FirstName = firstName;
        Surname = surname;
    }
    [Required]
    public int Age { get; set; }
    [Required]
    public string FirstName { get; set; }
    [Required]
    public string Surname { get; set; }
    public string JobTitle { get; set; }
    public decimal Salary { get; set; }
}
视图模型

internal sealed class MainViewModel : ViewModelBase
{
    private ObservableCollection<Foo> _foos = new ObservableCollection<Foo>();
    private IDataService _dataService;
    private Foo _selectedFoo;
    public RelayCommand ButtonClickCommand { get; private set; }
    public MainViewModel(IDataService dataService)
    {
        _dataService = dataService;
        Foos = _dataService.Foos();
        RegisterCommands();
    }

    public void RegisterCommands()
    {
        ButtonClickCommand = new RelayCommand(
            ButtonClick,
            () => true);
    }
    public ObservableCollection<Foo> Foos
    {
        get { return _foos; }
        set
        {
            if (_foos == value) return;
            _foos = value;
            RaisePropertyChanged();
        }
    }

    public Foo Foo
    {
        get { return _selectedFoo; }
        set
        {
            if (_selectedFoo == value) return;
            _selectedFoo = value;
            RaisePropertyChanged();
        }
    }

    private void ButtonClick()
    {
    }
}
现在,我的
DataGrid
如下所示:

 <DataGrid Grid.Row="0"
              Grid.Column="0"
              AutoGenerateColumns="False"
              ItemsSource="{Binding Foos, ValidatesOnExceptions=True}" 
              CanUserAddRows="True"
              SelectedItem="{Binding Foo}">
        <DataGrid.RowValidationErrorTemplate>
            <ControlTemplate>
                <Grid Margin="0,-2,0,-2"
                      ToolTip="{Binding RelativeSource={RelativeSource
        FindAncestor, AncestorType={x:Type DataGridRow}},
        Path=(Validation.Errors)[0].ErrorContent}">
                    <Ellipse StrokeThickness="0" Fill="Red" 
                             Width="{TemplateBinding FontSize}" 
                             Height="{TemplateBinding FontSize}" />
                    <TextBlock Text="!" FontSize="{TemplateBinding FontSize}" 
                               FontWeight="Bold" Foreground="White" 
                               HorizontalAlignment="Center"  />
                </Grid>
            </ControlTemplate>
        </DataGrid.RowValidationErrorTemplate>
        <DataGrid.RowValidationRules>
            <validation:FooValidationRule ValidationStep="UpdatedValue" ValidatesOnTargetUpdated="True"/>
        </DataGrid.RowValidationRules>
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding (model:Foo.Age), ValidatesOnExceptions=True}" Header="Age" />
            <DataGridTextColumn Binding="{Binding (model:Foo.FirstName), ValidatesOnExceptions=True}" Header="First Name"/>
            <DataGridTextColumn Binding="{Binding (model:Foo.Surname), ValidatesOnExceptions=True}" Header="Surname"/>
            <DataGridTextColumn Binding="{Binding (model:Foo.JobTitle), ValidatesOnExceptions=True}" Header="Job Title"/>
            <DataGridTextColumn Binding="{Binding (model:Foo.Salary), ValidatesOnExceptions=True}" Header="Salary"/>
        </DataGrid.Columns>
    </DataGrid>


但是,即使行处于无效状态,它仍然会向绑定的
Foos
集合添加一个新的
Foo

我过去这样做的一种方法是使用一个带有弹出对话框的“添加”按钮。在此对话框中设置字段,然后有一个“保存”-“取消”按钮。然后,您可以在保存时进行验证,然后单击并提交到datagrid。好了,验证只是向用户指示他们是否输入了有效的数据,它并不控制提交数据。我有两个列表。一个用于UI的列表和一个持久化的列表,一旦这些列表有效,我将把它们从UI列表移动到持久化列表。凌乱的我也喜欢你的想法,但必须通过datagrid输入:(这仍然不是最糟糕的事情。请记住,您的viewmodel充当视图到模型的中间层。您可以在viewmodel中有临时或中间层列表,并且只有在保存类型操作或某些触发器上,数据才会返回到实际模型。它并不总是很漂亮,但这就是开发:)。我过去这样做的一种方法是,有一个“添加”按钮,它有一个弹出的对话框。在这个对话框中,您可以设置字段,然后有一个“保存”-“取消”按钮。然后,您可以在保存时单击并提交到datagrid进行验证。好了,验证只是向用户指示他们是否输入了有效数据,它并不控制数据的提交。我有两个列表。一个列表用于UI,另一个列表用于持久化,我正在将内容从UI列表移动到持久化列表一旦它有效。混乱。我也喜欢你的想法,但它必须通过datagrid输入,不幸的是:(这仍然不是最糟糕的事情。请记住,您的viewmodel充当视图到模型的中间层。您可以在viewmodel中有临时或中间层列表,并且只有在保存类型操作或某些触发器上,数据才会返回到实际模型。它并不总是漂亮的,但这就是开发:)。
public sealed class FooValidationRule : ValidationRule
{
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        if (((BindingGroup) value)?.Items.Count == 0)
        {
            IsValid = true;
            return new ValidationResult(true, null);
        }
        if (!(((BindingGroup) value)?.Items[0] is Foo foo))
        {
            IsValid = false;
            return new ValidationResult(false, "Error");
        }
        if (string.IsNullOrWhiteSpace(foo.FirstName))
        {
            IsValid = false;
            return new ValidationResult(false, "Must enter a First Name");
        }
        if (string.IsNullOrWhiteSpace(foo.Surname))
        {
            IsValid = false;
            return new ValidationResult(false, "Must enter a Surname");
        }
        if (foo.Age == 0)
        {
            IsValid = false;
            return new ValidationResult(false, "Must enter age");
        }

        IsValid = true;
        return new ValidationResult(true, string.Empty);
    }

    public bool IsValid { get; set; }
}
 <DataGrid Grid.Row="0"
              Grid.Column="0"
              AutoGenerateColumns="False"
              ItemsSource="{Binding Foos, ValidatesOnExceptions=True}" 
              CanUserAddRows="True"
              SelectedItem="{Binding Foo}">
        <DataGrid.RowValidationErrorTemplate>
            <ControlTemplate>
                <Grid Margin="0,-2,0,-2"
                      ToolTip="{Binding RelativeSource={RelativeSource
        FindAncestor, AncestorType={x:Type DataGridRow}},
        Path=(Validation.Errors)[0].ErrorContent}">
                    <Ellipse StrokeThickness="0" Fill="Red" 
                             Width="{TemplateBinding FontSize}" 
                             Height="{TemplateBinding FontSize}" />
                    <TextBlock Text="!" FontSize="{TemplateBinding FontSize}" 
                               FontWeight="Bold" Foreground="White" 
                               HorizontalAlignment="Center"  />
                </Grid>
            </ControlTemplate>
        </DataGrid.RowValidationErrorTemplate>
        <DataGrid.RowValidationRules>
            <validation:FooValidationRule ValidationStep="UpdatedValue" ValidatesOnTargetUpdated="True"/>
        </DataGrid.RowValidationRules>
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding (model:Foo.Age), ValidatesOnExceptions=True}" Header="Age" />
            <DataGridTextColumn Binding="{Binding (model:Foo.FirstName), ValidatesOnExceptions=True}" Header="First Name"/>
            <DataGridTextColumn Binding="{Binding (model:Foo.Surname), ValidatesOnExceptions=True}" Header="Surname"/>
            <DataGridTextColumn Binding="{Binding (model:Foo.JobTitle), ValidatesOnExceptions=True}" Header="Job Title"/>
            <DataGridTextColumn Binding="{Binding (model:Foo.Salary), ValidatesOnExceptions=True}" Header="Salary"/>
        </DataGrid.Columns>
    </DataGrid>