You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
247 lines
8.2 KiB
247 lines
8.2 KiB
using Newtonsoft.Json; |
|
using System; |
|
using System.Collections.Generic; |
|
using System.Reflection; |
|
using System.Runtime.InteropServices.WindowsRuntime; |
|
using System.Threading.Tasks; |
|
using Windows.Foundation; |
|
using Windows.Foundation.Metadata; |
|
|
|
namespace Monaco.Helpers |
|
{ |
|
/// <summary> |
|
/// Class to aid in accessing WinRT values from JavaScript. |
|
/// Not Thread Safe. |
|
/// </summary> |
|
[AllowForWeb] |
|
public sealed class ParentAccessor : IDisposable |
|
{ |
|
private WeakReference<IParentAccessorAcceptor> parent; |
|
private Type typeinfo; |
|
private Dictionary<string, Action> actions; |
|
private Dictionary<string, Func<string[], Task<string>>> events; |
|
|
|
private List<Assembly> Assemblies { get; set; } = new List<Assembly>(); |
|
|
|
/// <summary> |
|
/// Constructs a new reflective parent Accessor for the provided object. |
|
/// </summary> |
|
/// <param name="parent">Object to provide Property Access.</param> |
|
public ParentAccessor(IParentAccessorAcceptor parent) |
|
{ |
|
this.parent = new WeakReference<IParentAccessorAcceptor>(parent); |
|
typeinfo = parent.GetType(); |
|
actions = new Dictionary<string, Action>(); |
|
events = new Dictionary<string, Func<string[], Task<string>>>(); |
|
} |
|
|
|
/// <summary> |
|
/// Registers an action from the .NET side which can be called from within the JavaScript code. |
|
/// </summary> |
|
/// <param name="name">String Key.</param> |
|
/// <param name="action">Action to perform.</param> |
|
internal void RegisterAction(string name, Action action) |
|
{ |
|
actions[name] = action; |
|
} |
|
|
|
/// <summary> |
|
/// Registers an event from the .NET side which can be called with the given jsonified string arguments within the JavaScript code. |
|
/// </summary> |
|
/// <param name="name">String Key.</param> |
|
/// <param name="function">Event to call.</param> |
|
internal void RegisterEvent(string name, Func<string[], Task<string>> function) |
|
{ |
|
events[name] = function; |
|
} |
|
|
|
/// <summary> |
|
/// Calls an Event registered before wit hthe <see cref="RegisterEvent(string, Func{string[], string})"/>. |
|
/// </summary> |
|
/// <param name="name">Name of event to call.</param> |
|
/// <param name="parameters">JSON string Parameters.</param> |
|
/// <returns></returns> |
|
public IAsyncOperation<string> CallEvent(string name, [ReadOnlyArray] string[] parameters) |
|
{ |
|
if (events.ContainsKey(name)) |
|
{ |
|
return events[name]?.Invoke(parameters).AsAsyncOperation(); |
|
} |
|
|
|
return (new Task<string>(() => { return null; })).AsAsyncOperation(); |
|
} |
|
|
|
/// <summary> |
|
/// Adds an Assembly to use for looking up types by name for <see cref="SetValue(string, string, string)"/>. |
|
/// </summary> |
|
/// <param name="assembly">Assembly to add.</param> |
|
internal void AddAssemblyForTypeLookup(Assembly assembly) |
|
{ |
|
Assemblies.Add(assembly); |
|
} |
|
|
|
/// <summary> |
|
/// Calls an Action registered before with <see cref="RegisterAction(string, Action)"/>. |
|
/// </summary> |
|
/// <param name="name">String Key.</param> |
|
/// <returns>True if method was found in registration.</returns> |
|
public bool CallAction(string name) |
|
{ |
|
if (actions.ContainsKey(name)) |
|
{ |
|
actions[name]?.Invoke(); |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
/// <summary> |
|
/// Returns the winrt primative object value for the specified Property. |
|
/// </summary> |
|
/// <param name="name">Property name on Parent Object.</param> |
|
/// <returns>Property Value or null.</returns> |
|
public object GetValue(string name) |
|
{ |
|
if (parent.TryGetTarget(out IParentAccessorAcceptor tobj)) |
|
{ |
|
var propinfo = typeinfo.GetProperty(name); |
|
return propinfo?.GetValue(tobj); |
|
} |
|
|
|
return null; |
|
} |
|
|
|
public string GetJsonValue(string name) |
|
{ |
|
if (parent.TryGetTarget(out IParentAccessorAcceptor tobj)) |
|
{ |
|
var propinfo = typeinfo.GetProperty(name); |
|
var obj = propinfo?.GetValue(tobj); |
|
|
|
return JsonConvert.SerializeObject(obj, new JsonSerializerSettings() |
|
{ |
|
NullValueHandling = NullValueHandling.Ignore |
|
}); |
|
} |
|
|
|
return "{}"; |
|
} |
|
|
|
/// <summary> |
|
/// Returns the winrt primative object value for a child property off of the specified Property. |
|
/// |
|
/// Useful for providing complex types to users of Parent but still access primatives in JavaScript. |
|
/// </summary> |
|
/// <param name="name">Parent Property name.</param> |
|
/// <param name="child">Property's Property name to retrieve.</param> |
|
/// <returns>Value of Child Property or null.</returns> |
|
public object GetChildValue(string name, string child) |
|
{ |
|
if (parent.TryGetTarget(out IParentAccessorAcceptor tobj)) |
|
{ |
|
// TODO: Support params for multi-level digging? |
|
var propinfo = typeinfo.GetProperty(name); |
|
var prop = propinfo?.GetValue(tobj); |
|
if (prop != null) |
|
{ |
|
var childinfo = prop.GetType().GetProperty(child); |
|
return childinfo?.GetValue(prop); |
|
} |
|
} |
|
|
|
return null; |
|
} |
|
|
|
/// <summary> |
|
/// Sets the value for the specified Property. |
|
/// </summary> |
|
/// <param name="name">Parent Property name.</param> |
|
/// <param name="value">Value to set.</param> |
|
public void SetValue(string name, object value) |
|
{ |
|
if (parent.TryGetTarget(out IParentAccessorAcceptor tobj)) |
|
{ |
|
var propinfo = typeinfo.GetProperty(name); // TODO: Cache these? |
|
tobj.IsSettingValue = true; |
|
propinfo?.SetValue(tobj, value); |
|
tobj.IsSettingValue = false; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Sets the value for the specified Property after deserializing the value as the given type name. |
|
/// </summary> |
|
/// <param name="name"></param> |
|
/// <param name="value"></param> |
|
/// <param name="type"></param> |
|
public void SetValue(string name, string value, string type) |
|
{ |
|
if (parent.TryGetTarget(out IParentAccessorAcceptor tobj)) |
|
{ |
|
var propinfo = typeinfo.GetProperty(name); |
|
var typeobj = LookForTypeByName(type); |
|
|
|
var obj = JsonConvert.DeserializeObject(value, typeobj); |
|
|
|
tobj.IsSettingValue = true; |
|
propinfo?.SetValue(tobj, obj); |
|
tobj.IsSettingValue = false; |
|
} |
|
} |
|
|
|
private Type LookForTypeByName(string name) |
|
{ |
|
// First search locally |
|
var result = Type.GetType(name); |
|
|
|
if (result != null) |
|
{ |
|
return result; |
|
} |
|
|
|
// Search in Other Assemblies |
|
foreach (var assembly in Assemblies) |
|
{ |
|
foreach (var typeInfo in assembly.ExportedTypes) |
|
{ |
|
if (typeInfo.Name == name) |
|
{ |
|
return typeInfo; |
|
} |
|
} |
|
} |
|
|
|
return null; |
|
} |
|
|
|
public void Dispose() |
|
{ |
|
if (actions != null) |
|
{ |
|
actions.Clear(); |
|
} |
|
|
|
actions = null; |
|
|
|
if (events != null) |
|
{ |
|
events.Clear(); |
|
} |
|
|
|
events = null; |
|
} |
|
} |
|
|
|
//// TODO: Find better approach than this. Issue #21. |
|
/// <summary> |
|
/// Interface used on objects to be accessed. |
|
/// </summary> |
|
public interface IParentAccessorAcceptor |
|
{ |
|
/// <summary> |
|
/// Property to tell object the value is being set by ParentAccessor. |
|
/// </summary> |
|
bool IsSettingValue { get; set; } |
|
} |
|
}
|
|
|