C# WPF DataGrid-如何在新添加的行上自动设置EditMode?

C# WPF DataGrid-如何在新添加的行上自动设置EditMode?,c#,wpf,mvvm,datagrid,C#,Wpf,Mvvm,Datagrid,到目前为止,我有一个遵循MVVM原则/规则的程序,代码为0,我有一个DataGrid,用户可以在其中添加、编辑或删除代表学生的行。 用户可以通过单击“+”按钮将一行添加到列表中,但为了编辑该行,用户必须首先单击他刚刚添加的行,该行不太友好 我一直在尝试将新添加的行设置为EditMode,但所有尝试都失败或成功,但对程序的其余部分产生了一些令人不安的副作用。我在网上查了一下,我发现的解决方案要么看起来有些过分,要么也有不良的副作用 我用更少的代码创建了一个类似的程序,以便更容易地显示我的程序和Da

到目前为止,我有一个遵循MVVM原则/规则的程序,代码为0,我有一个DataGrid,用户可以在其中添加、编辑或删除代表学生的行。 用户可以通过单击“+”按钮将一行添加到列表中,但为了编辑该行,用户必须首先单击他刚刚添加的行,该行不太友好

我一直在尝试将新添加的行设置为EditMode,但所有尝试都失败或成功,但对程序的其余部分产生了一些令人不安的副作用。我在网上查了一下,我发现的解决方案要么看起来有些过分,要么也有不良的副作用

我用更少的代码创建了一个类似的程序,以便更容易地显示我的程序和DataGrid的结构:

模型 视图模型
公共类学生视图模型:INotifyPropertyChanged
{
公立学生可视模型()
{
学生=新的可观察集合();
}
民办高校学生;
公选学生
{
获取{返回学生;}
设置
{
学生=价值观;
通知财产变更(学生姓名);
}
}
私人学生选择学生;
公共学生选择学生
{
获取{return selectedStudent;}
设置
{
selectedStudent=value;
NotifyPropertyChanged(姓名(SelectedStudent));
}
}
私有ICommand addRow;
公共ICommand AddRow
{
得到
{
if(addRow==null)
{
addRow=新的中继命令(
参数=>AddStudent(新学生()),
参数=>true
);
}
返回addRow;
}
}
私人ICommand removeCmd;
公共ICommand RemoveCmd
{
得到
{
removeCmd=新的RelayCommand(
参数=>RemoveStudent(作为学生的参数),
参数=>参数!=null
);
返回removeCmd;
}
}
私人无效添加学生(学生添加)
{
学生。添加(学生添加);
}
私人无效删除学生(学生删除)
{
if(Students.Contains(studentToRemove))
{
学生。移除(学生移动);
}
}
#区域索引
公共事件属性更改事件处理程序属性更改;
受保护的虚拟void NotifyPropertyChanged(字符串propertyName)
{
PropertyChanged?.Invoke(这是新的PropertyChangedEventArgs(propertyName));
}
#端区
}
看法

我更喜欢与MVVM兼容的解决方案,但老实说,在这一点上,我会感到满意,只要它不会导致其他问题,并且不需要大量的框架或多个充满代码的文件


我希望结果看起来像,但只要在点击“+”按钮后不涉及鼠标移动。

我找到了一种方法来实现您想要的结果,但它似乎不是很坚固,所以请小心使用。首先,您应该连接到
DataGrid
LoadingRow
事件

XAML


代码隐藏

private async void StudentDataGrid\u LoadingRow(对象发送方,DataGridRowEventArgs e){
//强制异步在UI完成加载后运行回调。
等待任务;
//将新行标记为选中
StudentDataGrid.SelectedItem=e.Row.Item;
StudentDataGrid.CurrentCell=新的DataGridCellInfo(e.Row.Item,StudentDataGrid.Columns[0]);
//进入编辑模式
StudentDataGrid.BeginEdit();
}
通过使方法
async
并通过
wait Task.Yield()
调用强制其异步执行,可以让UI在通过
BeginEdit()
调用开始编辑之前完成行的加载


我写这篇文章更多的是作为一个实验,我不知道我是否会推荐这篇文章,但我希望它能在有人找到更好的解决方案时有所帮助。

您的“添加”按钮应该绑定到视图模型,而不是绑定到视图模型的控件,这是第一件事,当我看到UI元素上的名称时,我总是会畏缩。我猜您不想显示模型(学生)的更改,因为没有INPC?你看到了吗?你是说添加按钮的命令参数吗?就我所见,结果是一样的,这是一种不好的做法吗?我在WPF方面只有不到一年的经验,所以当我问这个问题时,请原谅,但是如果您绑定到UI元素,命名它们有什么不好?另外,我的模型实际上不需要INPC(至少在本例中是这样),因为它只绑定到一个项目源—我的ObservableCollection,它将集合中的更改更新到UI。@XAMIMAX您链接的答案与我想要的答案大不相同,我不需要单击编辑,尤其是具有令人不安的副作用的编辑(阅读该答案的注释),我想要的是选择并编辑行
public class Student
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime DateOfBirth { get; set; }
    public string PhoneNumber { get; set; }
    public string Address { get; set; }
    public string Email { get; set; }
}
public class StudentsViewModel : INotifyPropertyChanged
{
    public StudentsViewModel()
    {
        Students = new ObservableCollection<Student>();
    }

    private ObservableCollection<Student> students;
    public ObservableCollection<Student> Students
    {
        get { return students; }
        set
        {
            students = value;
            NotifyPropertyChanged(nameof(Students));
        }
    }

    private Student selectedStudent;
    public Student SelectedStudent
    {
        get { return selectedStudent; }
        set
        {
            selectedStudent = value;
            NotifyPropertyChanged(nameof(SelectedStudent));
        }
    }

    private ICommand addRow;
    public ICommand AddRow
    {
        get
        {
            if(addRow == null)
            {
                addRow = new RelayCommand(
                    parameter => AddStudent(new Student()),
                    parameter => true
                );
            }
            return addRow;
        }
    }

    private ICommand removeCmd;
    public ICommand RemoveCmd
    {
        get
        {
            removeCmd = new RelayCommand(
                parameter => RemoveStudent(parameter as Student),
                parameter => parameter != null
            );
            return removeCmd;
        }
    }

    private void AddStudent(Student studentToAdd)
    {
        Students.Add(studentToAdd);
    }

    private void RemoveStudent(Student studentToRemove)
    {
        if (Students.Contains(studentToRemove))
        {
            Students.Remove(studentToRemove);
        }
    }

    #region INotify

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void NotifyPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion
}
<Window x:Class="DataGridExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:DataGridExample"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="600"
        Width="1000">
    <Window.Resources>
        <local:StudentsViewModel x:Key="StudentsVm"/>
    </Window.Resources>
    <Grid DataContext="{Binding Source={StaticResource StudentsVm}}">
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="1*"/>
        </Grid.RowDefinitions>
        <DockPanel LastChildFill="False"
                   Background="#FF2C58EC">
            <Button Command="{Binding AddRow}"
                    Height="25"
                    Margin="5">
                <Button.Template>
                    <ControlTemplate>
                        <Image Source="/Images/AddItem.png"/>
                    </ControlTemplate>
                </Button.Template>
            </Button>
            <Button Command="{Binding RemoveCmd}"
                    CommandParameter="{Binding ElementName=StudentDataGrid, Path=SelectedItem}"
                    Height="25"
                    Margin="5">
                <Button.Template>
                    <ControlTemplate>
                        <Image Source="/Images/DeleteItem.png"/>
                    </ControlTemplate>
                </Button.Template>
            </Button>
        </DockPanel>
        <DataGrid ItemsSource="{Binding Students}"
                  SelectedItem="{Binding Source={StaticResource StudentsVm}, Path=SelectedStudent, Mode=TwoWay}"
                  x:Name="StudentDataGrid"
                  ColumnWidth="*"
                  CanUserAddRows="False"
                  CanUserResizeRows="False"
                  CanUserResizeColumns="False"
                  CanUserSortColumns="False"
                  CanUserDeleteRows="False"
                  AutoGenerateColumns="False"
                  Grid.Row="1">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding FirstName, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
                                    Header="First Name">
                </DataGridTextColumn>
                <DataGridTextColumn Binding="{Binding LastName, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
                                    Header="Last Name">
                </DataGridTextColumn>
                <DataGridTemplateColumn Header="Date of Birth">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding DateOfBirth, StringFormat={}{0:dd.MM.yyyy}, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                    <DataGridTemplateColumn.CellEditingTemplate>
                        <DataTemplate>
                            <DatePicker SelectedDate="{Binding DateOfBirth, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
                                        DisplayDate="{Binding DateOfBirth, Mode=OneWay, UpdateSourceTrigger=LostFocus}">
                            </DatePicker>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellEditingTemplate>
                </DataGridTemplateColumn>
                <DataGridTextColumn Binding="{Binding PhoneNumber, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
                                    Header="Phone Number">
                </DataGridTextColumn>
                <DataGridTextColumn Binding="{Binding Address, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
                                    Header="Address">
                </DataGridTextColumn>
                <DataGridTextColumn Binding="{Binding Email, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
                                    Header="Email">
                </DataGridTextColumn>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>