닷넷/WPF

[WPF WebBrowser] C#에서 자바스크립트 함수를 호출하거나 자바스크립트에서 C# 메소드를 호출하는 방법

FreeBear 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

반응형