【问题标题】:WPF DataGrid SelectedItemWPF DataGrid 选定项
【发布时间】:2012-02-02 08:21:58
【问题描述】:

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

澄清: SelectedItem="{绑定 X}"

x在选中最后一行时,ViewModel中不会更改(SATTER根本未调用)。我不确定 SelectedItem 属性本身是否会发生变化,但我认为它不会发生变化。

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

【问题讨论】:

    标签: c# wpf mvvm datagrid


    【解决方案1】:

    运行下面的例子,你就会明白为什么它不起作用了。

    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>
    

    代码隐藏:

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

    正如您在运行时看到的那样,选择“新”行会导致将标记值设置为DataGrid 中的选定项。但是,WPF 无法将该标记项转换为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 (item.Equals(CollectionView.NewItemPlaceholder)))
                {
                    return null;
                }
    
                return value;
            }
        }
    }
    

    如您所见,不幸的是,必须针对哨兵的ToString() 值进行测试,因为它是一个内部类型。您可以选择(或另外)检查 GetType().Name 是否为 NamedObject

    【讨论】:

    • 5 岁以后,不过还不如放在这里:可以和CollectionView.NewItemPlaceholder对比一下,和你做的一样,不需要烧掉“{NewItemPlaceholder}”代码上的字符串。
    【解决方案2】:

    没有代码很难说,但我会看看以下内容。

    确保每当删除一个项目并且它也是选定项目时,将绑定到 ViewModel 中的属性的选定项目设置为 null。您需要确保绑定到属性的 SelectedItem 不是单向绑定的。

    【讨论】:

      【解决方案3】:

      您好像忘记设置绑定模式,默认设置为 OneWay。这意味着在您的视图中所做的任何更改都不会传播回您的视图模型。

      并始终确保您拥有正确的数据上下文。

      希望对您有所帮助。

      【讨论】:

        猜你喜欢
        • 2023-03-08
        • 1970-01-01
        • 2012-12-16
        • 2023-03-16
        • 2016-06-13
        • 2020-10-03
        • 2011-02-06
        • 2011-04-24
        • 1970-01-01
        相关资源
        最近更新 更多