private bool TryExecuteCommand(VueBindingDescription binding, string value) { PropertyInfo propertyInfo = _dotnetViewModel.GetType().GetProperty(binding.PropertyName); if ((propertyInfo != null) && (propertyInfo.PropertyType == typeof(ICommand))) { ICommand command = propertyInfo.GetValue(_dotnetViewModel, null) as ICommand; command.Execute(value); return(true); } return(false); }
/// <summary> /// Gets a value from a property of the ViewModel. /// </summary> /// <param name="binding">The description of the property binding.</param> /// <param name="propertyValue">Value of the property.</param> /// <returns>Returns true if the value could be retrieved successfully, otherwise false.</returns> private bool TryGetFromViewmodel(VueBindingDescription binding, out object propertyValue) { PropertyInfo propertyInfo = _dotnetViewModel.GetType().GetProperty(binding.PropertyName); if ((propertyInfo != null) && (propertyInfo.CanRead)) { propertyValue = propertyInfo.GetValue(_dotnetViewModel, null); return(true); } propertyValue = null; return(false); }
internal bool TryFormatForView(VueBindingDescription binding, object value, out string formattedValue) { PropertyInfo propertyInfo = _dotnetViewModel.GetType().GetProperty(binding.PropertyName); if (propertyInfo != null) { Type propertyType = propertyInfo.PropertyType; if (value == null) { formattedValue = "null"; return(true); } else if (propertyType == typeof(string)) { formattedValue = "'" + WebviewUtils.EscapeJavaScriptString(value.ToString()) + "'"; return(true); } else if (typeof(IEnumerable <string>).IsAssignableFrom(propertyType)) { IEnumerable <string> valueList = (IEnumerable <string>)value; formattedValue = string.Join( ",", valueList.Select(v => "'" + WebviewUtils.EscapeJavaScriptString(v) + "'")); formattedValue = "[" + formattedValue + "]"; return(true); } else if (propertyType == typeof(bool)) { formattedValue = value.ToString().ToLowerInvariant(); return(true); } else if (propertyType == typeof(int)) { formattedValue = value.ToString(); return(true); } else if (propertyType == typeof(SecureString)) { // HTML doesn't know the concept of a SecureString, so this is the moment we have to switch. string unprotectedValue = SecureStringExtensions.SecureStringToString((SecureString)value); formattedValue = "'" + WebviewUtils.EscapeJavaScriptString(unprotectedValue) + "'"; return(true); } } formattedValue = null; return(false); }
private bool TrySetToViewmodel(VueBindingDescription binding, string formattedValue) { if (string.IsNullOrEmpty(binding?.PropertyName)) { return(false); } if (TryParseFromView(binding, formattedValue, out object value)) { PropertyInfo propertyInfo = _dotnetViewModel.GetType().GetProperty(binding.PropertyName); if ((propertyInfo != null) && (propertyInfo.CanWrite)) { propertyInfo.SetValue(_dotnetViewModel, value); return(true); } } return(false); }
/// <summary> /// Sets a value to the Vue-model in the View. /// </summary> /// <param name="binding">The description of the property binding.</param> /// <param name="value">New value of the property.</param> /// <returns>Returns true if the value could be set successfully, otherwise false.</returns> private bool TrySetToView(VueBindingDescription binding, object value) { if (TryFormatForView(binding, value, out string formattedValue)) { try { string script = string.Format( "var newValue = {2}; if ({0}.{1} != newValue) {0}.{1} = newValue;", "vm", binding.PropertyName, formattedValue); _htmlView.ExecuteJavaScript(script); return(true); } catch (Exception) { // This will return false } } return(false); }
internal bool TryParseFromView(VueBindingDescription binding, string formattedValue, out object value) { PropertyInfo propertyInfo = _dotnetViewModel.GetType().GetProperty(binding.PropertyName); if (propertyInfo != null) { try { Type propertyType = propertyInfo.PropertyType; if (string.Equals("null", formattedValue, StringComparison.InvariantCultureIgnoreCase)) { value = null; return(true); } else if (propertyType == typeof(string)) { value = formattedValue; return(true); } else if (propertyType == typeof(bool)) { value = bool.Parse(formattedValue); return(true); } else if (propertyType == typeof(int)) { value = int.Parse(formattedValue); return(true); } else if (propertyType == typeof(SecureString)) { value = SecureStringExtensions.StringToSecureString(formattedValue); return(true); } } catch (Exception) { // This will return false } } value = null; return(false); }
/// <summary> /// This method is called when the viewmodel modified a property and raised an /// INotifyPropertyChanged event. /// </summary> /// <param name="sender">Sender of the event.</param> /// <param name="e">Event arguments.</param> private void ViewmodelPropertyChangedHandler(object sender, PropertyChangedEventArgs e) { if (!IsListening) { return; } VueBindingDescription binding = _bindingDescriptions.FindByPropertyName(e.PropertyName); if (binding == null) { return; } bool isSuppressedEvent = string.Equals(binding.PropertyName, _suppressedViewmodelPropertyChangedEvent); if (isSuppressedEvent) { return; } switch (binding.BindingMode) { case VueBindingMode.TwoWay: case VueBindingMode.OneWayToView: if (TryGetFromViewmodel(binding, out object propertyValue)) { TrySetToView(binding, propertyValue); } break; case VueBindingMode.OneWayToViewmodel: break; case VueBindingMode.Command: break; // Should never happen default: throw new ArgumentOutOfRangeException(nameof(binding.BindingMode)); } }
/// <summary> /// This method is called by the JavaScript, triggered by the Vue model. /// </summary> /// <param name="sender">The sender of the event.</param> /// <param name="uri">The navigation uri, which should be filtered for binding commands.</param> private void NavigatingEventHandler(object sender, string uri) { if (!IsListening || !IsVueBindingUri(uri)) { return; } if (uri.Contains("vueLoaded")) { ViewLoadedEvent?.Invoke(this, new EventArgs()); return; } string queryPart = GetUriQueryPart(uri); NameValueCollection queryArguments = HttpUtility.ParseQueryString(queryPart); string propertyName = queryArguments.Get("name"); string value = queryArguments.Get("value"); VueBindingDescription binding = _bindingDescriptions.FindByPropertyName(propertyName); if (binding != null) { switch (binding.BindingMode) { case VueBindingMode.TwoWay: case VueBindingMode.OneWayToViewmodel: try { // We are inside a changed event from the view, so we do not want to // handle events from the viewmodel to update the view again, because // this could lead to circles. _suppressedViewmodelPropertyChangedEvent = binding.PropertyName; TrySetToViewmodel(binding, value); } finally { _suppressedViewmodelPropertyChangedEvent = null; } break; case VueBindingMode.Command: TryExecuteCommand(binding, value); break; case VueBindingMode.OneWayToView: break; // Should never happen default: throw new ArgumentOutOfRangeException(nameof(binding.BindingMode)); } } else { // Collect individually named arguments var arguments = new KeyValueList <string, string>(StringComparer.InvariantCultureIgnoreCase); foreach (string item in queryArguments) { if (!string.Equals("name", item, StringComparison.InvariantCultureIgnoreCase) && !string.Equals("value", item, StringComparison.InvariantCultureIgnoreCase)) { arguments[item] = queryArguments[item]; } } UnhandledViewBindingEvent?.Invoke(this, new VueBindingUnhandledViewBindingEventArgs(propertyName, value, arguments)); } }