c# wpf mvvm datagrid

c# - WPF DataGrid SelectedItem



mvvm (3)

Tengo un DataGrid al que un usuario puede agregar elementos al ingresar datos en la última fila. También tengo un botón que elimina el elemento seleccionado actualmente. Pero cuando se selecciona la última fila (vacía, para agregar nuevos elementos), lo que fuera el último elemento seleccionado permanece en SelectedItem. Por lo tanto, si abro la ventana, selecciono la última fila y presiono el botón Eliminar, eliminará la primera fila, ya que está seleccionada de manera predeterminada, y la selección de la última fila no modificó SelectedItem. ¿Alguna buena forma de lidiar con esto?

Para aclarar: SelectedItem = "{Binding X}"

X en ViewModel no cambia cuando se selecciona la última fila (el setter no se invoca en absoluto). No estoy seguro de si la propiedad SelectedItem en sí misma cambia, pero supongo que no.

También hay una excepción cuando selecciono la última fila (borde rojo), pero cuando hago clic nuevamente para comenzar a ingresar datos, el borde rojo desaparece. No estoy seguro si estos dos están relacionados.


Difícil de decir sin código, pero me gustaría ver lo siguiente.

Asegúrese de que cada vez que se elimine un elemento y también sea el elemento seleccionado, establezca el elemento seleccionado vinculado a la propiedad en su ViewModel como nulo. Deberá asegurarse de que su SelectedItem vinculado a la propiedad no esté vinculado de una sola vez.


Ejecute el siguiente ejemplo y verá por qué no funciona.

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>

Código detrás:

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

Como puede ver al ejecutar, seleccionar la fila "nueva" hace que se establezca un valor centinela como el elemento seleccionado en el DataGrid . Sin embargo, WPF no puede convertir ese elemento centinela a una Person , por lo que el enlace SelectedItem no puede convertir.

Para solucionarlo, puede poner un convertidor en su enlace que detecta el centinela y devuelve un null cuando se detecta. Aquí hay un convertidor que lo hace:

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

Como puede ver, es una desafortunada necesidad probar el valor de ToString() del centinela, porque es un tipo interno. Alternativamente, podría (o además) verificar que GetType().Name es NamedObject .


Parece que olvidó establecer el Modo vinculante y el valor predeterminado está configurado en OneWay. Lo que significa que cualquier cambio realizado en su vista no se propagará de nuevo a su modelo de vista.

Y siempre asegúrese de tener el contexto de datos correcto.

Espero que ayude.