DataGrid Ctrl + F and F3 functionality WPF MVVM

StashyCode 61 Reputation points
2021-11-03T07:25:00.673+00:00

I am building a WPF MVVM application.

I have a DataGrid, like so:

<DataGrid 
       Name=“Employees” 
       AutoGenerateColumns=“False”         
       CanUserAddRows=“False” 
       EnableColumnVirtualization=“True” 
       EnableRowVirtualization=“True” 
       ItemsSource={Binding EmployeesCollectionView} 
       SelectionUnit=“FullRow”        
       VirtualizingPanel.VirtualizationMode=“Recycling” /> 

I want to implement a Ctrl+F and F3 search (similar like the browser one).

What is the best way to implement that?

Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,784 questions
XAML
XAML
A language based on Extensible Markup Language (XML) that enables developers to specify a hierarchy of objects with a set of properties and logic.
814 questions
0 comments No comments
{count} votes

Accepted answer
  1. Peter Fleischer (former MVP) 19,326 Reputation points
    2021-11-03T08:47:20.45+00:00

    Hi Stashy,
    try following demo (MVVM with Interactivity).

    XAML Main Window:

    <Window x:Class="Window097"  
            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:WpfApp1.WpfApp097"  
            xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"  
            mc:Ignorable="d"  
            Title="211103_StashyCode-7539" Height="450" Width="800">  
      <Window.DataContext>  
        <local:ViewModel/>  
      </Window.DataContext>  
      <StackPanel>  
        <DataGrid Name="Employees" Height="200"  
                  AutoGenerateColumns="True"  
                  CanUserAddRows="False"  
                  EnableColumnVirtualization="True"   
                  EnableRowVirtualization="True"  
                  ItemsSource="{Binding EmployeesDataTable}"  
                  SelectionUnit="CellOrRowHeader"  
                  VirtualizingPanel.VirtualizationMode="Recycling">  
          <i:Interaction.Behaviors>  
            <local:DataGridBehavior/>  
          </i:Interaction.Behaviors>  
        </DataGrid>  
        <Label Content="{Binding Info}"/>  
      </StackPanel>  
    </Window>  
    

    and classes:

    Imports System.Collections.ObjectModel  
    Imports System.ComponentModel  
    Imports System.Runtime.CompilerServices  
    Imports System.Windows.Interactivity  
      
    Namespace WpfApp097  
      Public Class ViewModel  
        Implements INotifyPropertyChanged  
      
        Public Sub New()  
          ' Load demo data '  
          For i = 1 To 10  
            col.Add(New Data With {.ID = i, .Name = $"Row {i}"})  
          Next  
          cvs.Source = col  
        End Sub  
      
        Private col As New ObservableCollection(Of Data)  
        Private cvs As New CollectionViewSource  
      
        Public ReadOnly Property EmployeesDataTable As ICollectionView  
          Get  
            Return cvs.View  
          End Get  
        End Property  
      
        Private _info As String  
        Public Property Info As String  
          Get  
            Return Me._info  
          End Get  
          Set(value As String)  
            Me._info = value  
            OnPropChanged()  
          End Set  
        End Property  
      
        Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged  
        Private Sub OnPropChanged(<CallerMemberName> Optional propName As String = "")  
          RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propName))  
        End Sub  
      End Class  
      
      Public Class Data  
        Public Property ID As Integer  
        Public Property Name As String  
      End Class  
      
      Public Class DataGridBehavior  
        Inherits Behavior(Of DataGrid)  
        Protected Overrides Sub OnAttached()  
          AddHandler AssociatedObject.PreviewKeyDown, AddressOf OnPreviewKeyDown  
          AddHandler AssociatedObject.PreviewKeyUp, AddressOf OnPreviewKeyUp  
        End Sub  
      
        Private Sub OnPreviewKeyDown(sender As Object, e As KeyEventArgs)  
          If e.Key = Key.F3 Then  
            CType(AssociatedObject.DataContext, ViewModel).Info = "Pressed F3"  
          ElseIf e.Key = Key.LeftCtrl Then  
            LeftCtrl = True  
          ElseIf e.Key = Key.F Then  
            CType(AssociatedObject.DataContext, ViewModel).Info = "Pressed ctrl left + F"  
          End If  
        End Sub  
      
        Private Sub OnPreviewKeyUp(sender As Object, e As KeyEventArgs)  
          If e.Key = Key.LeftCtrl Then  
            LeftCtrl = False  
          End If  
        End Sub  
      
        Private LeftCtrl As Boolean = False  
      
      End Class  
      
    End Namespace  
    

    Result:

    146132-x.gif

    The same code in C#:

    <Window x:Class="WpfApp1.Window079"  
            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:Wpf1App079"  
            xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"  
            mc:Ignorable="d"  
            Title="211103_StashyCode-7539" Height="450" Width="800">  
      <Window.DataContext>  
        <local:ViewModel/>  
      </Window.DataContext>  
      <StackPanel>  
        <DataGrid Name="Employees" Height="200"  
                  AutoGenerateColumns="True"  
                  CanUserAddRows="False"  
                  EnableColumnVirtualization="True"   
                  EnableRowVirtualization="True"  
                  ItemsSource="{Binding EmployeesDataTable}"  
                  SelectionUnit="CellOrRowHeader"  
                  VirtualizingPanel.VirtualizationMode="Recycling">  
          <i:Interaction.Behaviors>  
            <local:DataGridBehavior/>  
          </i:Interaction.Behaviors>  
        </DataGrid>  
        <Label Content="{Binding Info}"/>  
      </StackPanel>  
    </Window>  
      
      
    using System.Collections.ObjectModel;  
    using System.ComponentModel;  
    using System.Runtime.CompilerServices;  
    using System.Windows;  
    using System.Windows.Controls;  
    using System.Windows.Data;  
    using System.Windows.Input;  
    using System.Windows.Interactivity;  
      
    namespace Wpf1App079  
    {  
      public class ViewModel : INotifyPropertyChanged  
      {  
      
        public ViewModel()  
        {  
          // Load demo data  
          for (int i = 0; i < 10; i++) col.Add(new Data() { ID = i, Name = $"Row {i}" });  
          cvs.Source = col;  
        }  
      
        private CollectionViewSource cvs = new CollectionViewSource();  
        private ObservableCollection<Data> col = new ObservableCollection<Data>();  
      
        public ICollectionView EmployeesDataTable { get => cvs.View; }  
      
        private string _info;  
        public string Info  
        {  
          get => this._info;  
          set { this._info = value; OnPropertyChanged(); }  
        }  
      
        public event PropertyChangedEventHandler PropertyChanged;  
        private void OnPropertyChanged([CallerMemberName] string propName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));  
      }  
      
      public class Data  
      {  
        public int ID { get; set; }  
        public string Name { get; set; }  
      }  
      
      public class DataGridBehavior : Behavior<DataGrid>  
      {  
        protected override void OnAttached()  
        {  
          AssociatedObject.PreviewKeyDown += OnPreviewKeyDown;  
          AssociatedObject.PreviewKeyUp += OnPreviewKeyUp;  
        }  
      
        private void OnPreviewKeyDown(object sender, KeyEventArgs e)  
        {  
          if (e.Key == Key.F3) ((ViewModel)(AssociatedObject.DataContext)).Info = "Pressed F3";  
          else if (e.Key == Key.LeftCtrl) LeftCtrl = true;  
          else if (e.Key == Key.F) ((ViewModel)(AssociatedObject.DataContext)).Info = "Pressed ctrl left + F";  
        }  
      
        private void OnPreviewKeyUp(object sender, KeyEventArgs e) { if (e.Key == Key.LeftCtrl) LeftCtrl = false; }  
      
        private bool LeftCtrl = false;  
      
      }  
    }  
    
    1 person found this answer helpful.

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.