繁体   English   中英

如何处理 C# 中的 WPF 数据网格单元格上的鼠标单击,这取决于单击的行和列

[英]How to handle a mouse click on a WPF datagrid cell in C# that depends on the row and column clicked

这是我的 WPF 数据网格的图片: 在此处输入图像描述 它是用这个 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 页面的打开,以根据单击的特定单元格查看或编辑与所选行有关的详细信息。 我已经广泛搜索,但无法弄清楚如何捕获点击事件并使用点击的单元格来了解要加载哪些用户详细信息。

这可能吗? 想法非常受欢迎。

这可以使用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就是一种重冗余。 并向您展示我使用ListView玩过的各种 WPF 功能。

在 MVVM 中,您不需要与 C# 代码中的控件进行任何交互。 因此我没有给控件Name 要更改某些内容,只需更改数据,控件将通过BindingOnPropertyChanged()调用自动更新。

完整标记以使一切清晰。

<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

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

您没有显示数据 class。 我自己做了。

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

就是这样……除了代码隐藏 class。 这里是:

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

这个例子不是很大,我给你一个机会去探索它,没有我的详细描述,但添加了一些评论。

自定义列表视图

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM