diff options
author | Eric Maupin <ermaup@microsoft.com> | 2019-03-29 22:01:02 +0300 |
---|---|---|
committer | Eric Maupin <ermaup@microsoft.com> | 2019-04-24 22:26:09 +0300 |
commit | 97898744069fc854d59d7a855dd1b72c04106ee3 (patch) | |
tree | 3713c4225d0741602a33be6ec53c3984a2b632c5 /Xamarin.PropertyEditing.Windows | |
parent | 1ee3dc571f4e7d65b87afae5c701b9a1fa4e5eea (diff) |
[Win] Variants
Diffstat (limited to 'Xamarin.PropertyEditing.Windows')
8 files changed, 309 insertions, 11 deletions
diff --git a/Xamarin.PropertyEditing.Windows/BoolsToVisibilityConverter.cs b/Xamarin.PropertyEditing.Windows/BoolsToVisibilityConverter.cs index 9923ddd..770ac2b 100644 --- a/Xamarin.PropertyEditing.Windows/BoolsToVisibilityConverter.cs +++ b/Xamarin.PropertyEditing.Windows/BoolsToVisibilityConverter.cs @@ -10,15 +10,15 @@ namespace Xamarin.PropertyEditing.Windows { public object Convert (object[] values, Type targetType, object parameter, CultureInfo culture) { - bool value = false; if (values != null) { - value = (bool)values[0]; - for (int i = 1; i < values.Length; i++) { - value = value && (bool)values[i]; + for (int i = 0; i < values.Length; i++) { + object v = values[i]; + if (v == DependencyProperty.UnsetValue || !(bool) v) + return Visibility.Collapsed; } } - return (value) ? Visibility.Visible : Visibility.Collapsed; + return Visibility.Visible; } public object[] ConvertBack (object value, Type[] targetTypes, object parameter, CultureInfo culture) diff --git a/Xamarin.PropertyEditing.Windows/NegativeThicknessConverter.cs b/Xamarin.PropertyEditing.Windows/NegativeThicknessConverter.cs new file mode 100644 index 0000000..a7580ac --- /dev/null +++ b/Xamarin.PropertyEditing.Windows/NegativeThicknessConverter.cs @@ -0,0 +1,31 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; + +namespace Xamarin.PropertyEditing.Windows +{ + /// <summary> + /// Inverts a given thickness + /// </summary> + internal class NegativeThicknessConverter + : IValueConverter + { + public object Convert (object value, Type targetType, object parameter, CultureInfo culture) + { + Thickness t; + if (value is Thickness thickness) { + t = thickness; + } else { + t = new Thickness (0); + } + + return new Thickness (t.Left * -1, t.Top * -1, t.Right * -1, t.Bottom * -1); + } + + public object ConvertBack (object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException (); + } + } +}
\ No newline at end of file diff --git a/Xamarin.PropertyEditing.Windows/PropertyPresenter.cs b/Xamarin.PropertyEditing.Windows/PropertyPresenter.cs index cb1dc55..ebca5e3 100644 --- a/Xamarin.PropertyEditing.Windows/PropertyPresenter.cs +++ b/Xamarin.PropertyEditing.Windows/PropertyPresenter.cs @@ -1,9 +1,16 @@ +using System; using System.Collections; +using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Media; +using Xamarin.PropertyEditing.ViewModels; namespace Xamarin.PropertyEditing.Windows { + [TemplatePart (Name = "addVariant", Type = typeof(ButtonBase))] + [TemplatePart (Name = "removeVariant", Type = typeof (ButtonBase))] internal class PropertyPresenter : ContentControl { @@ -16,6 +23,7 @@ namespace Xamarin.PropertyEditing.Windows { Loaded += OnLoaded; Unloaded += OnUnloaded; + DataContextChanged += OnDataContextChanged; } public static readonly DependencyProperty LabelProperty = DependencyProperty.Register ( @@ -56,6 +64,95 @@ namespace Xamarin.PropertyEditing.Windows set { SetValue (ShowPropertyButtonProperty, value); } } + public static readonly DependencyProperty ShowVariantsProperty = DependencyProperty.Register ( + "ShowVariants", typeof(bool), typeof(PropertyPresenter), new PropertyMetadata (true)); + + public bool ShowVariants + { + get { return (bool) GetValue (ShowVariantsProperty); } + set { SetValue (ShowVariantsProperty, value); } + } + + public static readonly DependencyProperty LineBrushProperty = DependencyProperty.Register ( + "LineBrush", typeof (Brush), typeof (PropertyPresenter), new FrameworkPropertyMetadata (Brushes.White, FrameworkPropertyMetadataOptions.AffectsRender)); + + public Brush LineBrush + { + get { return (Brush)GetValue (LineBrushProperty); } + set { SetValue (LineBrushProperty, value); } + } + + public static readonly DependencyProperty LineThicknessProperty = DependencyProperty.Register ( + "LineThickness", typeof (double), typeof (PropertyPresenter), new FrameworkPropertyMetadata (1d, FrameworkPropertyMetadataOptions.AffectsRender)); + + public static readonly DependencyProperty VariantBackgroundBrushProperty = DependencyProperty.Register ( + "VariantBackgroundBrush", typeof(Brush), typeof(PropertyPresenter), new FrameworkPropertyMetadata (default(Brush), FrameworkPropertyMetadataOptions.AffectsRender)); + + public Brush VariantBackgroundBrush + { + get { return (Brush) GetValue (VariantBackgroundBrushProperty); } + set { SetValue (VariantBackgroundBrushProperty, value); } + } + + public double LineThickness + { + get { return (double)GetValue (LineThicknessProperty); } + set { SetValue (LineThicknessProperty, value); } + } + + public override void OnApplyTemplate () + { + this.addButton = GetTemplateChild ("addVariant") as ButtonBase; + this.removeButton = GetTemplateChild ("removeVariant") as ButtonBase; + if (this.removeButton != null) { + this.removeButton.Click += OnRemoveVariantClicked; + } + + this.variationRow = (RowDefinition) GetTemplateChild ("variationRow"); + this.variationsList = GetTemplateChild ("variationsList") as ItemsControl; + } + + protected override void OnRender (DrawingContext drawingContext) + { + if (this.pvm == null) + return; + + base.OnRender (drawingContext); + if (!this.pvm.HasVariantChildren && !this.pvm.IsVariant) + return; + + drawingContext.DrawRectangle (VariantBackgroundBrush, null, new Rect (0, 0, ActualWidth, ActualHeight)); + + var pen = new Pen (LineBrush, LineThickness); + double centerLeft = Math.Round (Padding.Left / 2); + + if (this.pvm.HasVariantChildren) { + if (this.addButton == null) + throw new InvalidOperationException ("addVariant button missing from PropertyPresenter template"); + + Point start = this.addButton.TranslatePoint (new Point (this.addButton.ActualWidth / 2, this.addButton.ActualHeight), this); + drawingContext.DrawLine (pen, new Point (centerLeft, start.Y), new Point (centerLeft, ActualHeight)); + } else if (this.pvm.IsVariant) { + if (this.removeButton == null) + throw new InvalidOperationException ("removeButton button missing from PropertyPresenter template"); + + Point start = new Point (centerLeft, 0); + Point end = new Point (centerLeft, (this.pvm.GetIsLastVariant ()) ? this.variationRow.ActualHeight / 2 : ActualHeight); + drawingContext.DrawLine (pen, start, end); + start = new Point (centerLeft, this.variationRow.ActualHeight / 2); + end = this.removeButton.TranslatePoint (new Point (0, this.removeButton.ActualHeight / 2), this); + drawingContext.DrawLine (pen, start, new Point (end.X, start.Y)); + start = new Point (end.X + this.removeButton.ActualWidth, start.Y); + end = this.variationsList.TranslatePoint (new Point (0, 0), this); + drawingContext.DrawLine (pen, start, new Point (end.X, start.Y)); + } + } + + private PropertyViewModel pvm; + private ButtonBase addButton, removeButton; + private RowDefinition variationRow; + private ItemsControl variationsList; + private void OnLoaded (object sender, RoutedEventArgs e) { IsSubProperty = this.FindParentUnless<PropertyPresenter, PropertyEditorPanel>() != null; @@ -64,6 +161,54 @@ namespace Xamarin.PropertyEditing.Windows private void OnUnloaded (object sender, RoutedEventArgs e) { IsSubProperty = false; + DataContext = null; + } + + private void OnDataContextChanged (object sender, DependencyPropertyChangedEventArgs e) + { + if (this.pvm != null) { + this.pvm.CreateVariantRequested -= OnCreateVariantRequested; + } + + this.pvm = e.NewValue as PropertyViewModel; + if (this.pvm != null && this.pvm.RequestCreateVariantCommand.CanExecute (null)) { + this.pvm.CreateVariantRequested += OnCreateVariantRequested; + } + } + + private void OnCreateVariantRequested (object sender, CreateVariantEventArgs e) + { + var variation = CreateVariantWindow.RequestVariant (this, this.pvm.Property); + if (variation != null) + e.Variation = Task.FromResult (variation); + } + + private void OnRemoveVariantClicked (object sender, RoutedEventArgs e) + { + var parent = (FrameworkElement) VisualTreeHelper.GetParent (this); + while (!(parent != null && parent.DataContext is PanelGroupViewModel)) { + parent = (FrameworkElement) VisualTreeHelper.GetParent (parent); + } + + // Ensure we re-draw background and lines if we removed a variant + if (parent != null) { + bool start = false; + int count = VisualTreeHelper.GetChildrenCount (parent); + for (int i = 0; i < count; i++) { + var element = (FrameworkElement) VisualTreeHelper.GetChild (parent, i); + if (element.DataContext == DataContext) + continue; + + if (element.DataContext is PropertyViewModel elementVm) { + if (Equals (elementVm.Property, this.pvm.Property)) { + start = true; + element = element.FindChildOrSelf<PropertyPresenter> (); + element.InvalidateVisual (); + } else if (start) + return; + } + } + } } } }
\ No newline at end of file diff --git a/Xamarin.PropertyEditing.Windows/Themes/Resources.xaml b/Xamarin.PropertyEditing.Windows/Themes/Resources.xaml index cfd9603..e191771 100644 --- a/Xamarin.PropertyEditing.Windows/Themes/Resources.xaml +++ b/Xamarin.PropertyEditing.Windows/Themes/Resources.xaml @@ -4,7 +4,9 @@ xmlns:drawing="clr-namespace:Xamarin.PropertyEditing.Drawing;assembly=Xamarin.PropertyEditing" xmlns:theme="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero" xmlns:prop="clr-namespace:Xamarin.PropertyEditing.Properties;assembly=Xamarin.PropertyEditing" - xmlns:system="clr-namespace:System;assembly=mscorlib"> + xmlns:system="clr-namespace:System;assembly=mscorlib" + xmlns:vm="clr-namespace:Xamarin.PropertyEditing.ViewModels;assembly=Xamarin.PropertyEditing" + xmlns:pe="clr-namespace:Xamarin.PropertyEditing;assembly=Xamarin.PropertyEditing"> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="MenuButtonStyle.xaml" /> @@ -43,6 +45,7 @@ <local:BoolsToVisibilityConverter x:Key="BoolsToVisibilityConverter" /> <local:CommonBrushToBrushConverter x:Key="BrushConverter" /> <local:CommonColorToColorConverter x:Key="ColorConverter" /> + <local:NegativeThicknessConverter x:Key="NegativeThicknessConverter" /> <local:GroupedEditorPropertySelector x:Key="GroupedEditorSelector" /> <Style TargetType="local:CombinablePredefinedValuesEditor"> @@ -1504,8 +1507,40 @@ </Setter> </Style> + <Style x:Key="VariantButton" TargetType="Button"> + <Setter Property="Background" Value="Transparent" /> + <Setter Property="BorderThickness" Value="0" /> + <Setter Property="FontSize" Value="9" /> + <Setter Property="Height" Value="10" /> + <Setter Property="Width" Value="10" /> + <Setter Property="Foreground" Value="{DynamicResource PropertyButtonBorderBrush}" /> + <Setter Property="VerticalAlignment" Value="Center" /> + <Setter Property="SnapsToDevicePixels" Value="True" /> + <Setter Property="UseLayoutRounding" Value="True" /> + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="Button"> + <Border Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}"> + <ContentPresenter /> + </Border> + </ControlTemplate> + </Setter.Value> + </Setter> + <Setter Property="FocusVisualStyle" Value="{DynamicResource GenericVisualFocusStyle}" /> + <Style.Triggers> + <Trigger Property="IsMouseOver" Value="True"> + <Trigger.Setters> + <Setter Property="Background" Value="{DynamicResource ToggleItemMouseOverBackgroundBrush}" /> + </Trigger.Setters> + </Trigger> + </Style.Triggers> + </Style> + <Style TargetType="local:PropertyPresenter"> <Setter Property="MinHeight" Value="24" /> + <Setter Property="VariantBackgroundBrush" Value="{DynamicResource PanelGroupSecondaryBackgroundBrush}" /> + <Setter Property="LineBrush" Value="{DynamicResource VariantLineBrush}" /> + <Setter Property="SnapsToDevicePixels" Value="True" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="local:PropertyPresenter"> @@ -1518,17 +1553,74 @@ <ColumnDefinition Name="propertyButtonColumn" Width="12" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> + <RowDefinition Name="variationRow" Height="Auto" /> <RowDefinition Height="Auto" MinHeight="24" /> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> - <ToggleButton Name="delveButton" Grid.Column="0" Style="{DynamicResource StandaloneToggleStyle}" Width="11" Height="11" Visibility="{Binding HasItems,ElementName=subProperties,Converter={StaticResource BoolToVisibilityConverter}}" /> - <TextBlock Name="Label" Margin="0,3,0,3" Grid.Row="0" Grid.Column="1" Text="{TemplateBinding Label}" ToolTip="{Binding Property.Description,Mode=OneTime}" VerticalAlignment="Center" TextWrapping="NoWrap" TextTrimming="CharacterEllipsis" /> - <ContentPresenter Grid.Row="0" Grid.RowSpan="2" Grid.Column="2" Margin="4,2,0,2" IsEnabled="{Binding Property.CanWrite}" MinHeight="20" /> - <local:PropertyButton Grid.Row="0" Grid.RowSpan="2" Grid.Column="3" ValueSource="{Binding ValueSource}" IsEnabled="{Binding Property.CanWrite,Mode=OneTime}"/> + <Button Name="addVariant" Grid.Column="0" Grid.Row="1" Margin="{TemplateBinding Padding,Converter={StaticResource NegativeThicknessConverter}}" Style="{StaticResource VariantButton}" Command="{Binding RequestCreateVariantCommand,Mode=OneTime}" ToolTip="{x:Static prop:Resources.AddVariant}" AutomationProperties.Name="{x:Static prop:Resources.AddVariant}"> + <Button.Visibility> + <MultiBinding Converter="{StaticResource BoolsToVisibilityConverter}"> + <Binding Path="HasVariations" Mode="OneTime" /> + <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="ShowVariants" /> + <Binding Path="IsEnabled" RelativeSource="{RelativeSource Self}" /> + </MultiBinding> + </Button.Visibility> + + <Grid Width="6" Height="6" VerticalAlignment="Center" HorizontalAlignment="Center"> + <Line X1="0" X2="6" Y1="3" Y2="3" Stroke="{Binding RelativeSource={RelativeSource AncestorType=Button},Path=Foreground}" StrokeThickness="1" /> + <Line X1="3" X2="3" Y1="0" Y2="6" Stroke="{Binding RelativeSource={RelativeSource AncestorType=Button},Path=Foreground}" StrokeThickness="1" /> + </Grid> + </Button> + + <Grid Grid.Column="0" Grid.ColumnSpan="4" Grid.Row="0" Visibility="{Binding IsVariant,Mode=OneTime,Converter={StaticResource BoolToVisibilityConverter},FallbackValue=Collapsed}"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + + <Button Name="removeVariant" Grid.Column="0" Command="{Binding RemoveVariationCommand,Mode=OneTime}" Style="{StaticResource VariantButton}" ToolTip="{x:Static prop:Resources.RemoveVariant}" AutomationProperties.Name="{x:Static prop:Resources.RemoveVariant}"> + <Grid RenderTransformOrigin="0.5,0.5" Width="6" Height="6" VerticalAlignment="Center" HorizontalAlignment="Center"> + <Grid.RenderTransform> + <RotateTransform Angle="45" /> + </Grid.RenderTransform> + + <Line X1="0" X2="6" Y1="3" Y2="3" Stroke="{Binding RelativeSource={RelativeSource AncestorType=Button},Path=Foreground}" StrokeThickness="1" /> + <Line X1="3" X2="3" Y1="0" Y2="6" Stroke="{Binding RelativeSource={RelativeSource AncestorType=Button},Path=Foreground}" StrokeThickness="1" /> + </Grid> + </Button> + + <ItemsControl Margin="5,2,0,2" Name="variationsList" Grid.Column="1" ItemsSource="{Binding Variation,Mode=OneTime}" Focusable="False"> + <ItemsControl.ItemsPanel> + <ItemsPanelTemplate> + <VirtualizingStackPanel Orientation="Horizontal" /> + </ItemsPanelTemplate> + </ItemsControl.ItemsPanel> + <ItemsControl.ItemTemplate> + <DataTemplate DataType="pe:PropertyVariationOption"> + <Border Background="{DynamicResource VariantBackgroundBrush}" MinHeight="14" Padding="5,0,5,0" Margin="0,0,2,0"> + <!-- We need to base this font size on some VS setting. !--> + <TextBlock Foreground="{DynamicResource VariantForegroundBrush}" FontSize="9px" VerticalAlignment="Center"> + <TextBlock.Text> + <MultiBinding StringFormat="{}{0}: {1}" Mode="OneTime"> + <Binding Path="Category" Mode="OneTime" /> + <Binding Path="Name" Mode="OneTime" /> + </MultiBinding> + </TextBlock.Text> + </TextBlock> + </Border> + </DataTemplate> + </ItemsControl.ItemTemplate> + </ItemsControl> + </Grid> + + <ToggleButton Name="delveButton" Grid.Column="0" Grid.Row="0" Style="{DynamicResource StandaloneToggleStyle}" Width="11" Height="11" Visibility="{Binding HasItems,ElementName=subProperties,Converter={StaticResource BoolToVisibilityConverter}}" /> + <TextBlock Name="Label" Margin="0,3,0,3" Grid.Row="1" Grid.Column="1" Text="{TemplateBinding Label}" ToolTip="{Binding Property.Description,Mode=OneTime}" VerticalAlignment="Center" TextWrapping="NoWrap" TextTrimming="CharacterEllipsis" /> + <ContentPresenter Grid.Row="1" Grid.RowSpan="2" Grid.Column="2" Margin="4,2,0,2" IsEnabled="{Binding Property.CanWrite}" MinHeight="20" /> + <local:PropertyButton Grid.Row="1" Grid.RowSpan="2" Grid.Column="3" ValueSource="{Binding ValueSource}" IsEnabled="{Binding Property.CanWrite,Mode=OneTime}"/> - <ItemsControl Name="subProperties" Background="{DynamicResource PanelGroupSecondaryBackgroundBrush}" Margin="-19,0,0,0" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="4" ItemsSource="{TemplateBinding ItemsSource}" ItemTemplate="{DynamicResource PropertyEditorTemplate}"> + <ItemsControl Name="subProperties" Background="{DynamicResource PanelGroupSecondaryBackgroundBrush}" Margin="{TemplateBinding Padding,Converter={StaticResource NegativeThicknessConverter}}" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="4" ItemsSource="{TemplateBinding ItemsSource}" ItemTemplate="{DynamicResource PropertyEditorTemplate}"> <ItemsControl.Visibility> <MultiBinding Converter="{StaticResource BoolsToVisibilityConverter}"> <Binding ElementName="delveButton" Path="IsChecked" /> diff --git a/Xamarin.PropertyEditing.Windows/Themes/VS.Dark.xaml b/Xamarin.PropertyEditing.Windows/Themes/VS.Dark.xaml index 880df43..cb76cf1 100644 --- a/Xamarin.PropertyEditing.Windows/Themes/VS.Dark.xaml +++ b/Xamarin.PropertyEditing.Windows/Themes/VS.Dark.xaml @@ -16,6 +16,10 @@ <SolidColorBrush x:Key="GroupBackgroundBrush">#252526</SolidColorBrush> <SolidColorBrush x:Key="GroupBorderBrush">#434346</SolidColorBrush> + <SolidColorBrush x:Key="VariantForegroundBrush">#000000</SolidColorBrush> + <SolidColorBrush x:Key="VariantBackgroundBrush">#ABABAB</SolidColorBrush> + <SolidColorBrush x:Key="VariantLineBrush">#5E5E5E</SolidColorBrush> + <SolidColorBrush x:Key="AdvancedExpanderCollapsedForegroundBrush">#F1F1F1</SolidColorBrush> <SolidColorBrush x:Key="AdvancedExpanderMouseOverForegroundBrush">#007ACC</SolidColorBrush> <SolidColorBrush x:Key="AdvancedExpanderMouseOverBorderBrush">#3E3E40</SolidColorBrush> diff --git a/Xamarin.PropertyEditing.Windows/Themes/VS.Light.xaml b/Xamarin.PropertyEditing.Windows/Themes/VS.Light.xaml index 3e8279a..b881f7f 100644 --- a/Xamarin.PropertyEditing.Windows/Themes/VS.Light.xaml +++ b/Xamarin.PropertyEditing.Windows/Themes/VS.Light.xaml @@ -15,6 +15,10 @@ <SolidColorBrush x:Key="GroupBackgroundBrush">#FFFFFF</SolidColorBrush> <SolidColorBrush x:Key="GroupBorderBrush">#CCCEDB</SolidColorBrush> + <SolidColorBrush x:Key="VariantForegroundBrush">#FFFFFF</SolidColorBrush> + <SolidColorBrush x:Key="VariantBackgroundBrush">#767676</SolidColorBrush> + <SolidColorBrush x:Key="VariantLineBrush">#B7B7B7</SolidColorBrush> + <SolidColorBrush x:Key="AdvancedExpanderMouseOverForegroundBrush">#007ACC</SolidColorBrush> <SolidColorBrush x:Key="AdvancedExpanderMouseOverBorderBrush">#C9DEF5</SolidColorBrush> <SolidColorBrush x:Key="AdvancedExpanderMouseOverBackgroundBrush">#C9DEF5</SolidColorBrush> diff --git a/Xamarin.PropertyEditing.Windows/Xamarin.PropertyEditing.Windows.csproj b/Xamarin.PropertyEditing.Windows/Xamarin.PropertyEditing.Windows.csproj index d3d5f6d..5459fb1 100644 --- a/Xamarin.PropertyEditing.Windows/Xamarin.PropertyEditing.Windows.csproj +++ b/Xamarin.PropertyEditing.Windows/Xamarin.PropertyEditing.Windows.csproj @@ -99,6 +99,7 @@ <Compile Include="IPropertiesHost.cs" /> <Compile Include="MaterialDesignColorEditorControl.cs" /> <Compile Include="MultiplyMarginConverter.cs" /> + <Compile Include="NegativeThicknessConverter.cs" /> <Compile Include="NullVisibilityConverter.cs" /> <Compile Include="ObjectEditorControl.cs" /> <Compile Include="PreviewTemplateSelector.cs" /> diff --git a/Xamarin.PropertyEditing.Windows/XamlHelper.cs b/Xamarin.PropertyEditing.Windows/XamlHelper.cs index 7f3ac32..7ea99d5 100644 --- a/Xamarin.PropertyEditing.Windows/XamlHelper.cs +++ b/Xamarin.PropertyEditing.Windows/XamlHelper.cs @@ -23,6 +23,27 @@ namespace Xamarin.PropertyEditing.Windows } } + public static T FindChildOrSelf<T> (this UIElement self) + where T : UIElement + { + if (self == null) + throw new ArgumentNullException (nameof(self)); + + if (self is T t) + return t; + + int count = VisualTreeHelper.GetChildrenCount (self); + for (int i = 0; i < count; i++) { + if (VisualTreeHelper.GetChild (self, i) is UIElement element) { + var child = FindChildOrSelf<T> (element); + if (child != null) + return child; + } + } + + return null; + } + public static T FindParent<T> (this UIElement self) where T : UIElement { |