-
[WPF WebBrowser] C#에서 자바스크립트 함수를 호출하거나 자바스크립트에서 C# 메소드를 호출하는 방법닷넷/WPF 2023. 3. 20. 16:50반응형
2022.08.03 - [C#/WinForm] - WebBrowser 자바스크립트에서 C# 메소드를 호출하는 방법
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 함수를 호출합니다.
반응형'닷넷 > WPF' 카테고리의 다른 글
C# WPF TextBlock이 렌더링 되지 않은 상태에서 ActualWidth 값이 계산되게 하는 방법 (0) 2023.03.28 C# WPF TextBlock의 Text를 오른쪽에서 왼쪽으로 애니메이션(BeginAnimation, 롤링) 하는방법 (0) 2023.03.21 C# WPF에서 DataGrid에 데이터를 바인딩하는 간단한 예제 (0) 2023.03.09 C# WPF에서 특정 컨트롤을 최상단에 표시하는 방법 (0) 2023.02.20 C# WPF에서 창을 최상단에 표시하는 방법 (0) 2023.02.20