How to call client code from javascript in WebBrowser on WPF? - wpf

I am trying to call WPF client code from WebBrowser control as given in the example https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.webbrowser.objectforscripting?redirectedfrom=MSDN&view=netframework-4.8#System_Windows_Forms_WebBrowser_ObjectForScripting. The example provided in the link is for Windows forms. I am trying to do similar thing in WPF. However, I am getting following error.
Managed Debugging Assistant 'NonComVisibleBaseClass' : 'A QueryInterface call was made requesting the default IDispatch interface of COM visible managed class 'WpfApp2.MainWindow'. However since this class does not have an explicit default interface and derives from non COM visible class 'System.Windows.Window', the QueryInterface call will fail. This is done to prevent the non COM visible base class from being constrained by the COM versioning rules.'
Is there any way to resolve this? or it just cannot be done in WebBrowser-control and WPF ?

We need to create a separate class and use it for scripting
using System.Windows;
using System.Security.Permissions;
using System.Windows.Controls;
using System;
namespace WpfApp2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
[System.Runtime.InteropServices.ComVisibleAttribute(true)]
public class ObjectForScriptingHelper
{
public void Test()
{
MessageBox.Show("xxxxx");
}
}
public MainWindow()
{
InitializeComponent();
button1.Content = "call script code from client code";
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
//webBrowser1.AllowWebBrowserDrop = false;
//webBrowser1.IsWebBrowserContextMenuEnabled = false;
//webBrowser1.WebBrowserShortcutsEnabled = false;
webBrowser1.ObjectForScripting = new ObjectForScriptingHelper();
// Uncomment the following line when you are finished debugging.
//webBrowser1.ScriptErrorsSuppressed = true;
webBrowser1.Navigate(#"C:\temp\WpfApp2\WpfApp2\HTMLPage1.html");
}
private void Button1_Click_1(object sender, RoutedEventArgs e)
{
webBrowser1.InvokeScript("test",
new String[] { "called from client code" });
}
}
}

Related

Interaction between forms

I am using visual studio 2012 (windows form application) and I have two forms, one with a label and the other with a button. I want it so that when you click the button the label on the other form goes up by one. I already have:
Label1 = Label1 + 1
I just need to know how to make the connection with the two forms. Maybe call a function?
Btw I am new to the program and script so in simple terms plz.
Here is a sample I create for you. Add Fomr2 Like this:
public partial class Form2 : Form
{
private void button1_Click(object sender, EventArgs e)
{
Form1.Instance.Controls.Find("label1", true).First().Text = "Some thing";
}
}
And Form1:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
_Instance = this;
}
private static Form1 _Instance;
public static Form1 Instance
{
get { return _Instance; }
set { Instance = value; }
}
private void button1_Click(object sender, EventArgs e)
{
new Form2().Show();
}

DispatcherTimer not firing in WPF Application

I am trying to understand why the DispatcherTimer contained within SingletonWithTimer is not firing in the following WPF application. I've been researching this for a couple of days and cannot seem to get to the bottom it. This application is the reduced essential parts of an existing application that I'm trying to fix. The Startup object of this project is WPFApplication5TimerTest.Program.
The output in the console lists as follows, the problem is evident because the word "TimerTick" is not shown in the output:
Timer is initialized
'WpfApplication5TimerTest.vshost.exe' (Managed (v4.0.30319)): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\PresentationFramework.Aero\v4.0_4.0.0.0__31bf3856ad364e35\PresentationFramework.Aero.dll', Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
Sample thread
Sample thread
Sample thread
Sample thread
Sample thread
Sample thread
The thread '<No Name>' (0x10b0) has exited with code 0 (0x0).
Sample thread exiting!
This is Program.cs:
using System;
namespace WpfApplication5TimerTest
{
static class Program
{
[STAThread]
static void Main(string[] args)
{
AppObject = new App();
AppObject.Run();
}
public static App AppObject
{
get;
private set;
}
}
}
This is App.xaml.cs:
using System;
using System.Threading;
using System.Windows;
namespace WpfApplication5TimerTest
{
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
var sampleThread = new Thread(new ThreadStart(SampleThreadEntryPoint));
sampleThread.Start();
new MainWindow().Show();
}
private void SampleThreadEntryPoint()
{
SingletonWithTimer.Initialize();
while (!_shutdownEvent.WaitOne(1000))
Console.WriteLine("Sample thread");
Console.WriteLine("Sample thread exiting!");
}
protected override void OnExit(ExitEventArgs e)
{
_shutdownEvent.Set();
}
private ManualResetEvent _shutdownEvent = new ManualResetEvent(false);
}
}
This is MainWindow.xaml.cs:
using System;
using System.Windows;
namespace WpfApplication5TimerTest
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Closed(object sender, EventArgs e)
{
Program.AppObject.Shutdown();
}
}
}
This is SingletonWithTimer.cs:
using System;
using System.Windows.Threading;
namespace WpfApplication5TimerTest
{
public class SingletonWithTimer
{
private static SingletonWithTimer Instance
{
get
{
if (_instance == null)
{
_instance = new SingletonWithTimer();
}
return _instance;
}
}
public static void Initialize()
{
SingletonWithTimer.Instance._timer = new DispatcherTimer();
SingletonWithTimer.Instance._timer.Interval = TimeSpan.FromSeconds(2);
SingletonWithTimer.Instance._timer.Tick += new EventHandler(SingletonWithTimer.Instance.OnTimerTick);
SingletonWithTimer.Instance._timer.Start();
Console.WriteLine("Timer is initialized");
}
private void OnTimerTick(object sender, EventArgs e)
{
Console.WriteLine("TimerTick");
}
private static SingletonWithTimer _instance;
private DispatcherTimer _timer = null;
}
}
It's because you've created the DispatcherTimer on another (non-UI) thread. Hence, it will be tied to the Dispatcher on that thread, not the one on the UI thread. Since nothing is running the Dispatcher on that thread, it will never fire.
Either create the DispatcherTimer on the UI thread, or use a constructor overload that allows you to pass in a specific Dispatcher to use.

Multithreading WPF Application Question

I have a WPF application like this.
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public delegate void NextPrimeDelegate();
int i = 0;
public MainWindow()
{
InitializeComponent();
}
public void CheckNextNumber()
{
i++;
textBox1.Text= i.ToString();
Dispatcher.BeginInvoke(
System.Windows.Threading.DispatcherPriority.SystemIdle,
new NextPrimeDelegate(this.CheckNextNumber));
}
private void button1_Click(object sender, RoutedEventArgs e)
{
Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
new NextPrimeDelegate(CheckNextNumber));
}
}
Above code is working without problem.My question is:I want to call more than a function that is called CheckNextNumber.
For example:I have to make something like this.
tr[0].Start();
tr[0].Stop();
For C# version prior to 3.0 you can use anonymous delegates:
Dispatcher.BeginInvoke(DispatcherPriority.Normal, (NextPrimeDelegate)delegate()
{
tr[0].Start();
tr[0].Stop();
});
Beginning with C# 3.0 you can use lambdas (which is basically syntactic sugar on top of anonymous delegates). Additionally you don't need your NextPrimeDelegate because .NET 3.5 introduced the generic parameterless Action delegate.
Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
tr[0].Start();
tr[0].Stop();
});
Instead of using a delegate, you can use an Action with a codeblock with whatever code you like, e.g:
Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
new Action(() =>
{
tr[0].Start();
tr[0].Stop();
}));

Global handling exception in WPF app with Caliburn.Micro

Hi I try implement solution from this site im my WPF app for global exception handling.
http://www.codeproject.com/Articles/90866/Unhandled-Exception-Handler-For-WPF-Applications.aspx
I use Caliburn Micro as MVVM framework. Service I have in external assembly and it is injected in view model class with MEF.
Here is my implementation for global exception handling in WPF app.
App.xaml
DispatcherUnhandledException="Application_DispatcherUnhandledException"
Startup="Application_Startup"
App class:
public partial class App : Application
{
private IMessageBox _msgBox = new MessageBoxes.MessageBoxes();
public bool DoHandle { get; set; }
private void Application_Startup(object sender, StartupEventArgs e)
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
}
private void Application_DispatcherUnhandledException(object sender,
System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
if (DoHandle)
{
_msgBox.ShowException(e.Exception);
e.Handled = true;
}
else
{
_msgBox.ShowException(e.Exception);
e.Handled = false;
}
}
void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var ex = e.ExceptionObject as Exception;
_msgBox.ShowException(ex);
}
}
Service method from external assembly:
public void ServiceLogOn()
{
try
{
}
catch (Exception ex)
{
throw ex;
}
}
This service method is call in view model class for example on button click event:
[Export(typeof(ILogOnViewModel))]
public class LogOnViewModel : Screen, ILogOnViewModel
{
public void LogOn()
{
_service.ServiceLogOn();
}
}
I run WPF app in Visual Studio and produce exception with message "Bad credentials" in ServiceLogOn method.
I expect that I see the message box with the exception.
But Visual Studio stop debuging app and show exception in service method in service project.
So I try run WPF from exe file and produce same exception in ServiceLogOn method.
I get this error:
Exception has been throw by target of an invocation.
Any exception from view model class is not handled in methods:
Application_DispatcherUnhandledException
or CurrentDomain_UnhandledException.
in App class.
What I do bad?
EDITED with Simon Fox’s answer.
I try implement in MEF bootstraper advice of Simon Fox’s, but I still something do wrong.
I move handle logic for exception to OnUnhandledException method in bootstraper class.
Here is my code from bootstraper class:
public class MefBootStrapper : Bootstrapper<IShellViewModel>
{
//...
private IMessageBox _msgBox = new MessageBoxes.MessageBoxes();
public bool DoHandle { get; set; }
protected override void OnUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
if (DoHandle)
{
_msgBox.ShowException(e.Exception);
e.Handled = true;
}
else
{
_msgBox.ShowException(e.Exception);
e.Handled = false;
}
}
//...
}
I bind some method from view model on button and throw new exception. Something like this:
public void LogOn()
{
throw new ArgumentException("Bad argument");
}
But result is sam, I test app out of Visual Studio and get this exception.
Exception has been throw by target of an invocation.
Caliburn.Micro has built in support for hooking unhandled exceptions. The Bootstrapper class (which every Caliburn project requires) sets this up for you and provides the virtual OnUnhandledException method.
In your custom BootStrapper you must override OnUnhandledException to perform any custom actions for unhandled exceptions in your app. Note that you will most likely have to marshal actions such as displaying a message box to the UI thread (Caliburn enables this easily via Execute.OnUIThread).
You may also have an issue in the way your service moves exceptions to the client, but without any details of how the service is implemented/hosted/etc I cannot help. Are you using WCF to do SOAP? Are you using FaultContracts?

Windows Forms Binding Problem: How to know if user has changed a value

I currently have a windows form application composed of a textbox and two buttons (previous and next) The textbox is bound to the name of a Person on a list. Previous and Next button changes the position of the BindingManager by 1 (either increment or decrement).
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private List<Person> stringList;
BindingManagerBase bindingManager;
public Form1()
{
InitializeComponent();
stringList = new List<Person>();
stringList.Add(new Person{ name = "person1" });
stringList.Add(new Person { name = "person2" });
stringList.Add(new Person { name = "person3" });
stringList.Add(new Person { name = "person4" });
bindingManager = this.BindingContext[stringList];
bindingManager.CurrentChanged += handleCurrentChanged;
textBox1.DataBindings.Add(new Binding("Text", stringList, "name"));
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
}
private void handleCurrentChanged(object sender, EventArgs e)
{
MessageBox.Show("handleCurrentChanged");
}
private void button1_Click(object sender, EventArgs e)
{
bindingManager.Position++;
}
private void button2_Click(object sender, EventArgs e)
{
bindingManager.Position--;
}
}
public class Person
{
public String name { get; set; }
}
}
What is needed is to prompt the user whenever he/she presses the previous or next button whether to save his changes or not. But only if he made some changes to the textbox.
My problem is how to know whether there has been some changes to the Person object so that I can initiate the prompt. I had intended to use BindingManagerBase's currentChanged event but this only checks if you have changed which item you are working on the list. I also can't check for the textbox since the previous and next button manipulate it also, which I do not want to prompt the user for.
You could implement INotifyPropertyChanged interface in your person class and then your GUI class can be notified every time a property changed in your Person class.

Resources