-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
demo_basic.PRG Demonstrates the simplest usage that displays an external web page demo_evaluateScript.PRG Uses the EvaluateScript function to read properties from the DOM and return them into Visual FoxPro Demo_Events.PRG Implements the TitleChanged event to respond to title changes due to navigation or setting the window.title property. Added a new example form that provides a button to run code defined in the config object. Implemented the reload method in the sample form. Added the .NET event subscription mechanism from my customized version of wwDotNetBridge. CefSharp will now work with other versions of wwDotNetBridge as well. Some demos missed include files. These have been added. New versions of wwDotNetBridge return objects differently than before. Added code to handle the interface change for both, old versions and the current version of wwDotNetBridge.
- Loading branch information
1 parent
85a0d0d
commit 523c372
Showing
11 changed files
with
4,548 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
using System; | ||
using System.Collections.Concurrent; | ||
using System.Collections.Generic; | ||
using System.Linq.Expressions; | ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Threading.Tasks; | ||
|
||
namespace fpCefSharp | ||
{ | ||
/// <summary> | ||
/// FoxPro interop access to .NET events. Handles all events of a source object for subsequent retrieval by a FoxPro client. | ||
/// </summary> | ||
/// <remarks>For a FoxPro program to be notified of events, it should use `wwDotNetBridge.InvokeMethodAsync` to call | ||
/// <see cref="WaitForEvent"/>. When <see cref="WaitForEvent"/> asynchronously completes, the FoxPro program should | ||
/// handle the event it returns and then call <see cref="WaitForEvent"/> again to wait for the next event. | ||
/// The FoxPro class `EventSubscription`, which is returned by `SubscribeToEvents`, encapsulates this async wait loop. | ||
/// | ||
/// NOTE: This code is based on wwDotNetBridge from Rick Strahl and Edward Brey. It's the same code as in pull request | ||
/// #26 for wwDotNetBridge, the class name aside. This code can be removed after the pull request is merged and | ||
/// the FoxPro code has been updated to use the built-in mechanism of wwDotNetBridge. | ||
/// </remarks> | ||
public class fpEventSubscriber : IDisposable | ||
{ | ||
private readonly object _source; | ||
private readonly List<DelegateInfo> _eventHandlers = new List<DelegateInfo>(); | ||
private readonly ConcurrentQueue<fpRaisedEvent> _raisedEvents = new ConcurrentQueue<fpRaisedEvent>(); | ||
private TaskCompletionSource<fpRaisedEvent> _completion = new TaskCompletionSource<fpRaisedEvent>(); | ||
|
||
public fpEventSubscriber(object source, String prefix = "", dynamic vfp = null) | ||
{ | ||
// Indicates that initially the client is not waiting. | ||
_completion.SetResult(null); | ||
|
||
// For each event, adds a handler that calls QueueInteropEvent. | ||
_source = source; | ||
foreach (var ev in source.GetType().GetEvents()) | ||
{ | ||
// handler is a PRIVATE variable defined in EventSubscription.Setup(). | ||
Boolean hasMethod = vfp?.Eval($"PEMSTATUS(m.handler, '{prefix}{ev.Name}', 5)"); | ||
if (!hasMethod) | ||
continue; | ||
|
||
var eventParams = ev.EventHandlerType.GetMethod("Invoke").GetParameters().Select(p => Expression.Parameter(p.ParameterType)).ToArray(); | ||
var eventHandlerLambda = Expression.Lambda(ev.EventHandlerType, | ||
Expression.Call( | ||
instance: Expression.Constant(this), | ||
method: typeof(fpEventSubscriber).GetMethod(nameof(QueueInteropEvent), BindingFlags.NonPublic | BindingFlags.Instance), | ||
arg0: Expression.Constant(ev.Name), | ||
arg1: Expression.NewArrayInit(typeof(object), eventParams.Select(p => Expression.Convert(p, typeof(object))))), | ||
eventParams); | ||
var eventHandler = eventHandlerLambda.Compile(); | ||
ev.AddEventHandler(source, eventHandler); | ||
_eventHandlers.Add(new DelegateInfo(eventHandler, ev)); | ||
} | ||
} | ||
|
||
class DelegateInfo | ||
{ | ||
public DelegateInfo(Delegate handler, EventInfo eventInfo) | ||
{ | ||
Delegate = handler; | ||
EventInfo = eventInfo; | ||
} | ||
|
||
public Delegate Delegate { get; } | ||
public EventInfo EventInfo { get; } | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
foreach (var item in _eventHandlers) | ||
item.EventInfo.RemoveEventHandler(_source, item.Delegate); | ||
_completion.TrySetCanceled(); | ||
} | ||
|
||
private void QueueInteropEvent(string name, object[] parameters) | ||
{ | ||
var interopEvent = new fpRaisedEvent { Name = name, Params = parameters }; | ||
if (!_completion.TrySetResult(interopEvent)) | ||
_raisedEvents.Enqueue(interopEvent); | ||
} | ||
|
||
/// <summary> | ||
/// Waits until an event is raised, or returns immediately if a queued event is available. | ||
/// </summary> | ||
/// <returns>The next event, or null if this subscriber has been disposed.</returns> | ||
public fpRaisedEvent WaitForEvent() | ||
{ | ||
if (_raisedEvents.TryDequeue(out var interopEvent)) return interopEvent; | ||
_completion = new TaskCompletionSource<fpRaisedEvent>(); | ||
var task = _completion.Task; | ||
|
||
task.Wait(); | ||
|
||
return task.IsCanceled ? null : task.Result; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
namespace fpCefSharp | ||
{ | ||
public class fpRaisedEvent | ||
{ | ||
public string Name { get; internal set; } | ||
public object[] Params { get; internal set; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
*======================================================================================== | ||
* Respond to events in the browser | ||
*======================================================================================== | ||
|
||
LOCAL loConfig, loEvents | ||
loConfig = CREATEOBJECT("MyHandler") | ||
loEvents = CreateObject("MyEvents") | ||
|
||
Local loForm | ||
DO FORM ShowHtml WITH "https://myApp/index.html", m.loConfig Name loForm | ||
loEvents.AddProperty ("oCefSharpBrowser", m.loForm.oCefSharpBrowser) | ||
loForm.oCefSharpBrowser.HandleEvents (m.loEvents) | ||
|
||
DEFINE CLASS MyHandler as Custom | ||
|
||
index_html = "" | ||
|
||
PROCEDURE Init | ||
TEXT TO This.index_html NoShow | ||
<html> | ||
<script> | ||
function changeTitle () { | ||
window.top.document.title = new Date().toLocaleString(); | ||
} | ||
</script> | ||
<button onclick="changeTitle();">Change title</button> | ||
</html> | ||
ENDTEXT | ||
|
||
Procedure TitleChangedEvent (m.tcTitle) | ||
MessageBox("TitleChangedEvent: " + m.tcTitle) | ||
|
||
ENDDEFINE | ||
|
||
|
||
Define Class MyEvents as Custom | ||
|
||
Procedure OnTitleChanged (toSender, toEventArgs) | ||
This.oCefSharpBrowser.HandleTitleChangedEvent (m.toSender, m.toEventArgs) | ||
|
||
EndDefine |
Oops, something went wrong.