ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [WPF WebBrowser] C#에서 자바스크립트 함수를 호출하거나 자바스크립트에서 C# 메소드를 호출하는 방법
    닷넷/WPF 2023. 3. 20. 16:50
    반응형

     

    2022.08.03 - [C#/WinForm] - WebBrowser 자바스크립트에서 C# 메소드를 호출하는 방법

     

    WebBrowser 자바스크립트에서 C# 메소드를 호출하는 방법

    namespace WindowsFormsApplication6 { // This first namespace is required for the ComVisible attribute used on the ScriptManager class. using System.Runtime.InteropServices; using System.Windows.Forms; // This is your form. public partial class Form1 : Form

    jasmintime.com

     

    WPF에서는 뷰와 뷰모델을 분리하여 호출하는 방법에 대해 기술합니다.

     

    1. 프로젝트를 생성합니다.

    닷넷프레임워크 버전은 WPF 앱(.NET Framework) 선택

    닷넷5, 6, 7 버전은 WPF 애플리케이션 선택

     

    2. [도구]-[NuGet 패키지 관리자]-[솔루션용 NuGet 패키지 관리]에 들어갑니다.

     

    3. Microsoft.XamlBehaviors.Wpf를 검색하여 설치합니다.

    참고로 Microsoft.XamlBehaviors.Wpf는 닷넷프레임워크의 경우 4.5이상부터 되므로 닷넷프레임워크 4.0이면 System.Windows.Interactivity를 검색하여 설치합니다.

     

    4. 사용자 정의 컨트롤(UserControl)를 추가합니다.

     

    UCWebBrowser 명으로 추가하였습니다.

     

    5. UCWebBrowser.xaml

    <UserControl x:Class="WPF_Webbrowser.UCWebbrowser"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 mc:Ignorable="d" 
                 d:DesignHeight="450" d:DesignWidth="800">
        <Grid>
            <WebBrowser x:Name="WebBrowserMain" Navigating="WebBrowserMain_Navigating" />
        </Grid>
    </UserControl>

     

    6. UCWebBrowser.xaml.cs

    using Microsoft.Win32;
    using System;
    using System.Reflection;
    using System.Security.Permissions;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Navigation;
    
    namespace WPF_Webbrowser
    {
        /// <summary>
        /// UCWebbrowser.xaml에 대한 상호 작용 논리
        /// </summary>
        public partial class UCWebbrowser : UserControl
        {
            public event EventHandler<MyEventArgs> OnGetJavastriptText;
    
            /// <summary>
            /// Map Url이 로드 되었는지 여부 입니다.
            /// </summary>
            public bool IsMapLoaded
            {
                get { return (bool)GetValue(IsMapLoadedProperty); }
                set { SetValue(IsMapLoadedProperty, value); }
            }
    
            /// <summary>
            /// WebBrowser에서 표시할 Map Url 입니다.
            /// </summary>
            public string MapUrl
            {
                get { return (string)GetValue(MapUrlProperty); }
                set { SetValue(MapUrlProperty, value); }
            }
    
            /// <summary>
            /// WebBrowser에서 실행할 Script 입니다. 할당 될 때마다 실행합니다.
            /// </summary>
            public string ExecuteScript
            {
                get { return (string)GetValue(ExecuteScriptProperty); }
                set { SetValue(ExecuteScriptProperty, value); }
            }
    
            /// <summary>
            /// WebBrowser에서 전달되는 데이터를 받기 위한 것 입니다.
            /// </summary>
            public MyEventArgs MyEventArgs
            {
                get { return (MyEventArgs)GetValue(MyEventArgsProperty); }
                set { SetValue(MyEventArgsProperty, value); }
            }
    
            public static readonly DependencyProperty IsMapLoadedProperty =
                DependencyProperty.Register("IsMapLoaded", typeof(bool), typeof(UCWebbrowser), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
    
            public static readonly DependencyProperty MapUrlProperty =
                DependencyProperty.Register("MapUrl", typeof(string), typeof(UCWebbrowser), new PropertyMetadata(string.Empty, MapUrlChanged));
    
            public static readonly DependencyProperty ExecuteScriptProperty =
                DependencyProperty.Register(
                    "ExecuteScript",
                    typeof(string),
                    typeof(UCWebbrowser),
                    new PropertyMetadata(ExecuteScriptChanged));
    
            public static readonly DependencyProperty MyEventArgsProperty =
                DependencyProperty.Register("MyEventArgs", typeof(MyEventArgs), typeof(UCWebbrowser), new PropertyMetadata(null));
    
            private static void MapUrlChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var control = d as UCWebbrowser;
    
                if (e.NewValue == null || string.IsNullOrEmpty(e.NewValue.ToString()))
                {
                    return;
                }
    
                control.WebBrowserMain.Navigate(e.NewValue.ToString());
            }
    
            private static void ExecuteScriptChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var control = d as UCWebbrowser;
    
                if (e.NewValue == null)
                {
                    return;
                }
    
                if (e.NewValue != null && string.IsNullOrEmpty(e.NewValue.ToString()))
                {
                    return;
                }
    
                if (control.IsMapLoaded)
                {
                    control.ExecuteJavaScript(control.WebBrowserMain, e.NewValue.ToString());
                }
            }
    
            public JavaScriptFuntion javaScriptFuntion;
    
            public UCWebbrowser()
            {
                InitializeComponent();
                SetUserAgent();
                javaScriptFuntion = new JavaScriptFuntion();
                javaScriptFuntion.OnMessageBoxShow += JavaScriptFuntion_OnMessageBoxShow;
                WebBrowserMain.ObjectForScripting = javaScriptFuntion;
            }
    
            private void WebBrowserMain_Navigating(object sender, NavigatingCancelEventArgs e)
            {
                IsMapLoaded = true;
                dynamic activeX = WebBrowserMain.GetType().InvokeMember("ActiveXInstance", BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.NonPublic
                                                                        , null, WebBrowserMain, new object[] { });
    
                activeX.Silent = true;
            }
    
    
            private void JavaScriptFuntion_OnMessageBoxShow(object sender, EventArgs e)
            {
                var data = sender.ToString();
                MyEventArgs = new MyEventArgs(data);
                OnGetJavastriptText?.Invoke(this, MyEventArgs);
            }
    
            /// <summary>
            /// WebBrowser에서 스크립트를 실행합니다.
            /// </summary>
            /// <param name="web"></param>
            /// <param name="script"></param>
            private void ExecuteJavaScript(WebBrowser web, string script)
            {
                try
                {
                    Console.WriteLine("ExecuteJavaScript : {0}", script);
                    Task.Factory.StartNew(delegate
                    {
                        Application.Current.Dispatcher.Invoke((Action)delegate
                        {
                            web.InvokeScript("execScript", new object[] { script, "JavaScript" });
                        });
                    });
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.ToString());
                }
            }
    
            private void UserControl_Unloaded(object sender, RoutedEventArgs e)
            {
                javaScriptFuntion.OnMessageBoxShow -= JavaScriptFuntion_OnMessageBoxShow;
                WebBrowserMain.Dispose();
            }
    
            /// <summary>
            /// WebBrowser가 사용하는 IE 엔진이 구버전일 때 스크립트 오류 발생 방지를 위한
            /// WebBrowser가 사용하는 IE 엔진이 최신 버전으로 갱신될 수 있게 UserAgent 속성을 설정
            /// </summary>
            private void SetUserAgent()
            {
                string regPath = @"SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION";
                string programName = System.Diagnostics.Process.GetCurrentProcess().ProcessName + ".exe";
                try
                {
                    using (var regKey = Registry.CurrentUser.OpenSubKey(regPath, true))
                    {
                        using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Internet Explorer"))
                        {
                            string version = key.GetValue("svcVersion").ToString();
                            int ieVersion = 0;
    
                            if (version.StartsWith("11."))
                            {
                                ieVersion = 11001;
                            }
                            else if (version.StartsWith("10."))
                            {
                                ieVersion = 10001;
                            }
                            else if (version.StartsWith("9."))
                            {
                                ieVersion = 9999;
                            }
                            else
                            {
                                Console.WriteLine("현재 시스템에는 웹브라우저를 업그레이드 할 수 없습니다.");
                            }
    
                            // 키가 이미 등록되어 있는지 확인 후 등록
                            if (regKey.GetValue(programName) == null && ieVersion > 0)
                            {
                                regKey.SetValue(programName, ieVersion, RegistryValueKind.DWord);
                            }
    
                            key.Close();
                        }
    
                        regKey.Close();
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }
    
        /// <summary>
        /// 자바스크립트에서 실행 가능한 C#의 메소드를 정의
        /// </summary>
        [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
        [System.Runtime.InteropServices.ComVisible(true)]
        public class JavaScriptFuntion
        {
            public event EventHandler OnMessageBoxShow;
    
            public void MessageBoxShow(string text)
            {
                if (string.IsNullOrEmpty(text))
                {
                    text = "";
                }
                OnMessageBoxShow?.Invoke(text, null);
            }
        }
    
        public class MyEventArgs : EventArgs
        {
            public string Data { get; set; }
    
            public MyEventArgs(string data)
            {
                Data = data;
            }
        }
    }

     

    7. MainWindow.xaml

    <Window x:Class="WPF_Webbrowser.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:local="clr-namespace:WPF_Webbrowser"
            xmlns:behaviors="http://schemas.microsoft.com/xaml/behaviors"
            xmlns:vm="clr-namespace:WPF_Webbrowser.ViewModels"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
        <Window.DataContext>
            <vm:MainViewModel/>
        </Window.DataContext>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
    
            <TextBox Width="245" Height="30" HorizontalAlignment="Left" Text="{Binding InputText, UpdateSourceTrigger=LostFocus}" />
            <Button Grid.Column="1" Width="300" Height="30" HorizontalAlignment="Center" Command="{Binding ButtonCommand}" CommandParameter="ToJavaStript" Content="javascript의 helloWorld 함수 호출" />
            <local:UCWebbrowser x:Name="WebBrowserMain" Grid.Row="1" MapUrl="{Binding MapUrl}" ExecuteScript="{Binding ExecuteScript}" >
                <behaviors:Interaction.Triggers>
                    <behaviors:EventTrigger EventName="OnGetJavastriptText">
                        <behaviors:InvokeCommandAction Command="{Binding DataContext.MyEventCommand, RelativeSource={RelativeSource AncestorType=UserControl}}" 
                                                        CommandParameter="{Binding ElementName=WebBrowserMain, Path=MyEventArgs}" />
                    </behaviors:EventTrigger>
                </behaviors:Interaction.Triggers>
            </local:UCWebbrowser>
        </Grid>
    </Window>

     

    8. MainViewModel.cs

    using System;
    using System.Windows;
    using System.Windows.Input;
    using WPF_Webbrowser.Models;
    
    namespace WPF_Webbrowser.ViewModels
    {
        public class MainViewModel : NotifyPropertyChanged
        {
            private ICommand _ButtonCommand;
            private ICommand _MyEventCommand;
            private string _MapUrl;
            private string _ExecuteScript;
            private string _InputText;
    
            public string MapUrl
            {
                get { return _MapUrl; }
                set
                {
                    _MapUrl = value;
                    OnPropertyChanged();
                }
            }
    
            public string ExecuteScript
            {
                get { return _ExecuteScript; }
                set
                {
                    _ExecuteScript = value;
                    OnPropertyChanged();
                }
            }
    
            public string InputText
            {
                get { return _InputText; }
                set
                {
                    _InputText = value;
                    OnPropertyChanged();
                }
            }
    
            public ICommand ButtonCommand
            {
                get
                {
                    if (_ButtonCommand == null)
                    {
                        _ButtonCommand = new RelayCommand<string>(obj => ButtonAction(obj));
                    }
                    return _ButtonCommand;
                }
            }
    
            public ICommand MyEventCommand
            {
                get
                {
                    if (_MyEventCommand == null)
                    {
                        _MyEventCommand = new RelayCommand<MyEventArgs>(obj => MyEventAction(obj));
                    }
                    return _MyEventCommand;
                }
            }
    
            public MainViewModel()
            {
                string path = AppDomain.CurrentDomain.BaseDirectory + @"Resources\test.html";
                Uri uri = new Uri(@"file:///" + path);
                MapUrl = uri.ToString();
            }
    
            /// <summary>
            /// ButtonCommand의 동작을 수행합니다.
            /// </summary>
            /// <param name="obj"></param>
            protected void ButtonAction(string obj)
            {
                Console.WriteLine("ButtonAction : {0}", obj);
    
                switch(obj)
                {
                    case "ToJavaStript":
                        ExecuteScript = $"helloWorld('{InputText}');";
                        break;
                }
            }
    
            /// <summary>
            /// MyEventCommand의 동작을 수행합니다.
            /// </summary>
            /// <param name="obj"></param>
            protected void MyEventAction(MyEventArgs obj)
            {
                MessageBox.Show(obj.Data);
            }
        }
    }

     

    9. test.html

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <title>자바스크립트 ↔ C# 함수 호출 예제</title>
    </head>
    <body>
    <input type="text" width=200 id="messageBox">
    <input type="button" value="폼에서 메시지박스 띄우기" onclick="messageBoxShow()">
    
    <script>
    function helloWorld(message) {
     alert(message); 
    }
    
    function messageBoxShow()
    {
     var textBox = document.getElementById("messageBox").value;
     window.external.messageBoxShow(textBox); 
    }
    
    </script>
    
    </body>
    </html>

     

    10. 실행 화면

    웹에서 button 클릭 시 C#의 messageBoxShow 메소드를 호출합니다.

     

    C#에서 Button 클릭 시 웹의 helloWorld 함수를 호출합니다.

     


    프로젝트 전체 코드

     

    GitHub - yoosple/WPFs

    Contribute to yoosple/WPFs development by creating an account on GitHub.

    github.com

    반응형

    댓글

Designed by Tistory.