Xử lý Sự kiện Nhấn Enter và Nhấp Đúp trong Ứng dụng WPF C#

Trong quá trình phát triển các ứng dụng desktop với WPF và C#, việc tương tác của người dùng thông qua cả bàn phím và chuột là điều không thể thiếu. Việc tích hợp các sự kiện đặc biệt như nhấn phím Enter hoặc nhấp đúp chuột không chỉ giúp tăng tốc độ làm việc mà còn cải thiện đáng kể trải nghiệm người dùng. Bài viết này sẽ hướng dẫn bạn cách triển khai các sự kiện này một cách hiệu quả.

1. Xử lý Sự kiện Nhấn Phím Enter

Người dùng thường mong muốn có thể di chuyển giữa các trường nhập liệu hoặc kích hoạt một hành động chỉ bằng phím Enter, thay vì phải dùng chuột. Trong WPF, có nhiều cách để thực hiện điều này, tùy thuộc vào ngữ cảnh sử dụng.

1.1. Gán Nút Mặc Định (IsDefault)

Đối với các cửa sổ hoặc hộp thoại, bạn có thể chỉ định một nút là "nút mặc định". Khi người dùng nhấn phím Enter và không có control nào khác đang xử lý sự kiện này (ví dụ: một TextBox đang trong quá trình nhập liệu), sự kiện Click của nút mặc định sẽ được kích hoạt.

Bạn chỉ cần thêm thuộc tính IsDefault="True" vào nút trong XAML:

<Button x:Name="LoginButton" Content="Đăng nhập" IsDefault="True" Click="LoginButton_Click" Width="100" Height="30" Margin="10"/>

1.2. Chuyển Trọng Tâm (Focus) Giữa Các Control

Để tự động chuyển trọng tâm từ control hiện tại sang control tiếp theo khi nhấn Enter, bạn có thể sử dụng sự kiện KeyDown hoặc PreviewKeyDown.

  • KeyDownPreviewKeyDown: Sự kiện PreviewKeyDown được kích hoạt trong giai đoạn "tunneling" (đi từ gốc cây điều khiển xuống) trước khi sự kiện KeyDown được kích hoạt trong giai đoạn "bubbling" (đi từ control đích lên). Điều này có nghĩa là PreviewKeyDown cung cấp cơ hội để chặn hoặc xử lý sự kiện trước khi các control con xử lý nó, rất hữu ích cho các control phức tạp như AutoCompleteBox.

Ví dụ sau đây minh họa cách chuyển trọng tâm giữa các TextBox khi nhấn Enter:

XAML:

<StackPanel Margin="20">
    <TextBox x:Name="UsernameTextBox" Header="Tên người dùng" KeyDown="InputTextBox_KeyDown" Width="200" Margin="0,0,0,10"/>
    <TextBox x:Name="PasswordTextBox" Header="Mật khẩu" KeyDown="InputTextBox_KeyDown" Width="200" Margin="0,0,0,10"/>
    <Button x:Name="SubmitDataButton" Content="Gửi dữ liệu" Width="100" Height="30"/>
</StackPanel>

C# (Code-behind):

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

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

    private void InputTextBox_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Enter)
        {
            // Đánh dấu sự kiện đã được xử lý để ngăn chặn các xử lý tiếp theo
            e.Handled = true; 

            // Chuyển trọng tâm đến control tiếp theo trong thứ tự TabIndex
            // UIElement.MoveFocus sử dụng TraversalRequest để điều hướng trọng tâm
            (sender as UIElement)?.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
        }
    }
}

1.3. Kích Hoạt Hành Động Cụ Thể

Trong một số trường hợp, bạn muốn nhấn Enter để kích hoạt một hành động phức tạp hơn, ví dụ như tìm kiếm dữ liệu hoặc gọi một phương thức xử lý sự kiện Click của một nút khác. Sử dụng PreviewKeyDown là lựa chọn tốt để đảm bảo sự kiện được bắt kịp thời.

XAML:

<StackPanel Margin="20">
    <TextBox x:Name="SearchInputField"
             Header="Nhập từ khóa tìm kiếm"
             PreviewKeyDown="SearchInputField_PreviewKeyDown"
             Width="300" Margin="0,0,0,10"/>
    
    <Button x:Name="ExecuteSearchButton"
            Content="Tìm kiếm"
            Click="ExecuteSearchButton_Click"
            Width="120" Margin="0,0,0,5"/>
    
    <Button x:Name="SelectResultButton"
            Content="Chọn kết quả"
            Click="SelectResultButton_Click"
            Width="120"
            Visibility="Collapsed"/> <!-- Ban đầu ẩn -->
</StackPanel>

C# (Code-behind):

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

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

    private void SearchInputField_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Enter)
        {
            e.Handled = true; // Ngăn chặn sự kiện Enter được xử lý bởi control khác
            if (ExecuteSearchButton.Visibility == Visibility.Visible)
            {
                ExecuteSearchButton_Click(sender, e); // Kích hoạt nút tìm kiếm
            }
            else if (SelectResultButton.Visibility == Visibility.Visible)
            {
                SelectResultButton_Click(sender, e); // Kích hoạt nút chọn kết quả
            }
        }
    }

    private void ExecuteSearchButton_Click(object sender, RoutedEventArgs e)
    {
        MessageBox.Show($"Đang tìm kiếm: {SearchInputField.Text}");
        // Sau khi tìm kiếm, có thể hiển thị nút SelectResultButton
        SelectResultButton.Visibility = Visibility.Visible;
    }

    private void SelectResultButton_Click(object sender, RoutedEventArgs e)
    {
        MessageBox.Show($"Đã chọn kết quả: {SearchInputField.Text}");
    }
}

2. Xử lý Sự kiện Nhấp Đúp Chuột

Việc nhấp đúp vào một mục trong danh sách (như ListView hoặc DataGrid) để xem chi tiết hoặc thực hiện một hành động là một mô hình tương tác phổ biến. Trong WPF, bạn có thể dễ dàng thêm sự kiện nhấp đúp vào các mục trong danh sách bằng cách sử dụng EventSetter trong Style.

XAML:

<Window.Resources>
    <!-- Định nghĩa Style cho ListViewItem để thêm sự kiện nhấp đúp -->
    <Style x:Key="ClickableListViewItemStyle" TargetType="{x:Type ListViewItem}">
        <EventSetter Event="MouseDoubleClick" Handler="OnListViewItemDoubleClick" />
    </Style>
</Window.Resources>

<Grid>
    <ListView x:Name="ProductListView" ItemContainerStyle="{StaticResource ClickableListViewItemStyle}" Margin="20">
        <ListView.View>
            <GridView>
                <GridViewColumn Header="Mã sản phẩm" DisplayMemberBinding="{Binding ProductCode}"/>
                <GridViewColumn Header="Tên sản phẩm" DisplayMemberBinding="{Binding ProductName}"/>
                <GridViewColumn Header="Giá" DisplayMemberBinding="{Binding Price}"/>
            </GridView>
        </ListView.View>
    </ListView>
</Grid>

C# (Code-behind):

using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

public partial class MyWindow : Window
{
    public MyWindow()
    {
        InitializeComponent();
        PopulateProductListView();
    }

    // Lớp dữ liệu mẫu
    public class Product
    {
        public string ProductCode { get; set; }
        public string ProductName { get; set; }
        public decimal Price { get; set; }
    }

    private void PopulateProductListView()
    {
        // Thêm dữ liệu giả vào ListView
        ProductListView.ItemsSource = new List<Product>
        {
            new Product { ProductCode = "P001", ProductName = "Laptop ABC", Price = 1200.00M },
            new Product { ProductCode = "P002", ProductName = "Điện thoại XYZ", Price = 800.50M },
            new Product { ProductCode = "P003", ProductName = "Bàn phím Gaming", Price = 150.99M }
        };
    }

    protected void OnListViewItemDoubleClick(object sender, MouseButtonEventArgs e)
    {
        // Đảm bảo sự kiện được kích hoạt từ một ListViewItem hợp lệ và có dữ liệu
        if (sender is ListViewItem item && item.IsSelected)
        {
            // Lấy đối tượng dữ liệu tương ứng với item đã nhấp đúp
            if (ProductListView.SelectedItem is Product selectedProduct)
            {
                MessageBox.Show($"Bạn đã nhấp đúp vào sản phẩm: {selectedProduct.ProductName} (Mã: {selectedProduct.ProductCode})");
                // Tại đây, bạn có thể mở một cửa sổ chi tiết sản phẩm hoặc thực hiện hành động khác.
            }
        }
    }
}

Thẻ: C# WPF Event Handling Keyboard Events Mouse events

Đăng vào ngày 14 tháng 7 lúc 18:36