public async Task PreparePropertyDescriptorAsync() { if (string.IsNullOrEmpty(XamlCodeFile)) { return; } if (_propertyDescriptor == null) { // Get Xaml code using (var codeStream = await StreamHelper.GetPackagedFileStreamAsync($"Patterns/{Name}/{XamlCodeFile}")) { XamlCode = await codeStream.ReadTextAsync(Encoding.UTF8); // Look for @[] values and generate associated properties var regularExpression = new Regex(@"@\[(?<name>.+?)(:(?<type>.+?):(?<value>.+?)(:(?<parameters>.+?))?(:(?<options>.*))*)?\]@?"); _propertyDescriptor = new PropertyDescriptor { Expando = new ExpandoObject() }; var proxy = (IDictionary <string, object>)_propertyDescriptor.Expando; foreach (Match match in regularExpression.Matches(XamlCode)) { var label = match.Groups["name"].Value; var name = label.Replace(" ", string.Empty); // Allow us to have nicer display names, but create valid properties. var type = match.Groups["type"].Value; var value = match.Groups["value"].Value; var existingOption = _propertyDescriptor.Options.Where(o => o.Name == name).FirstOrDefault(); if (existingOption == null && string.IsNullOrWhiteSpace(type)) { throw new NotSupportedException($"Unrecognized short identifier '{name}'; Define type and parameters of property in first occurance in {XamlCodeFile}."); } if (Enum.TryParse(type, out PropertyKind kind)) { if (existingOption != null) { if (existingOption.Kind != kind) { throw new NotSupportedException($"Multiple options with same name but different type not supported: {XamlCodeFile}:{name}"); } continue; } PropertyOptions options; switch (kind) { case PropertyKind.Slider: case PropertyKind.DoubleSlider: try { var sliderOptions = new SliderPropertyOptions { DefaultValue = double.Parse(value) }; var parameters = match.Groups["parameters"].Value; var split = parameters.Split('-'); int minIndex = 0; int minMultiplier = 1; if (string.IsNullOrEmpty(split[0])) { minIndex = 1; minMultiplier = -1; } sliderOptions.MinValue = minMultiplier * double.Parse(split[minIndex]); sliderOptions.MaxValue = double.Parse(split[minIndex + 1]); if (split.Length > 2 + minIndex) { sliderOptions.Step = double.Parse(split[split.Length - 1]); } options = sliderOptions; } catch (Exception ex) { Debug.WriteLine($"Unable to extract slider info from {value}({ex.Message})"); TrackingManager.TrackException(ex); continue; } break; case PropertyKind.TimeSpan: try { var sliderOptions = new SliderPropertyOptions { DefaultValue = TimeSpan.FromMilliseconds(double.Parse(value)) }; var parameters = match.Groups["parameters"].Value; var split = parameters.Split('-'); int minIndex = 0; int minMultiplier = 1; if (string.IsNullOrEmpty(split[0])) { minIndex = 1; minMultiplier = -1; } sliderOptions.MinValue = minMultiplier * double.Parse(split[minIndex]); sliderOptions.MaxValue = double.Parse(split[minIndex + 1]); if (split.Length > 2 + minIndex) { sliderOptions.Step = double.Parse(split[split.Length - 1]); } options = sliderOptions; } catch (Exception ex) { Debug.WriteLine($"Unable to extract slider info from {value}({ex.Message})"); TrackingManager.TrackException(ex); continue; } break; case PropertyKind.Enum: try { options = new PropertyOptions(); var split = value.Split('.'); var typeName = string.Join(".", split.Take(split.Length - 1)); var enumType = LookForTypeByName(typeName); options.DefaultValue = Enum.Parse(enumType, split.Last()); } catch (Exception ex) { Debug.WriteLine($"Unable to parse enum from {value}({ex.Message})"); TrackingManager.TrackException(ex); continue; } break; case PropertyKind.Bool: try { options = new PropertyOptions { DefaultValue = bool.Parse(value) }; } catch (Exception ex) { Debug.WriteLine($"Unable to parse bool from {value}({ex.Message})"); continue; } break; case PropertyKind.Brush: try { options = new PropertyOptions { DefaultValue = value }; } catch (Exception ex) { Debug.WriteLine($"Unable to parse bool from {value}({ex.Message})"); TrackingManager.TrackException(ex); continue; } break; default: options = new PropertyOptions { DefaultValue = value }; break; } options.Label = label; options.Name = name; options.OriginalString = match.Value; options.Kind = kind; options.IsTwoWayBinding = options.OriginalString.EndsWith("@"); proxy[name] = new ValueHolder(options.DefaultValue); _propertyDescriptor.Options.Add(options); } } } } }
private void GitHub_OnClick(object sender, RoutedEventArgs e) { TrackingManager.TrackEvent("Link", GitHub.NavigateUri.ToString()); }
private async void InfoAreaPivot_OnSelectionChanged(object sender, SelectionChangedEventArgs e) { if (InfoAreaPivot.SelectedItem != null) { if (DataContext is Pattern pattern) { TrackingManager.TrackEvent("PropertyGrid", (InfoAreaPivot.SelectedItem as FrameworkElement)?.Name, pattern.Name); } } if (HamburgerMenu.CurrentPattern == null) { return; } if (InfoAreaPivot.SelectedItem == PropertiesPivotItem) { // If we switch to the Properties Panel, we want to use a binded version of the Xaml Code. if (HamburgerMenu.CurrentPattern.HasXAMLCode) { _lastRenderedProperties = true; var t = UpdateXamlRenderAsync(HamburgerMenu.CurrentPattern.BindedXamlCode); } return; } if (HamburgerMenu.CurrentPattern.HasXAMLCode && InfoAreaPivot.SelectedItem == XamlPivotItem && _lastRenderedProperties) { // Use this flag so we don't re-render the XAML tab if we're switching from tabs other than the properties one. _lastRenderedProperties = false; // If we switch to the Live Preview, then we want to use the Value based Text XamlCodeRenderer.Text = HamburgerMenu.CurrentPattern.UpdatedXamlCode; var t = UpdateXamlRenderAsync(HamburgerMenu.CurrentPattern.UpdatedXamlCode); await XamlCodeRenderer.RevealPositionAsync(new Position(1, 1)); XamlCodeRenderer.Focus(FocusState.Programmatic); return; } if (HamburgerMenu.CurrentPattern.HasXAMLCode && InfoAreaPivot.SelectedItem == XamlReadOnlyPivotItem) { // Update Read-Only XAML tab on non-desktop devices to show changes to Properties XamlReadOnlyCodeRenderer.XamlSource = HamburgerMenu.CurrentPattern.UpdatedXamlCode; } if (HamburgerMenu.CurrentPattern.HasCSharpCode && InfoAreaPivot.SelectedItem == CSharpPivotItem) { CSharpCodeRenderer.CSharpSource = await HamburgerMenu.CurrentPattern.GetCSharpSourceAsync(); return; } if (HamburgerMenu.CurrentPattern.HasJavaScriptCode && InfoAreaPivot.SelectedItem == JavaScriptPivotItem) { JavaScriptCodeRenderer.JavaScriptSource = await HamburgerMenu.CurrentPattern.GetJavaScriptSourceAsync(); return; } }
private async void DocumentationTextblock_OnLinkClicked(object sender, LinkClickedEventArgs e) { TrackingManager.TrackEvent("Link", e.Link); await Launcher.LaunchUriAsync(new Uri(e.Link)); }
private async void NavigationFrame_Navigating(object sender, NavigatingCancelEventArgs navigationEventArgs) { ProcessPatternEditorTime(); MenuCategory category; if (navigationEventArgs.Parameter == null) { DataContext = null; HamburgerMenu.CurrentPattern = null; category = navigationEventArgs.Parameter as MenuCategory; if (category != null) { TrackingManager.TrackPage($"{navigationEventArgs.SourcePageType.Name} - {category.Name}"); } else { TrackingManager.TrackPage($"{navigationEventArgs.SourcePageType.Name}"); } HideInfoArea(); } else { TrackingManager.TrackPage(navigationEventArgs.SourcePageType.Name); Commands.Clear(); ShowInfoArea(); var patternName = navigationEventArgs.Parameter.ToString(); HamburgerMenu.CurrentPattern = await Patterns.GetPatternByName(patternName); DataContext = HamburgerMenu.CurrentPattern; if (HamburgerMenu.CurrentPattern == null) { HideInfoArea(); return; } category = await Patterns.GetCategoryByPattern(HamburgerMenu.CurrentPattern); await Patterns.PushRecentPattern(HamburgerMenu.CurrentPattern); var propertyDesc = HamburgerMenu.CurrentPattern.PropertyDescriptor; InfoAreaPivot.Items.Clear(); if (propertyDesc != null) { _xamlRenderer.DataContext = propertyDesc.Expando; } Title.Text = HamburgerMenu.CurrentPattern.Name; if (propertyDesc != null && propertyDesc.Options.Count > 0) { InfoAreaPivot.Items.Add(PropertiesPivotItem); } if (HamburgerMenu.CurrentPattern.HasXAMLCode) { if (AnalyticsInfo.VersionInfo.GetDeviceFormFactor() != DeviceFormFactor.Desktop || HamburgerMenu.CurrentPattern.DisableXamlEditorRendering) { // Only makes sense (and works) for now to show Live Xaml on Desktop, so fallback to old system here otherwise. XamlReadOnlyCodeRenderer.XamlSource = HamburgerMenu.CurrentPattern.UpdatedXamlCode; InfoAreaPivot.Items.Add(XamlReadOnlyPivotItem); } else { XamlCodeRenderer.Text = HamburgerMenu.CurrentPattern.UpdatedXamlCode; InfoAreaPivot.Items.Add(XamlPivotItem); _xamlCodeRendererSupported = true; } InfoAreaPivot.SelectedIndex = 0; } if (HamburgerMenu.CurrentPattern.HasCSharpCode) { CSharpCodeRenderer.CSharpSource = await this.HamburgerMenu.CurrentPattern.GetCSharpSourceAsync(); InfoAreaPivot.Items.Add(CSharpPivotItem); } if (HamburgerMenu.CurrentPattern.HasJavaScriptCode) { JavaScriptCodeRenderer.CSharpSource = await this.HamburgerMenu.CurrentPattern.GetJavaScriptSourceAsync(); InfoAreaPivot.Items.Add(JavaScriptPivotItem); } if (!string.IsNullOrEmpty(HamburgerMenu.CurrentPattern.CodeUrl)) { GitHub.NavigateUri = new Uri(HamburgerMenu.CurrentPattern.CodeUrl); GitHub.Visibility = Visibility.Visible; } else { GitHub.Visibility = Visibility.Collapsed; } if (HamburgerMenu.CurrentPattern.HasDocumentation) { var docs = await this.HamburgerMenu.CurrentPattern.GetDocumentationAsync(); if (!string.IsNullOrWhiteSpace(docs)) { DocumentationTextblock.Text = docs; InfoAreaPivot.Items.Add(DocumentationPivotItem); } } if (InfoAreaPivot.Items.Count == 0) { HideInfoArea(); } HamburgerMenu.Title = $"{category.Name} > {HamburgerMenu.CurrentPattern?.Name}"; ApplicationView.SetTitle(this, $"{category.Name} > {HamburgerMenu.CurrentPattern?.Name}"); } }