WPF DataGrid SelectedItem

svi*_*nja 5 c# wpf datagrid mvvm

我有一个DataGrid,用户可以通过在最后一行输入数据来添加项目.我还有一个删除当前所选项目的按钮.但是当选择最后一个(空的,用于添加新项目)行时,最后选择的项目保留在SelectedItem中.因此,如果我打开窗口,选择最后一行,然后按删除按钮,它将删除第一行,因为它是默认选中的,并且选择最后一行不会更改SelectedItem.有什么好方法可以解决这个问题吗?

澄清:SelectedItem ="{Binding X}"

选择最后一行时,ViewModel中的X不会更改(根本不调用setter).我不确定SelectedItem属性本身是否会发生变化,但我认为它不会.

当我选择最后一行(红色边框)时也有一个例外,但当我再次单击它以开始输入数据时,红色边框会消失.不确定这两者是否相关.

Ken*_*art 11

运行以下示例,您将看到它为什么不起作用.

XAML:

<Window x:Class="DataGridTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <DockPanel>
        <TextBlock DockPanel.Dock="Bottom" Text="{Binding SelectedItem, ElementName=dataGrid}"/>
        <TextBlock DockPanel.Dock="Bottom" Text="{Binding SelectedItem}"/>
        <DataGrid x:Name="dataGrid" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}" CanUserAddRows="True" CanUserDeleteRows="True" AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTextColumn Header="First Name" Binding="{Binding FirstName}"/>
                <DataGridTextColumn Header="Last Name" Binding="{Binding FirstName}"/>
            </DataGrid.Columns>
        </DataGrid>
    </DockPanel>
</Window>
Run Code Online (Sandbox Code Playgroud)

代码隐藏:

namespace DataGridTest
{
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Windows;

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private readonly ICollection<Person> items;
        private Person selectedItem;

        public MainWindow()
        {
            InitializeComponent();

            this.items = new ObservableCollection<Person>();
            this.items.Add(new Person
                {
                    FirstName = "Kent",
                    LastName = "Boogaart"
                });
            this.items.Add(new Person
            {
                FirstName = "Tempany",
                LastName = "Boogaart"
            });

            this.DataContext = this;
        }

        public ICollection<Person> Items
        {
            get { return this.items; }
        }

        public Person SelectedItem
        {
            get { return this.selectedItem; }
            set
            {
                this.selectedItem = value;
                this.OnPropertyChanged("SelectedItem");
            }
        }

        private void OnPropertyChanged(string propertyName)
        {
            var handler = this.PropertyChanged;

            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }

    public class Person
    {
        public string FirstName
        {
            get;
            set;
        }

        public string LastName
        {
            get;
            set;
        }

        public override string ToString()
        {
            return FirstName + " " + LastName;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

正如您在运行时所看到的那样,选择"新"行会导致将标记值设置为所选项目DataGrid.但是,WPF无法将该sentinel项转换为a Person,因此SelectedItem绑定无法转换.

要解决此问题,您可以在绑定上放置一个转换器来检测哨兵并null在检测到时返回.这是一个转换器,它这样做:

namespace DataGridTest
{
    using System;
    using System.Windows.Data;

    public sealed class SentinelConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return value;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value != null && string.Equals("{NewItemPlaceholder}", value.ToString(), StringComparison.Ordinal))
            {
                return null;
            }

            return value;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,测试ToString()哨兵的价值是一个不幸的必要,因为它是一种内部类型.或者,您可以(或补充)检查GetType().NameNamedObject.

  • 5岁以后,但也可以把它放在这里:您可以将它与CollectionView.NewItemPlaceholder进行比较,这与您正在做的事情相同,而无需在代码上刻录"{NewItemPlaceholder}"字符串. (3认同)