How to implement Text Search in CefSharp
Asked Answered
K

2

5

I'm building an application using CefSharp and need to provide text search functionality to user just like Google Chrome has.

Can any one help me with the implementation of text search in CefSharp?

Klein answered 9/3, 2016 at 6:30 Comment(0)
P
7

I've built this demo application using CefSharp 47.0.3, hopefully this is what you're looking for.

The view:

<Window x:Class="CefSharpSearchDemo.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:wpf="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:cefSharpSearchDemo="clr-namespace:CefSharpSearchDemo"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525"
        d:DataContext="{d:DesignInstance {x:Type cefSharpSearchDemo:MainWindowViewModel}}">
    <DockPanel>
        <DockPanel DockPanel.Dock="Top">
            <Button Content="Next" DockPanel.Dock="Right" Command="{Binding ElementName=SearchBehavior, Path=NextCommand}" />
            <Button Content="Previous" DockPanel.Dock="Right" Command="{Binding ElementName=SearchBehavior, Path=PreviousCommand}"  />
            <TextBox DockPanel.Dock="Right" Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}"></TextBox>
        </DockPanel>

        <wpf:ChromiumWebBrowser x:Name="wb" DockPanel.Dock="Bottom"
                                Address="http://stackoverflow.com">
            <i:Interaction.Behaviors>
                <cefSharpSearchDemo:ChromiumWebBrowserSearchBehavior x:Name="SearchBehavior" SearchText="{Binding SearchText}" />
            </i:Interaction.Behaviors>
        </wpf:ChromiumWebBrowser>
    </DockPanel>
</Window>

The code-behind for the view:

namespace CefSharpSearchDemo
{
    using System.Windows;

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            DataContext = new MainWindowViewModel();
        }
    }
}

The view model:

namespace CefSharpSearchDemo
{
    using System.ComponentModel;
    using System.Runtime.CompilerServices;

    public class MainWindowViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private string _searchText;

        public string SearchText
        {
            get { return _searchText; }
            set
            {
                _searchText = value;
                NotifyPropertyChanged();
            }
        }

        protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

And now the important part. As you could see in the view there is a behavior attached to the ChromiumWebBrowser:

namespace CefSharpSearchDemo
{
    using System.Windows;
    using System.Windows.Input;
    using System.Windows.Interactivity;
    using CefSharp;
    using CefSharp.Wpf;

    public class ChromiumWebBrowserSearchBehavior : Behavior<ChromiumWebBrowser>
    {
        private bool _isSearchEnabled;

        public ChromiumWebBrowserSearchBehavior()
        {
            NextCommand = new DelegateCommand(OnNext);
            PreviousCommand = new DelegateCommand(OnPrevious);
        }

        private void OnNext()
        {
            AssociatedObject.Find(identifier: 1, searchText: SearchText, forward: true, matchCase: false, findNext: true);
        }

        private void OnPrevious()
        {
            AssociatedObject.Find(identifier: 1, searchText: SearchText, forward: false, matchCase: false, findNext: true);
        }

        protected override void OnAttached()
        {
            AssociatedObject.FrameLoadEnd += ChromiumWebBrowserOnFrameLoadEnd;
        }

        private void ChromiumWebBrowserOnFrameLoadEnd(object sender, FrameLoadEndEventArgs frameLoadEndEventArgs)
        {
            _isSearchEnabled = frameLoadEndEventArgs.Frame.IsMain;

            Dispatcher.Invoke(() =>
            {
                if (_isSearchEnabled && !string.IsNullOrEmpty(SearchText))
                {
                    AssociatedObject.Find(1, SearchText, true, false, false);
                }
            });
        }

        public static readonly DependencyProperty SearchTextProperty = DependencyProperty.Register(
            "SearchText", typeof(string), typeof(ChromiumWebBrowserSearchBehavior), new PropertyMetadata(default(string), OnSearchTextChanged));

        public string SearchText
        {
            get { return (string)GetValue(SearchTextProperty); }
            set { SetValue(SearchTextProperty, value); }
        }

        public static readonly DependencyProperty NextCommandProperty = DependencyProperty.Register(
            "NextCommand", typeof (ICommand), typeof (ChromiumWebBrowserSearchBehavior), new PropertyMetadata(default(ICommand)));

        public ICommand NextCommand
        {
            get { return (ICommand) GetValue(NextCommandProperty); }
            set { SetValue(NextCommandProperty, value); }
        }

        public static readonly DependencyProperty PreviousCommandProperty = DependencyProperty.Register(
            "PreviousCommand", typeof (ICommand), typeof (ChromiumWebBrowserSearchBehavior), new PropertyMetadata(default(ICommand)));

        public ICommand PreviousCommand
        {
            get { return (ICommand) GetValue(PreviousCommandProperty); }
            set { SetValue(PreviousCommandProperty, value); }
        }

        private static void OnSearchTextChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
        {
            var behavior = dependencyObject as ChromiumWebBrowserSearchBehavior;

            if (behavior != null && behavior._isSearchEnabled)
            {
                var newSearchText = dependencyPropertyChangedEventArgs.NewValue as string;

                if (string.IsNullOrEmpty(newSearchText))
                {
                    behavior.AssociatedObject.StopFinding(true);
                }
                else
                {
                    behavior.AssociatedObject.Find(1, newSearchText, true, false, false);
                }
            }
        }

        protected override void OnDetaching()
        {
            AssociatedObject.FrameLoadEnd -= ChromiumWebBrowserOnFrameLoadEnd;
        }
    }
}

And the minor additional code for the DelegateCommand:

namespace CefSharpSearchDemo
{
    using System;
    using System.Windows.Input;

    public class DelegateCommand : ICommand
    {
        private readonly Action _action;

        public DelegateCommand(Action action)
        {
            _action = action;
        }

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public void Execute(object parameter)
        {
            _action();
        }

        public event EventHandler CanExecuteChanged;
    }
}

The resulting application has a TextBox on the top and two buttons labeled "Previous" and "Next" next to it.

The main area is a CefSharp browser which loads http://www.stackoverflow.com.

You can type into the TextBox and it will search in the browser (and highlight the scrollbar where the hits are, just like in Chrome). You can then press the Next/Previous buttons to cycle through the hits.

I hope this helps in developing your own solution.

All this said, let me just note that next time if you ask a question, actually provide some code what you tried, or try to ask a more specific question, because this is probably too broad for this site. Anyway I leave this here, maybe others will find it useful as well.

Important lesson: there are some methods exposed on ChromiumWebBrowser that you can use to implement the search functionality (namely: Find and StopFinding).

Phosphoric answered 9/3, 2016 at 20:31 Comment(0)
P
9

You can do it simple just add two buttons and a textbox on your form. first buttons for next result,second buttons for previous result and textbox for search text provider.

On textbox's KeyUp event run below code

if (tosBrowserSearchTxt.Text.Length <= 0)
{
    //this will clear all search result
    webBrowserChromium.StopFinding(true);
}
else
{
    webBrowserChromium.Find(0, tosBrowserSearchTxt.Text, true, false,false);
}

On next button click run below code

webBrowserChromium.Find(0, tosBrowserSearchTxt.Text, true, false, false); 

On previous button click run below code

webBrowserChromium.Find(0, tosBrowserSearchTxt.Text, false, false, false);

When user type any character into text box KeyUp event's code will search that text, by using next and previous button you can navigate from one result to another.

Peanut answered 11/3, 2016 at 11:21 Comment(0)
P
7

I've built this demo application using CefSharp 47.0.3, hopefully this is what you're looking for.

The view:

<Window x:Class="CefSharpSearchDemo.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:wpf="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:cefSharpSearchDemo="clr-namespace:CefSharpSearchDemo"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525"
        d:DataContext="{d:DesignInstance {x:Type cefSharpSearchDemo:MainWindowViewModel}}">
    <DockPanel>
        <DockPanel DockPanel.Dock="Top">
            <Button Content="Next" DockPanel.Dock="Right" Command="{Binding ElementName=SearchBehavior, Path=NextCommand}" />
            <Button Content="Previous" DockPanel.Dock="Right" Command="{Binding ElementName=SearchBehavior, Path=PreviousCommand}"  />
            <TextBox DockPanel.Dock="Right" Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}"></TextBox>
        </DockPanel>

        <wpf:ChromiumWebBrowser x:Name="wb" DockPanel.Dock="Bottom"
                                Address="http://stackoverflow.com">
            <i:Interaction.Behaviors>
                <cefSharpSearchDemo:ChromiumWebBrowserSearchBehavior x:Name="SearchBehavior" SearchText="{Binding SearchText}" />
            </i:Interaction.Behaviors>
        </wpf:ChromiumWebBrowser>
    </DockPanel>
</Window>

The code-behind for the view:

namespace CefSharpSearchDemo
{
    using System.Windows;

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            DataContext = new MainWindowViewModel();
        }
    }
}

The view model:

namespace CefSharpSearchDemo
{
    using System.ComponentModel;
    using System.Runtime.CompilerServices;

    public class MainWindowViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private string _searchText;

        public string SearchText
        {
            get { return _searchText; }
            set
            {
                _searchText = value;
                NotifyPropertyChanged();
            }
        }

        protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

And now the important part. As you could see in the view there is a behavior attached to the ChromiumWebBrowser:

namespace CefSharpSearchDemo
{
    using System.Windows;
    using System.Windows.Input;
    using System.Windows.Interactivity;
    using CefSharp;
    using CefSharp.Wpf;

    public class ChromiumWebBrowserSearchBehavior : Behavior<ChromiumWebBrowser>
    {
        private bool _isSearchEnabled;

        public ChromiumWebBrowserSearchBehavior()
        {
            NextCommand = new DelegateCommand(OnNext);
            PreviousCommand = new DelegateCommand(OnPrevious);
        }

        private void OnNext()
        {
            AssociatedObject.Find(identifier: 1, searchText: SearchText, forward: true, matchCase: false, findNext: true);
        }

        private void OnPrevious()
        {
            AssociatedObject.Find(identifier: 1, searchText: SearchText, forward: false, matchCase: false, findNext: true);
        }

        protected override void OnAttached()
        {
            AssociatedObject.FrameLoadEnd += ChromiumWebBrowserOnFrameLoadEnd;
        }

        private void ChromiumWebBrowserOnFrameLoadEnd(object sender, FrameLoadEndEventArgs frameLoadEndEventArgs)
        {
            _isSearchEnabled = frameLoadEndEventArgs.Frame.IsMain;

            Dispatcher.Invoke(() =>
            {
                if (_isSearchEnabled && !string.IsNullOrEmpty(SearchText))
                {
                    AssociatedObject.Find(1, SearchText, true, false, false);
                }
            });
        }

        public static readonly DependencyProperty SearchTextProperty = DependencyProperty.Register(
            "SearchText", typeof(string), typeof(ChromiumWebBrowserSearchBehavior), new PropertyMetadata(default(string), OnSearchTextChanged));

        public string SearchText
        {
            get { return (string)GetValue(SearchTextProperty); }
            set { SetValue(SearchTextProperty, value); }
        }

        public static readonly DependencyProperty NextCommandProperty = DependencyProperty.Register(
            "NextCommand", typeof (ICommand), typeof (ChromiumWebBrowserSearchBehavior), new PropertyMetadata(default(ICommand)));

        public ICommand NextCommand
        {
            get { return (ICommand) GetValue(NextCommandProperty); }
            set { SetValue(NextCommandProperty, value); }
        }

        public static readonly DependencyProperty PreviousCommandProperty = DependencyProperty.Register(
            "PreviousCommand", typeof (ICommand), typeof (ChromiumWebBrowserSearchBehavior), new PropertyMetadata(default(ICommand)));

        public ICommand PreviousCommand
        {
            get { return (ICommand) GetValue(PreviousCommandProperty); }
            set { SetValue(PreviousCommandProperty, value); }
        }

        private static void OnSearchTextChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
        {
            var behavior = dependencyObject as ChromiumWebBrowserSearchBehavior;

            if (behavior != null && behavior._isSearchEnabled)
            {
                var newSearchText = dependencyPropertyChangedEventArgs.NewValue as string;

                if (string.IsNullOrEmpty(newSearchText))
                {
                    behavior.AssociatedObject.StopFinding(true);
                }
                else
                {
                    behavior.AssociatedObject.Find(1, newSearchText, true, false, false);
                }
            }
        }

        protected override void OnDetaching()
        {
            AssociatedObject.FrameLoadEnd -= ChromiumWebBrowserOnFrameLoadEnd;
        }
    }
}

And the minor additional code for the DelegateCommand:

namespace CefSharpSearchDemo
{
    using System;
    using System.Windows.Input;

    public class DelegateCommand : ICommand
    {
        private readonly Action _action;

        public DelegateCommand(Action action)
        {
            _action = action;
        }

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public void Execute(object parameter)
        {
            _action();
        }

        public event EventHandler CanExecuteChanged;
    }
}

The resulting application has a TextBox on the top and two buttons labeled "Previous" and "Next" next to it.

The main area is a CefSharp browser which loads http://www.stackoverflow.com.

You can type into the TextBox and it will search in the browser (and highlight the scrollbar where the hits are, just like in Chrome). You can then press the Next/Previous buttons to cycle through the hits.

I hope this helps in developing your own solution.

All this said, let me just note that next time if you ask a question, actually provide some code what you tried, or try to ask a more specific question, because this is probably too broad for this site. Anyway I leave this here, maybe others will find it useful as well.

Important lesson: there are some methods exposed on ChromiumWebBrowser that you can use to implement the search functionality (namely: Find and StopFinding).

Phosphoric answered 9/3, 2016 at 20:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.