[UItest] Added dynamic view gallery (#5402)

This commit is contained in:
Pavel Yakovlev 2019-04-02 05:45:41 +03:00 коммит произвёл Shane Neuville
Родитель 35f830502e
Коммит 9bb6c24b49
2 изменённых файлов: 437 добавлений и 0 удалений

Просмотреть файл

@ -342,6 +342,7 @@ namespace Xamarin.Forms.Controls
new GalleryPageFactory(() => new VisualGallery(), "Visual Gallery"),
new GalleryPageFactory(() => new WebViewCoreGalleryPage(), "WebView Gallery"),
new GalleryPageFactory(() => new WkWebViewCoreGalleryPage(), "WkWebView Gallery"),
new GalleryPageFactory(() => new DynamicViewGallery(), "Dynamic ViewGallery"),
//pages
new GalleryPageFactory(() => new RootContentPage ("Content"), "RootPages Gallery"),
new GalleryPageFactory(() => new MasterDetailPageTabletPage(), "MasterDetailPage Tablet Page"),

Просмотреть файл

@ -0,0 +1,436 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Xamarin.Forms.Controls
{
public class DynamicViewGallery : ContentPage
{
Dictionary<string, (Func<View> ctor, NamedAction[] methods)> _testedTypes;
HashSet<string> _exceptProperties = new HashSet<string>
{
AutomationIdProperty.PropertyName,
ClassIdProperty.PropertyName,
"StyleId",
};
View _element;
StackLayout _propertyLayout;
StackLayout _pageContent;
Picker _selector;
public DynamicViewGallery()
{
_testedTypes = new Dictionary<string, (Func<View> ctor, NamedAction[] methods)>
{
{ nameof(ActivityIndicator), (() => new ActivityIndicator() { IsRunning = false }, null) },
{ nameof(ProgressBar), (() => new ProgressBar(), null) },
{ nameof(Button), (() => new Button { Text = "Button" }, null) },
{ nameof(Label), (() => new Label { Text = "label" }, null) },
{ nameof(Entry), (() => new Entry(), null) },
{ nameof(Editor), (() => new Editor(), null) },
{ nameof(Image), (() => new Image { Source = ImageSource.FromFile("cover1.jpg") }, null) },
{ nameof(ImageButton),(() => new ImageButton { Source = "bank.png"}, null) },
{ nameof(WebView), (() => new WebView(), null) },
{ nameof(SearchBar), (() => new SearchBar(), null) },
{ nameof(Stepper), (() => new Stepper(), null) },
{ nameof(Switch), (() => new Switch(), null) },
{ nameof(Picker), GetPicker()},
{ nameof(DatePicker), (() => new DatePicker(), null) },
{ nameof(TimePicker), (() => new TimePicker(), null) },
{ nameof(ListView), (() => new ListView(), null) },
{ nameof(BoxView), (() => new BoxView(), null) },
};
_selector = new Picker();
foreach (var item in _testedTypes)
_selector.Items.Add(item.Key.ToString());
_selector.SelectedIndexChanged += TypeSelected;
var selectorGrid = new Grid
{
Padding = 0,
ColumnSpacing = 6,
RowSpacing = 6,
MinimumHeightRequest = 40,
ColumnDefinitions = {
new ColumnDefinition { Width = 150 },
new ColumnDefinition { Width = GridLength.Star }
}
};
selectorGrid.AddChild(new Label { Text = "Control:" }, 0, 0);
selectorGrid.AddChild(_selector, 1, 0);
_propertyLayout = new StackLayout
{
Spacing = 10,
Padding = 10
};
Content = _pageContent = new StackLayout
{
Padding = 10,
Spacing = 10,
Children =
{
selectorGrid,
new ScrollView
{
Margin = new Thickness(-10, 0),
Content = _propertyLayout
},
new BoxView
{
HeightRequest = 1,
Margin = new Thickness(-10, 0),
Color = Color.Black
}
}
};
}
void OnElementUpdated(View oldElement)
{
if (oldElement != null)
_pageContent.Children.Remove(oldElement);
_propertyLayout.Children.Clear();
if (_element == null)
return;
var elementType = _element.GetType();
var publicProperties = elementType
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.CanRead && p.CanWrite && !_exceptProperties.Contains(p.Name));
// BindableProperty used to clean property values
var bindableProperties = elementType
.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)
.Where(p => p.FieldType.IsAssignableFrom(typeof(BindableProperty)))
.Select(p => (BindableProperty)p.GetValue(_element));
foreach (var property in publicProperties)
{
if (property.PropertyType == typeof(Color))
{
var colorPicker = new ColorPicker
{
Title = property.Name,
Color = (Color)property.GetValue(_element)
};
colorPicker.ColorPicked += (_, e) => property.SetValue(_element, e.Color);
_propertyLayout.Children.Add(colorPicker);
}
else if (property.PropertyType == typeof(string))
{
_propertyLayout.Children.Add(CreateStringPicker(property));
}
else if (property.PropertyType == typeof(double) ||
property.PropertyType == typeof(float) ||
property.PropertyType == typeof(int))
{
_propertyLayout.Children.Add(
CreateValuePicker(property, bindableProperties.FirstOrDefault(p => p.PropertyName == property.Name)));
}
else if (property.PropertyType == typeof(bool))
{
_propertyLayout.Children.Add(CreateBooleanPicker(property));
}
else if (property.PropertyType == typeof(Thickness))
{
_propertyLayout.Children.Add(CreateThicknessPicker(property));
}
else
{
//_propertyLayout.Children.Add(new Label { Text = $"//TODO: {property.Name} ({property.PropertyType})", TextColor = Color.Gray });
}
}
var customMethods = _testedTypes[elementType.Name].methods;
if (customMethods != null)
{
_propertyLayout.Children.Add(new Label {
Text = "Custom methods",
FontSize = 20,
Margin = 6
});
foreach (var method in customMethods)
{
_propertyLayout.Children.Add(new Button
{
Text = method.Name,
FontAttributes = FontAttributes.Bold,
Padding = 6,
Command = new Command(() => method.Action(_element))
});
}
}
_pageContent.Children.Add(_element);
}
void TypeSelected(object sender, EventArgs e)
{
var oldElement = _element;
try
{
_element = _testedTypes[(string)_selector.SelectedItem].ctor();
}
catch
{
_element = null;
}
OnElementUpdated(oldElement);
}
Dictionary<string, (double min, double max)> _minMaxProperties = new Dictionary<string, (double min, double max)>
{
{ ScaleProperty.PropertyName, (0d, 1d) },
{ ScaleXProperty.PropertyName, (0d, 1d) },
{ ScaleYProperty.PropertyName, (0d, 1d) },
{ OpacityProperty.PropertyName, (0d, 1d) },
{ RotationProperty.PropertyName, (0d, 360d) },
{ RotationXProperty.PropertyName, (0d, 360d) },
{ RotationYProperty.PropertyName, (0d, 360d) },
{ View.MarginProperty.PropertyName, (-100, 100) },
{ PaddingElement.PaddingProperty.PropertyName, (-100, 100) },
};
Grid CreateValuePicker(PropertyInfo property, BindableProperty bindableProperty)
{
var min = 0d;
var max = 100d;
if (_minMaxProperties.ContainsKey(property.Name))
{
min = _minMaxProperties[property.Name].min;
max = _minMaxProperties[property.Name].max;
}
var isInt = property.PropertyType == typeof(int);
var value = isInt ? (int)property.GetValue(_element) : (double)property.GetValue(_element);
var slider = new Slider(min, max, value);
var actions = new Grid
{
Padding = 0,
ColumnSpacing = 6,
RowSpacing = 6,
ColumnDefinitions =
{
new ColumnDefinition { Width = GridLength.Star },
new ColumnDefinition { Width = 40 }
}
};
actions.AddChild(new Label { Text = property.Name, FontAttributes = FontAttributes.Bold }, 0, 0, 2);
if (bindableProperty != null)
{
actions.AddChild(new Button
{
Text = "X",
TextColor = Color.White,
BackgroundColor = Color.DarkRed,
WidthRequest = 28,
HeightRequest = 28,
Margin = 0,
Padding = 0,
Command = new Command(() => _element.ClearValue(bindableProperty))
}, 1, 0);
}
var valueLabel = new Label
{
Text = slider.Value.ToString(isInt ? "0" : "0.#"),
HorizontalOptions = LayoutOptions.End
};
slider.ValueChanged += (_, e) =>
{
if (isInt)
property.SetValue(_element, (int)e.NewValue);
else
property.SetValue(_element, e.NewValue);
valueLabel.Text = e.NewValue.ToString(isInt ? "0" : "0.#");
};
actions.AddChild(slider, 0, 1);
actions.AddChild(valueLabel, 1, 1);
return actions;
}
Grid CreateThicknessPicker(PropertyInfo property)
{
var grid = new Grid
{
Padding = 0,
RowSpacing = 3,
ColumnSpacing = 3,
ColumnDefinitions =
{
new ColumnDefinition { Width = 50 },
new ColumnDefinition { Width = GridLength.Star },
new ColumnDefinition { Width = 30 }
},
};
grid.AddChild(new Label { Text = property.Name, FontAttributes = FontAttributes.Bold }, 0, 0, 2);
var val = (Thickness)property.GetValue(_element);
var sliders = new Slider[4];
var valueLabels = new Label[4];
for (int i = 0; i < 4; i++)
{
sliders[i] = new Slider
{
VerticalOptions = LayoutOptions.Center,
Minimum = 0,
Maximum = 100
};
var row = i + 1;
switch (i)
{
case 0:
sliders[i].Value = val.Left;
grid.AddChild(new Label { Text = nameof(val.Left) }, 0, row);
break;
case 1:
sliders[i].Value = val.Top;
grid.AddChild(new Label { Text = nameof(val.Top) }, 0, row);
break;
case 2:
sliders[i].Value = val.Right;
grid.AddChild(new Label { Text = nameof(val.Right) }, 0, row);
break;
case 3:
sliders[i].Value = val.Bottom;
grid.AddChild(new Label { Text = nameof(val.Bottom) }, 0, row);
break;
}
valueLabels[i] = new Label { Text = sliders[i].Value.ToString("0") };
grid.AddChild(sliders[i], 1, row);
grid.AddChild(valueLabels[i], 2, row);
sliders[i].ValueChanged += ThicknessChanged;
}
void ThicknessChanged(object sender, ValueChangedEventArgs e)
{
property.SetValue(_element, new Thickness(sliders[0].Value, sliders[1].Value, sliders[2].Value, sliders[3].Value));
for (int i = 0; i < valueLabels.Length; i++)
valueLabels[i].Text = sliders[i].Value.ToString("0");
}
return grid;
}
Grid CreateBooleanPicker(PropertyInfo property)
{
var grid = new Grid
{
Padding = 0,
ColumnSpacing = 6,
RowSpacing = 6,
ColumnDefinitions =
{
new ColumnDefinition { Width = GridLength.Star },
new ColumnDefinition { Width = 50 }
}
};
grid.AddChild(new Label { Text = property.Name, FontAttributes = FontAttributes.Bold }, 0, 0);
var boolSwitch = new Switch
{
IsToggled = (bool)property.GetValue(_element),
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center
};
boolSwitch.Toggled += (_, e) => property.SetValue(_element, e.Value);
grid.AddChild(boolSwitch, 1, 0);
_element.PropertyChanged += (_, e) =>
{
if (e.PropertyName == property.Name)
{
var newVal = (bool)property.GetValue(_element);
if (newVal != boolSwitch.IsToggled)
boolSwitch.IsToggled = newVal;
}
};
return grid;
}
Grid CreateStringPicker(PropertyInfo property)
{
var grid = new Grid
{
Padding = 0,
ColumnSpacing = 6,
RowSpacing = 6
};
grid.AddChild(new Label { Text = property.Name, FontAttributes = FontAttributes.Bold }, 0, 0);
var entry = new Entry
{
Text = (string)property.GetValue(_element),
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand
};
entry.TextChanged += (_, e) => property.SetValue(_element, e.NewTextValue);
grid.AddChild(entry, 0, 1);
_element.PropertyChanged += (_, e) =>
{
if (e.PropertyName == property.Name)
{
var newVal = (string)property.GetValue(_element);
if (newVal != entry.Text)
entry.Text = newVal;
}
};
return grid;
}
class NamedAction
{
public string Name { get; set; }
public Action<View> Action { get; set; }
}
(Func<View> ctor, NamedAction[] methods) GetPicker()
{
return (ctor: () =>
{
var picker = new Picker();
picker.Items.Add("item 1");
picker.Items.Add("item 2");
return picker;
}, methods: new[] {
new NamedAction {
Name = "Add item",
Action = (p) => (p as Picker).Items.Add("item")
},
new NamedAction {
Name = "Remove item last item",
Action = (p) => {
var picker = (Picker)p;
if (picker.Items.Count > 0)
picker.Items.RemoveAt(picker.Items.Count - 1);
}
},
new NamedAction {
Name = "Clear",
Action = (p) => (p as Picker).Items.Clear()
}
}
);
}
}
}