Как обрабатывать щелчок мыши на ячейке таблицы данных WPF в C#, которая зависит от выбранной строки и столбца - PullRequest
0 голосов
/ 01 мая 2020

Вот изображение моей таблицы данных WPF: enter image description here Он был создан с этим XAML:

        <Grid x:Name="LinkedInUsersDataGrid" Margin="54 0">
        <DataGrid x:Name="LinkedInUsersLatestDetails" 
                  Style="{DynamicResource LinkedInDataGridStyle}" 
                  AutoGenerateColumns="False" 
                  GridLinesVisibility="None">
            <DataGrid.Columns>
                <DataGridTextColumn Header="User name" Binding="{Binding UserName}" Width="120" />
                <DataGridTextColumn Header="LinkedIn profile id" Binding="{Binding LinkedInProfileId}" Width="150"/>
                <DataGridTextColumn Header="Primary email" Binding="{Binding PrimaryEmail}" Width="180"/>
                <DataGridTextColumn Header="Actions" Binding="{Binding View}" Width="60" Foreground="#0073b1" />
                <DataGridTextColumn Header="" Binding="{Binding Edit}" Width="60" Foreground="#0073b1" />
                <DataGridTextColumn Header="" Binding="{Binding Delete}" Width="60" Foreground="#0073b1" />
            </DataGrid.Columns>
        </DataGrid>
    </Grid>

Я хотел бы инициировать открытие новой страницы XAML для Просмотрите или измените сведения, относящиеся к выбранной строке, в зависимости от указанной ячейки c, по которой щелкнули. Я много гуглил, но не могу понять, как зафиксировать событие щелчка и использовать ячейку, по которой щелкнули, чтобы узнать, какие пользовательские данные загрузить.

Возможно ли это? Мысли очень приветствуются.

В

1 Ответ

0 голосов
/ 01 мая 2020

Это может быть достигнуто с помощью MVVM .

По шаблону нам требуется 2 вспомогательных класса:

// notifies UI if some property changed
public class NotifyPropertyChanged : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

// needed for easy Commands use
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);
}

Я предполагаю, что вы нужно просто отображать данные, а не редактировать их в строке. Тогда DataGrid - это какая-то избыточная избыточность. И чтобы показать вам различные функции WPF, с которыми я играл ListView.

В MVVM вам не нужны взаимодействия с элементами управления из кода C#. Таким образом, я не дал Name элементам управления. Чтобы что-то изменить, просто измените данные, элементы управления будут обновляться автоматически с помощью вызовов Binding и OnPropertyChanged().

Полная разметка, чтобы все было понятно.

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApp1"
        Title="MainWindow" Height="600" Width="1000" WindowStartupLocation="CenterScreen">
    <Window.DataContext>
        <local:MainViewModel/><!-- MainViewModel attached here -->
    </Window.DataContext>
    <Grid>
        <ListView ItemsSource="{Binding ProfilesList}" Margin="5">
            <ListView.Resources>
                <Style TargetType="Button" x:Key="ListViewButtonStyle">
                    <Setter Property="Foreground" Value="Black"/>
                    <Setter Property="Margin" Value="5,0"/>
                    <Setter Property="Visibility" Value="Hidden"/>
                    <Style.Triggers><!-- show buttons in list if MouseOver or Selected-->
                        <DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType=ListBoxItem}}" Value="True">
                            <Setter Property="Visibility" Value="Visible"/>
                        </DataTrigger>
                        <DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem}}" Value="True">
                            <Setter Property="Visibility" Value="Visible"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </ListView.Resources>
            <ListView.Template>
                <ControlTemplate>
                    <StackPanel Orientation="Vertical" >
                        <Grid Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"><!-- header markup -->
                            <Grid Margin="6,1" >
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition />
                                    <ColumnDefinition />
                                    <ColumnDefinition />
                                    <ColumnDefinition Width="70"/>
                                </Grid.ColumnDefinitions>
                                <Grid.Resources>
                                    <Style TargetType="TextBlock">
                                        <Setter Property="Margin" Value="5,4"/>
                                        <Setter Property="FontWeight" Value="Bold"/>
                                    </Style>
                                    <Style TargetType="Border">
                                        <Setter Property="Background" Value="LightGray"/>
                                        <Setter Property="Width" Value="1"/>
                                        <Setter Property="HorizontalAlignment" Value="Right"/>
                                    </Style>
                                </Grid.Resources>
                                <TextBlock Grid.Column="0" Text="User name"/>
                                <TextBlock Grid.Column="1" Text="LinkedIn profile id"/>
                                <TextBlock Grid.Column="2" Text="Primary email" />
                                <TextBlock Grid.Column="3" Text="Actions"/>
                                <Border Grid.Column="0"/>
                                <Border Grid.Column="1"/>
                                <Border Grid.Column="2"/>
                            </Grid>
                        </Grid>
                        <ItemsPresenter /><!-- telling Control to insert contents here -->
                    </StackPanel>
                </ControlTemplate>
            </ListView.Template>
            <ListView.ItemTemplate>
                <DataTemplate DataType="{x:Type local:Profile}">
                    <Grid Background="Transparent">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition />
                            <ColumnDefinition />
                            <ColumnDefinition />
                            <ColumnDefinition Width="35"/>
                            <ColumnDefinition Width="35"/>
                        </Grid.ColumnDefinitions>
                        <Grid.InputBindings>
                            <MouseBinding Gesture="LeftDoubleClick" Command="{Binding DataContext.OpenItemCommand, RelativeSource={RelativeSource AncestorType=Window}}" CommandParameter="{Binding}"/>
                        </Grid.InputBindings>
                        <TextBlock Grid.Column="0" Text="{Binding UserName}" TextWrapping="Wrap" Margin="5"/>
                        <TextBlock Grid.Column="1" Text="{Binding LinkedInProfileId}" TextWrapping="Wrap" Margin="5"/>
                        <TextBlock Grid.Column="2" Text="{Binding PrimaryEmail}" TextWrapping="Wrap" Margin="5"/>
                        <Button Grid.Column="3" Style="{StaticResource ListViewButtonStyle}" Content="?" Command="{Binding DataContext.EditItemCommand, RelativeSource={RelativeSource AncestorType=Window}}" CommandParameter="{Binding}"/>
                        <Button Grid.Column="4" Style="{StaticResource ListViewButtonStyle}" Content="❌" Command="{Binding DataContext.DeleteItemCommand, RelativeSource={RelativeSource AncestorType=Window}}" CommandParameter="{Binding}"/>
                    </Grid>
                </DataTemplate>
            </ListView.ItemTemplate>
            <ListView.ItemContainerStyle>
                <Style TargetType="ListViewItem">
                    <Setter Property="HorizontalContentAlignment" Value="Stretch" />
                    <Setter Property="MaxWidth" Value="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=ListView}}"/>
                </Style>
            </ListView.ItemContainerStyle>
            <ListView.InputBindings>
                <KeyBinding Key="Return" Command="{Binding DataContext.OpenItemCommand, RelativeSource={RelativeSource AncestorType=Window}}" CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource AncestorType=ListView}}"/>
            </ListView.InputBindings>
        </ListView>
    </Grid>
</Window>

MainViewModel class

public class MainViewModel : NotifyPropertyChanged
{
    // fields containing everything but not use it outside of properties
    private ObservableCollection<Profile> _profilesList;
    private ICommand _deleteItemCommand;
    private ICommand _editItemCommand;
    private ICommand _openItemCommand;

    // properties backed by fields
    public ObservableCollection<Profile> ProfilesList
    {
        get => _profilesList;
        set
        {
            _profilesList = value;
            OnPropertyChanged(); // notify UI
        }
    }

    public ICommand OpenItemCommand => _openItemCommand ?? (_openItemCommand = new RelayCommand(parameter =>
    {
        if (parameter is Profile profile)
        {
            MessageBox.Show(profile.ToString(), "OpenItemCommand");
        }
    }));

    public ICommand DeleteItemCommand => _deleteItemCommand ?? (_deleteItemCommand = new RelayCommand(parameter =>
    {
        if (parameter is Profile profile)
        {
            if (MessageBox.Show(profile.ToString(), "Delete Item?", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No) == MessageBoxResult.Yes)
            {
                ProfilesList.Remove(profile);
            }
        }
    }));

    public ICommand EditItemCommand => _editItemCommand ?? (_editItemCommand = new RelayCommand(parameter =>
    {
        if (parameter is Profile profile)
        {
            MessageBox.Show(profile.ToString(), "EditItemCommand");
        }
    }));

    public MainViewModel()
    {
        // initializing the collection with demo values
        ProfilesList = new ObservableCollection<Profile>
        {
            new Profile { UserName = "John Smith", LinkedInProfileId = "jsmith", PrimaryEmail = "jsmith@example.com" },
            new Profile { UserName = "Jane Doe", LinkedInProfileId = "jane", PrimaryEmail = "jdoe@example.org" },
            new Profile { UserName = "Forrest Gump", LinkedInProfileId = "runningguy", PrimaryEmail = "fgump@example.net" }
        };
    }
}

Вы не показали класс данных. Я сделал свой собственный.

public class Profile : NotifyPropertyChanged
{
    private string _userName;
    private string _linkedInProfileId;
    private string _primaryEmail;
    public string UserName 
    { 
        get => _userName; 
        set
        {
            _userName = value;
            OnPropertyChanged();
        } 
    }
    public string LinkedInProfileId
    {
        get => _linkedInProfileId;
        set
        {
            _linkedInProfileId = value;
            OnPropertyChanged();
        }
    }
    public string PrimaryEmail
    {
        get => _primaryEmail;
        set
        {
            _primaryEmail = value;
            OnPropertyChanged();
        }
    }
    public override string ToString() => UserName + "\r\n" + LinkedInProfileId + "\r\n" + PrimaryEmail;
}

Это все ... кроме класса code-behind. Вот он:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
}

Пример не такой большой, и я даю вам возможность изучить его без моего подробного описания, но добавил несколько комментариев.

Custom ListView

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...