How to create and use WebBrowser in background thread? - wpf

How can I create System.Windows.Forms.WebBrowser in background STA thread? I try use some code like this:
var tr = new Thread(wbThread);
tr.SetApartmentState(ApartmentState.STA);
tr.Start();
private void wbThread()
{
CWebBrowser browser = new CWebBrowser();
var text = browser.Navigate("http://site.com", CWebBrowser.EventType.loadCompleted).Body.InnerHtml;
}
CWebBrowser - custom class, wich delegate System.Windows.Forms.WebBrowser object Navigate method and wait until page completed loads. The problem is LoadCompleted event on System.Windows.Forms.WebBrowser object never raises. I found some solution here, but it does not work (can't find method Application.Run() on my WPF app).
public class CWebBrowser : ContentControl
{
public readonly System.Windows.Forms.WebBrowser innerWebBrowser;
private readonly AutoResetEvent loadCompletedEvent;
private readonly AutoResetEvent navigatedEvent;
public enum EventType
{
navigated, loadCompleted
}
public CWebBrowser()
{
innerWebBrowser = new System.Windows.Forms.WebBrowser();
loadCompletedEvent = new AutoResetEvent(false);
navigatedEvent = new AutoResetEvent(false);
System.Windows.Forms.Integration.WindowsFormsHost host = new System.Windows.Forms.Integration.WindowsFormsHost();
host.Child = innerWebBrowser;
Content = host;
innerWebBrowser.DocumentCompleted +=new System.Windows.Forms.WebBrowserDocumentCompletedEventHandler(innerWebBrowser_DocumentCompleted);
innerWebBrowser.Navigated += new System.Windows.Forms.WebBrowserNavigatedEventHandler(innerWebBrowser_Navigated);
}
void innerWebBrowser_Navigated(object sender, System.Windows.Forms.WebBrowserNavigatedEventArgs e)
{
navigatedEvent.Set();
}
void innerWebBrowser_DocumentCompleted(object sender, System.Windows.Forms.WebBrowserDocumentCompletedEventArgs e)
{
if (((sender as System.Windows.Forms.WebBrowser).ReadyState != System.Windows.Forms.WebBrowserReadyState.Complete) || innerWebBrowser.IsBusy)
return;
var doc = innerWebBrowser.Document;
loadCompletedEvent.Set();
}
public System.Windows.Forms.HtmlDocument Navigate(string url, EventType etype)
{
if (etype == EventType.loadCompleted)
loadCompletedEvent.Reset();
else if (etype == EventType.navigated)
navigatedEvent.Reset();
innerWebBrowser.Navigate(url);
if (etype == EventType.loadCompleted)
loadCompletedEvent.WaitOne();
else if (etype == EventType.navigated)
navigatedEvent.WaitOne();
System.Windows.Forms.HtmlDocument doc = null;
Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, new Action(
delegate
{
doc = innerWebBrowser.Document;
}));
return doc;
}
}
Thansk for all advices and sorry for my bad english :o(

Why don't you use the default WebBrowser control like this?
public MainPage()
{
InitializeComponent();
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(startNavigate);
}
void startNavigate()
{
WebBrowser wb = new WebBrowser();
wb.LoadCompleted += new LoadCompletedEventHandler(wb_LoadCompleted);
wb.Navigated += new EventHandler<System.Windows.Navigation.NavigationEventArgs>(wb_Navigated);
wb.Navigate(new Uri("http://www.google.com"));
}
void wb_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
// e.Content
}
void wb_LoadCompleted(object sender, NavigationEventArgs e)
{
// e.Content when the document finished loading.
}
Edit: You are using old System.Windows.Forms.WebBrowser control, instead System.Windows.Controls.WebBrowser which is part of WPF.

Related

Prevent RepositoryItemSearchLookUpEdit when Popup is Open When CloseUpKey.Key is pressed

I use a RepositoryItemSearchLookUpEdit. its CloseUpKey property is set to space
result.CloseUpKey = new DevExpress.Utils.KeyShortcut(System.Windows.Forms.Keys.Space);
I want to use this shortcut only for open popup and not for closing popup.
How can I achieve this?
UPDATE------------------------
First I create an RepositoryItemSearchLookUpEdit object
var result = new RepositoryItemSearchLookUpEdit();
result.CloseUpKey = new DevExpress.Utils.KeyShortcut(System.Windows.Forms.Keys.Space);
result.KeyDown += repositoryItemLookUpEdit_KeyDown;
result.CloseUp += repositoryItemLookUpEdit_CloseUp;
result.QueryCloseUp += repositoryItemLookUpEdit_QueryCloseUp;
private void repositoryItemLookUpEdit_QueryCloseUp(object sender, System.ComponentModel.CancelEventArgs e)
{
var edit = sender as SearchLookUpEdit;
KeyEventArgs k = new KeyEventArgs(edit.Properties.CloseUpKey.Key);
AttachKeyPressEvent(k);
if (k.KeyCode == edit.Properties.CloseUpKey.Key)
e.Cancel = true;
}
And pass it to a grid column:
grdListView.Columns["yyy"].ColumnEdit = result
How can I achieve that with these events, without creating a descendant SearchLookUpEdit
UPDATED:
The problem is that CloseUp event (where you could get the necessary info about the closeup key) occurs after the QueryCloseUp event (where you could precent the closing up event). Also the KeyPress, KeyDown and KeyUp events seem also NOT to occur when the QueryCloseUp occurs, as a result they couldn't be overridden. So I tried this, I created a custom KeyEventHandler and triggered him during QueryCloseUp event in order to get the necessary info of what key was pressed and cancel the event if the close key event was the one. Here is my codeTry it to see if it suits you
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//Here you can add your grid control as you have created
DataTable dt = new DataTable();
dt.Columns.Add("ID"); //use your own names and types
gridControl1.DataSource = dt;
var result = new RepositoryItemSearchLookUpEdit();
result.CloseUpKey = new DevExpress.Utils.KeyShortcut(System.Windows.Forms.Keys.Space);
result.QueryCloseUp += new CancelEventHandler(repositoryItemLookUpEdit_QueryCloseUp);
((gridControl1.MainView as GridView).Columns["ID"] as GridColumn).ColumnEdit = result;
}
private static readonly object myQueryCloseUp = new object();
public event KeyEventHandler MyQueryCloseUp
{
add { Events.AddHandler(myQueryCloseUp, value); }
remove { Events.RemoveHandler(myQueryCloseUp, value); }
}
protected virtual void AttachKeyPressEvent(KeyEventArgs e)
{
KeyEventHandler handler = (KeyEventHandler)Events[myQueryCloseUp];
if (handler != null)
handler(this, e);
}
//Here you add your own Handler implementation
public void repositoryItemLookUpEdit_QueryCloseUp(object sender, CancelEventArgs e)
{
KeyEventArgs k = new KeyEventArgs((sender as SearchLookUpEdit).Properties.CloseUpKey.Key);
AttachKeyPressEvent(k);
if (k.KeyCode == (sender as SearchLookUpEdit).Properties.CloseUpKey.Key)
e.Cancel = true;
}
}

How to automatically refresh listbox when add or remove the item in WPF?

I have WPF application that uses web service (asmx). The web service uses EF to get the data from MS SQL Server.
The code looks as following:
1) WPF:
public partial class MainWindow : Window
{
LetterWebServiceSoapClient _client = new LetterWebServiceSoapClient();
private ObservableCollection<Letter> _letters;
public MainWindow()
{
InitializeComponent();
}
private void cmdGetLetters_Click(object sender, RoutedEventArgs e)
{
lstLetters.ItemsSource = null;
_letters = _client.GetAllLetters();
lstLetters.ItemsSource = _letters;
}
private void cmdDeleteLetter_Click(object sender, RoutedEventArgs e)
{
_client.DeleteLetter((Letter)lstLetters.SelectedItem);
}
private void cmdAddLetter_Click(object sender, RoutedEventArgs e)
{
var newLetter = new Letter
{
Name = "Letter3",
Date = DateTime.Now,
Recipient = "John",
Sender = "David",
Content = "cccc"
};
_client.AddNewLetter(newLetter);
}
}
2) The web service:
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class LetterWebService : System.Web.Services.WebService
{
LetterDbEntities _letterDbEntities = new LetterDbEntities();
[WebMethod]
public ObservableCollection<Letter> GetAllLetters()
{
return new ObservableCollection<Letter>(_letterDbEntities.Letters.ToList());
}
[WebMethod]
public void AddNewLetter(Letter newLetter)
{
_letterDbEntities.Letters.Add(newLetter);
_letterDbEntities.SaveChanges();
}
[WebMethod]
public void DeleteLetter(Letter letter)
{
var letterToBeDeleted = _letterDbEntities.Letters.First(l => l.Id == letter.Id);
_letterDbEntities.Letters.Remove(letterToBeDeleted);
_letterDbEntities.SaveChanges();
}
}
When I add new letter or remove existing one they are added or removed on database level, but it doesn't reflect in UI that is in list box. What I'm missing?
You need to change the _letters collection. Try this:
private void cmdDeleteLetter_Click(object sender, RoutedEventArgs e)
{
var selectedItem = (Letter)lstLetters.SelectedItem;
_client.DeleteLetter(selectedItem);
_letters.Remove(selectedItem);
}
private void cmdAddLetter_Click(object sender, RoutedEventArgs e)
{
var newLetter = new Letter
{
Name = "Letter3",
Date = DateTime.Now,
Recipient = "John",
Sender = "David",
Content = "cccc"
};
_client.AddNewLetter(newLetter);
_letters.Add(newLetter);
}

WebKit.NET C# Custom Context Menu

I am implementing a Webkit Browser control in my windows app.
I need to use a custom context menu (right click) that only has copy/cut/paste as its options regardless of what element is right clicked. I need kind of a step-by-step as to how to implement it
Customizing the context menu for the WebKitBrowser supposes that you get a reference to the WebViewClass and then, setting a IWebUIDelegate for it by calling the setUIDelegate() method.
void MyWebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
var webView = this.GetWebView() as WebKit.Interop.WebViewClass;
webView.setUIDelegate(new MyWebUIDelegate(this));
}
In the IWebUIDelegate implementation you may intercept the contextMenuItemsForElement method and trigger the display of the context menu of the browser.
Here is a working sample:
public partial class Form1 : Form
{
MyWebBrowser webKitBrowser;
public Form1()
{
InitializeComponent();
webKitBrowser = new MyWebBrowser();
webKitBrowser.Dock = DockStyle.Fill;
this.Controls.Add(webKitBrowser);
webKitBrowser.Navigate("http://www.google.com");
}
}
class MyContextMenu : ContextMenu
{
public MyContextMenu()
{
var cutMenuItem = new MenuItem("Cut");
var copyMenuItem = new MenuItem("Copy");
var pasteMenuItem = new MenuItem("Paste");
cutMenuItem.Click += cutMenuItem_Click;
MenuItems.Add(cutMenuItem);
MenuItems.Add(copyMenuItem);
MenuItems.Add(pasteMenuItem);
}
void cutMenuItem_Click(object sender, EventArgs e)
{
//TODO: implement functionality
MessageBox.Show("Cut was selected");
}
}
class MyWebBrowser : WebKitBrowser
{
public event EventHandler ShowContextMenu = new EventHandler(OnFireShowContextMenu);
public MyWebBrowser()
{
DocumentCompleted += MyWebBrowser_DocumentCompleted;
var myContextMenu = new MyContextMenu();
ContextMenu = myContextMenu;
}
void MyWebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
var webView = this.GetWebView() as WebKit.Interop.WebViewClass;
webView.setUIDelegate(new MyWebUIDelegate(this));
}
public static void OnFireShowContextMenu(object sender, EventArgs e)
{
var webBrowser = (Control)sender;
var webView = (WebKit.Interop.WebViewClass)((MyWebBrowser)sender).GetWebView();
var originalPoint = webBrowser.PointToScreen(new Point(0, 0));
var currentPoint = new Point(Cursor.Position.X - originalPoint.X, Cursor.Position.Y - originalPoint.Y);
((WebKitBrowser)sender).ContextMenu.Show((Control)sender, currentPoint);
}
public void FireShowContextMenu()
{
this.ShowContextMenu(this, null);
}
}
class MyWebUIDelegate : IWebUIDelegate
{
private MyWebBrowser owner;
public MyWebUIDelegate(MyWebBrowser browser)
{
this.owner = browser;
}
//trigger the browser's FireShowContextMenu() method
public int contextMenuItemsForElement(WebView sender, CFDictionaryPropertyBag element, int defaultItemsHMenu)
{
owner.FireShowContextMenu();
return defaultItemsHMenu;
}
//return 1, true
public int hasCustomMenuImplementation()
{
return 1;
}
//the rest of the IWebUIDelegate interface implementation
}
For more insight, probably you would want to study some other customizations, such as open-webkit-sharp.

DataBinding Image.Source to a PhotoChooserTask result not working

I have the following class:
public class Sticky : INotifyPropertyChanged {
// ... some members
private BitmapImage _frontPic;
[DataMember]
public BitmapImage FrontPic {
get {
return _frontPic;
}
set {
_frontPic = value;
Changed("FrontPic");
Changed("FrontBrush");
}
}
}
I'm trying to databind it to this XAML:
<Image Width="173" Height="173" Source="{Binding FrontPic}" />
after launching a PhotoChooserTask with this code in my PhoneApplicationPage:
public Sticky Sticky { get; set; }
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) {
Sticky = new Sticky();
DataContext = Sticky;
}
private void ShowFrontPicPicker(object sender, RoutedEventArgs e) {
var t = new PhotoChooserTask();
t.PixelHeight = 173;
t.PixelWidth = 173;
t.ShowCamera = true;
t.Completed += (s, ev) => {
if (ev.TaskResult == TaskResult.OK) {
var img = new BitmapImage();
img.SetSource(ev.ChosenPhoto);
Sticky.FrontPic = img;
}
};
t.Show();
}
However, my image remains blank. If I assign the Image.Source property directly to the Image without databinding, everything works. Databinding other properties works, it's just the image that seems to be the problem. How can I make the DataBinding on the image work?
Found the problem! The completed callback for PhotoChooserTask does not excecute in the UI thread, so a call to Dispatcher.BeginInvoke must be added:
t.Completed += (s, ev) => Dispatcher.BeginInvoke(() => {
// do stuff...
});

How do I capture key down in WPF?

How do I capture a key down event in WPF even if my application is not focused?
For me, the best way is this:
public MainWindow()
{
InitializeComponent();
CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
}
void CompositionTarget_Rendering(object sender, EventArgs e)
{
if ((Keyboard.GetKeyStates(Key.W) & KeyStates.Down) > 0)
{
player1.walk();
}
}
The rendering event runs every time.
Global keyboard hook can slow down your debugging.
I prefer to use this approach:
Create KeyboardListener class
public class KeyboardListener : IDisposable
{
private readonly Thread keyboardThread;
//Here you can put those keys that you want to capture
private readonly List<KeyState> numericKeys = new List<KeyState>
{
new KeyState(Key.D0),
new KeyState(Key.D1),
new KeyState(Key.D2),
new KeyState(Key.D3),
new KeyState(Key.D4),
new KeyState(Key.D5),
new KeyState(Key.D6),
new KeyState(Key.D7),
new KeyState(Key.D8),
new KeyState(Key.D9),
new KeyState(Key.NumPad0),
new KeyState(Key.NumPad1),
new KeyState(Key.NumPad2),
new KeyState(Key.NumPad3),
new KeyState(Key.NumPad4),
new KeyState(Key.NumPad5),
new KeyState(Key.NumPad6),
new KeyState(Key.NumPad7),
new KeyState(Key.NumPad8),
new KeyState(Key.NumPad9),
new KeyState(Key.Enter)
};
private bool isRunning = true;
public KeyboardListener()
{
keyboardThread = new Thread(StartKeyboardListener) { IsBackground = true };
keyboardThread.Start();
}
private void StartKeyboardListener()
{
while (isRunning)
{
Thread.Sleep(15);
if (Application.Current != null)
{
Application.Current.Dispatcher.Invoke(() =>
{
if (Application.Current.Windows.Count > 0)
{
foreach (var keyState in numericKeys)
{
if (Keyboard.IsKeyDown(keyState.Key) && !keyState.IsPressed) //
{
keyState.IsPressed = true;
KeyboardDownEvent?.Invoke(null, new KeyEventArgs(Keyboard.PrimaryDevice, PresentationSource.FromDependencyObject(Application.Current.Windows[0]), 0, keyState.Key));
}
if (Keyboard.IsKeyUp(keyState.Key))
{
keyState.IsPressed = false;
}
}
}
});
}
}
}
public event KeyEventHandler KeyboardDownEvent;
/// <summary>
/// Состояние клавиши
/// </summary>
private class KeyState
{
public KeyState(Key key)
{
this.Key = key;
}
public Key Key { get; }
public bool IsPressed { get; set; }
}
public void Dispose()
{
isRunning = false;
Task.Run(() =>
{
if (keyboardThread != null && !keyboardThread.Join(1000))
{
keyboardThread.Abort();
}
});
}
}
Subscribe to KeyboardDownEvent in code-behind (or where you need it).
public partial class MainWindow : Window
{
private KeyboardListener listener;
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
listener = new KeyboardListener();
listener.KeyboardDownEvent += ListenerOnKeyPressed;
}
private void ListenerOnKeyPressed(object sender, KeyEventArgs e)
{
// TYPE YOUR CODE HERE
}
private void Window_OnUnloaded(object sender, RoutedEventArgs e)
{
listener.KeyboardDownEvent -= ListenerOnKeyPressed;
}
}
Done
See this questions for hooking the keyboard Using global keyboard hook (WH_KEYBOARD_LL) in WPF / C#

Resources