Автозаполнение элементов ввода и удаление/добавление строки с помощью MVVM
1 Answers
Вот пример с использованием шаблона MVVM.
NotifyPropertyChanged.cs - вспомогательный класс
public class NotifyPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Member.cs - класс данных
public class Member : NotifyPropertyChanged
{
private string _firstName;
private string _middleName;
private string _lastName;
public string FirstName
{
get => _firstName;
set
{
_firstName = value;
OnPropertyChanged();
}
}
public string MiddleName
{
get => _middleName;
set
{
_middleName = value;
OnPropertyChanged();
}
}
public string LastName
{
get => _lastName;
set
{
_lastName = value;
OnPropertyChanged();
}
}
}
MainViewModel.cs - сюда указывает MainWindow.DataContext
public class MainViewModel : NotifyPropertyChanged
{
private ObservableCollection<Member> _members;
public ObservableCollection<Member> Members
{
get => _members;
set
{
_members = value;
OnPropertyChanged();
}
}
public MainViewModel()
{
// тестовые данные
Members = new ObservableCollection<Member>
{
new Member { FirstName = "Иван", MiddleName = "Иванович", LastName = "Иванов"},
new Member { FirstName = "Пётр", MiddleName = "Петрович", LastName = "Петров"}
};
}
}
MainWindow.xaml - полная разметка
<Window x:Class="WpfApp2.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:WpfApp2"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<TextBox Width="150" Margin="5" Text="{Binding Members/FirstName}"/>
<TextBox Width="150" Margin="5" Text="{Binding Members/MiddleName}"/>
<TextBox Width="150" Margin="5" Text="{Binding Members/LastName}"/>
</StackPanel>
<DataGrid Grid.Row="1" Margin="5" ItemsSource="{Binding Members}" IsSynchronizedWithCurrentItem="True" CanUserSortColumns="False"/>
</Grid>
</Window>
Выглядит это так
Вся магия заключается в IsSynchronizedWithCurrentItem, оно передает текущий выбранный элемент в CollectionView, через которую DataGrid взаимодействут с коллекцией Members. Затем нехитрым способом этом можно подхватить через Binding.Path в TextBox.
Дополнение
Вы отредактировали вопрос, чтобы узнать, как добавлять и удалять элементы, и как удалять сразу несколько элементов. Вообще, в таких случаях стоит создавтаь отдельный вопрос, но если очень хочется, то вот. Добавил немного кода в решение выше.
Потребуется еще один вспомогательный класс для работы с командами.
RelayCommand.cs
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Func<object, bool> _canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);
public void Execute(object parameter) => _execute(parameter);
}
Далее, добавил код в следующие классы:
Member.cs - пробросил статус выделения строки сюда через Binding
private bool _selected;
public bool Selected
{
get => _selected;
set
{
_selected = value;
OnPropertyChanged();
}
}
MainViewModel.cs - добавил команды
private ICommand _addMemberCommand;
private ICommand _removeSelectedCommand;
public ICommand AddMemberCommand => _addMemberCommand ?? (_addMemberCommand = new RelayCommand(parameter =>
{
foreach (Member m in Members)
{
m.Selected = false;
}
Members.Add(new Member() { Selected = true });
}));
public ICommand RemoveSelectedCommand => _removeSelectedCommand ?? (_removeSelectedCommand = new RelayCommand(parameter =>
{
List<Member> members = Members.Where(x => x.Selected).ToList();
if (members.Count > 0 && MessageBox.Show("Remove " + members.Count + " items?", "Delete", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
foreach (Member m in members)
{
Members.Remove(m);
}
}
}));
MainWindow.xaml - новая разметка
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<TextBox Width="150" Margin="5" Text="{Binding Members/FirstName}" IsEnabled="{Binding Members/Selected, FallbackValue=False}"/>
<TextBox Width="150" Margin="5" Text="{Binding Members/MiddleName}" IsEnabled="{Binding Members/Selected, FallbackValue=False}"/>
<TextBox Width="150" Margin="5" Text="{Binding Members/LastName}" IsEnabled="{Binding Members/Selected, FallbackValue=False}"/>
<Button Margin="5" Padding="5,0" Content="Add" Command="{Binding AddMemberCommand}"/>
<Button Margin="5" Padding="5,0" Content="Remove selected" Command="{Binding RemoveSelectedCommand}"/>
</StackPanel>
<DataGrid Grid.Row="1" Margin="5" ItemsSource="{Binding Members}" IsSynchronizedWithCurrentItem="True" AutoGenerateColumns="False" IsReadOnly="True" CanUserSortColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Имя" Binding="{Binding FirstName}"/>
<DataGridTextColumn Header="Отчество" Binding="{Binding MiddleName}"/>
<DataGridTextColumn Header="Фамилия" Binding="{Binding LastName}"/>
</DataGrid.Columns>
<DataGrid.ItemContainerStyle>
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="IsSelected" Value="{Binding Selected}"/>
</Style>
</DataGrid.ItemContainerStyle>
<DataGrid.InputBindings>
<KeyBinding Key="Delete" Command="{Binding RemoveSelectedCommand}"/>
</DataGrid.InputBindings>
</DataGrid>
</Grid>
Так как клавиша Delete по умолчанию удаляет в DataGrid то что выделено без предупреждения, я ее тоже перенаправил на команду.
IsEnabled в текстбоксах прибиндил, чтобы они стали неактивными, если в таблице ничего не выделено, или вообще нет строк.
Колонки в DataGrid теперь формируются в разметке, а не автоматически. Сортировку при клике по заголовку отключил, потому что она не работает нормально с IsSynchronizedWithCurrentItem, начинается рассинхронизация после сортировки.
- 49,560

