/// <summary> /// If a view model's property is a delegate, wrap the action in a Command object and invoke it. /// </summary> /// <param name="viewModel">View model object.</param> /// <param name="propInfo">Delegate property type info.</param> /// <param name="newValue">Delegate argument.</param> /// <returns>Whether the delegate was invoked.</returns> private bool InvokeDelegate(object viewModel, PropertyInfoHelper propInfo, string newValue) { var propType = propInfo.PropertyType.GetTypeInfo(); var argTypes = propType.GetGenericArguments(); Type delegateReturnType = propType.GetMethod(nameof(Action.Invoke)).ReturnType; if (delegateReturnType == typeof(void)) { var cmdType = argTypes.Length > 0 ? typeof(Command <>).MakeGenericType(argTypes) : typeof(Command); (Activator.CreateInstance(cmdType, new object[] { propInfo.GetValue(viewModel) }) as ICommand)?.Execute(newValue); return(true); } else if (delegateReturnType == typeof(Task)) { var cmdType = argTypes.Length > 1 ? typeof(Command <>).MakeGenericType(argTypes[0]) : typeof(Command); var task = (Activator.CreateInstance(cmdType, new object[] { propInfo.GetValue(viewModel) }) as IAsyncCommand)?.ExecuteAsync(newValue); if (viewModel is BaseVM) { (viewModel as BaseVM).AsyncCommands.Add(task); } return(true); } return(false); }
/// <summary> /// Deserializes a property value of a view model. /// </summary> /// <param name="viewModel">View model to deserialize the property to.</param> /// <param name="vmPath">View model property path.</param> /// <param name="newValue">New value.</param> /// <returns>True if the value was deserialized.</returns> public bool Deserialize(object viewModel, string vmPath, string newValue) { try { var vmType = viewModel.GetType(); var path = vmPath.Split('.'); for (int i = 0; i < path.Length; i++) { var propName = path[i]; var propInfo = viewModel != null?PropertyInfoHelper.Find(viewModel, propName) : null; if (propInfo == null) { return(false); } var propType = propInfo.PropertyType.GetTypeInfo(); if (i < path.Length - 1) { // Path that starts with $ sign means it is a key to an IEnumerable property. // By convention we expect a method whose name is in this format: // <IEnumerable property name>_get (for example: ListContent_get) // to get the object whose key matches the given value in the path. if (path[i + 1].StartsWith("$")) { var key = path[i + 1].TrimStart('$'); var methodInfo = vmType.GetTypeInfo().GetMethod(propName + "_get"); if (methodInfo == null) { return(false); } viewModel = methodInfo.Invoke(viewModel, new object[] { key }); if (viewModel == null) { return(false); } vmType = viewModel.GetType(); i++; } else { viewModel = propInfo.GetValue(viewModel); vmType = viewModel?.GetType() ?? propInfo.PropertyType; } } else if (viewModel == null) { return(false); } else if (typeof(ICommand).GetTypeInfo().IsAssignableFrom(propInfo.PropertyType)) { // If the property type is ICommand, execute the command. (propInfo.GetValue(viewModel) as ICommand)?.Execute(newValue); } else if (propType.IsSubclassOf(typeof(MulticastDelegate)) && propType.GetMethod(nameof(Action.Invoke)).ReturnType == typeof(void)) { // If the property type is Action, wrap the action in a Command object and execute it. var argTypes = propType.GetGenericArguments(); var cmdType = argTypes.Length > 0 ? typeof(Command <>).MakeGenericType(argTypes) : typeof(Command); (Activator.CreateInstance(cmdType, new object[] { propInfo.GetValue(viewModel) }) as ICommand)?.Execute(newValue); } else if (typeof(IReactiveProperty).GetTypeInfo().IsAssignableFrom(propInfo.PropertyType)) { // If the property type is ReactiveProperty, set the new value into the object. var reactiveProp = propInfo.GetValue(viewModel) as IReactiveProperty; reactiveProp.Value = ConvertFromString(reactiveProp.PropertyType, newValue); } else if (propInfo.SetMethod != null) { // Update the new value to the property. propInfo.SetValue(viewModel, ConvertFromString(propInfo.PropertyType, newValue)); } } } catch (Exception ex) { Trace.Fail(ex.ToString()); throw ex; } return(true); }
/// <summary> /// Deserializes a property value of a view model. /// </summary> /// <param name="viewModel">View model to deserialize the property to.</param> /// <param name="vmPath">View model property path.</param> /// <param name="newValue">New value.</param> /// <returns>True if the value was deserialized.</returns> public bool Deserialize(object viewModel, string vmPath, string newValue) { try { var vmType = viewModel.GetType(); var path = vmPath.Split('.'); for (int i = 0; i < path.Length; i++) { var propName = path[i]; var propInfo = viewModel != null?PropertyInfoHelper.Find(viewModel, propName) : null; if (propInfo == null) { // If not a property, maybe it's a method with 0 or 1 argument. if (InvokeIfMethod(viewModel, propName, newValue)) { continue; } else { return(false); } } var propType = propInfo.PropertyType.GetTypeInfo(); if (i < path.Length - 1) { // Path that starts with $ sign means it is a key to an IEnumerable property. // By convention we expect a method whose name is in this format: // <IEnumerable property name>_get (for example: ListContent_get) // to get the object whose key matches the given value in the path. if (path[i + 1].StartsWith("$")) { var key = path[i + 1].TrimStart('$'); var methodInfo = vmType.GetTypeInfo().GetMethod(propName + "_get"); if (methodInfo == null) { return(false); } viewModel = methodInfo.Invoke(viewModel, new object[] { key }); if (viewModel == null) { return(false); } vmType = viewModel.GetType(); i++; } else { viewModel = propInfo.GetValue(viewModel); vmType = viewModel?.GetType() ?? propInfo.PropertyType; } } else if (viewModel == null) { return(false); } else if (typeof(ICommand).GetTypeInfo().IsAssignableFrom(propInfo.PropertyType)) { // If the property type is ICommand, execute the command. (propInfo.GetValue(viewModel) as ICommand)?.Execute(newValue); } else if (propType.IsSubclassOf(typeof(MulticastDelegate))) { // If the property type is a delegate, wrap the action in a Command object and invoke it. InvokeDelegate(viewModel, propInfo, newValue); } else if (typeof(IReactiveProperty).GetTypeInfo().IsAssignableFrom(propInfo.PropertyType)) { // If the property type is ReactiveProperty, set the new value into the object. var reactiveProp = propInfo.GetValue(viewModel) as IReactiveProperty; reactiveProp.Value = ConvertFromString(reactiveProp.PropertyType, newValue); } else if (propInfo.SetMethod != null) { // Update the new value to the property. propInfo.SetValue(viewModel, ConvertFromString(propInfo.PropertyType, newValue)); } } } catch (Exception ex) { Logger.LogError($"Failed to deserialize view model property '{vmPath}': {ex.Message}"); throw ex; } return(true); }