Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RadioButton] Fix Issue with BorderWidth, Incorrect spacing in Default Control Template. #13407

Merged
merged 8 commits into from
Mar 3, 2023
45 changes: 37 additions & 8 deletions src/Controls/src/Core/RadioButton.cs
Original file line number Diff line number Diff line change
Expand Up @@ -460,19 +460,32 @@ static void BindToTemplatedParent(BindableObject bindableObject, params Bindable

static View BuildDefaultTemplate()
{
var frame = new Frame
Border border = new Border()
{
HasShadow = false,
Padding = 6
};

BindToTemplatedParent(frame, BackgroundColorProperty, Microsoft.Maui.Controls.Frame.BorderColorProperty, HorizontalOptionsProperty,
BindToTemplatedParent(border, BackgroundColorProperty, HorizontalOptionsProperty,
MarginProperty, OpacityProperty, RotationProperty, ScaleProperty, ScaleXProperty, ScaleYProperty,
TranslationYProperty, TranslationXProperty, VerticalOptionsProperty);

border.SetBinding(Border.StrokeProperty,
new Binding(BorderColorProperty.PropertyName,
source: RelativeBindingSource.TemplatedParent));

border.SetBinding(Border.StrokeShapeProperty,
new Binding(CornerRadiusProperty.PropertyName, converter: new CornerRadiusToShape(),
source: RelativeBindingSource.TemplatedParent));

border.SetBinding(Border.StrokeThicknessProperty,
new Binding(BorderWidthProperty.PropertyName,
source: RelativeBindingSource.TemplatedParent));

var grid = new Grid
{
Padding = 2,
RowSpacing = 0,
ColumnSpacing = 6,
ColumnDefinitions = new ColumnDefinitionCollection {
new ColumnDefinition { Width = GridLength.Auto },
new ColumnDefinition { Width = GridLength.Star }
Expand Down Expand Up @@ -521,11 +534,11 @@ static View BuildDefaultTemplate()
grid.Add(checkMark);
grid.Add(contentPresenter, 1, 0);

frame.Content = grid;
border.Content = grid;

INameScope nameScope = new NameScope();
NameScope.SetNameScope(frame, nameScope);
nameScope.RegisterName(TemplateRootName, frame);
NameScope.SetNameScope(border, nameScope);
nameScope.RegisterName(TemplateRootName, border);
nameScope.RegisterName(UncheckedButton, normalEllipse);
nameScope.RegisterName(CheckedIndicator, checkMark);
nameScope.RegisterName("ContentPresenter", contentPresenter);
Expand All @@ -552,9 +565,9 @@ static View BuildDefaultTemplate()

visualStateGroups.Add(checkedStates);

VisualStateManager.SetVisualStateGroups(frame, visualStateGroups);
VisualStateManager.SetVisualStateGroups(border, visualStateGroups);

return frame;
return border;
}

/// <include file="../../docs/Microsoft.Maui.Controls/RadioButton.xml" path="//Member[@MemberName='ContentAsString']/Docs/*" />
Expand All @@ -568,5 +581,21 @@ public string ContentAsString()

return content?.ToString();
}

class CornerRadiusToShape : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return new RoundRectangle
{
CornerRadius = (int)value,
};
}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
}
68 changes: 47 additions & 21 deletions src/Controls/tests/Core.UnitTests/RadioButtonTemplateTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Microsoft.Maui.Controls.Shapes;
using Microsoft.Maui.Graphics;
using Xunit;

Expand All @@ -9,22 +10,24 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Category("RadioButton")]
public class RadioButtonTemplateTests : BaseTestFixture
{
public class FrameStyleCases : IEnumerable<object[]>
public class BorderStyleCases : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] { Frame.VerticalOptionsProperty, LayoutOptions.End };
yield return new object[] { Frame.HorizontalOptionsProperty, LayoutOptions.End };
yield return new object[] { Frame.BackgroundColorProperty, Colors.Red };
yield return new object[] { Frame.BorderColorProperty, Colors.Magenta };
yield return new object[] { Frame.MarginProperty, new Thickness(1, 2, 3, 4) };
yield return new object[] { Frame.OpacityProperty, 0.67 };
yield return new object[] { Frame.RotationProperty, 0.3 };
yield return new object[] { Frame.ScaleProperty, 0.8 };
yield return new object[] { Frame.ScaleXProperty, 0.9 };
yield return new object[] { Frame.ScaleYProperty, 0.95 };
yield return new object[] { Frame.TranslationXProperty, 123 };
yield return new object[] { Frame.TranslationYProperty, 321 };
yield return new object[] { Border.VerticalOptionsProperty, LayoutOptions.End };
yield return new object[] { Border.HorizontalOptionsProperty, LayoutOptions.End };
yield return new object[] { Border.BackgroundColorProperty, Colors.Red };
yield return new object[] { Border.StrokeProperty, Colors.Magenta };
yield return new object[] { Border.StrokeShapeProperty, new RoundRectangle() };
yield return new object[] { Border.StrokeThicknessProperty, new Thickness(1, 2, 3, 4) };
yield return new object[] { Border.MarginProperty, new Thickness(1, 2, 3, 4) };
yield return new object[] { Border.OpacityProperty, 0.67 };
yield return new object[] { Border.RotationProperty, 0.3 };
yield return new object[] { Border.ScaleProperty, 0.8 };
yield return new object[] { Border.ScaleXProperty, 0.9 };
yield return new object[] { Border.ScaleYProperty, 0.95 };
yield return new object[] { Border.TranslationXProperty, 123 };
yield return new object[] { Border.TranslationYProperty, 321 };
}

IEnumerator IEnumerable.GetEnumerator()
Expand All @@ -33,20 +36,20 @@ IEnumerator IEnumerable.GetEnumerator()
}
}

[ClassData(typeof(FrameStyleCases))]
[Theory(DisplayName = "Frame Style properties should not affect RadioButton")]
public void RadioButtonIgnoresFrameStyleProperties(BindableProperty property, object value)
[ClassData(typeof(BorderStyleCases))]
[Theory(DisplayName = "Border Style properties should not affect RadioButton")]
public void RadioButtonIgnoresBorderStyleProperties(BindableProperty property, object value)
{
var implicitFrameStyle = new Style(typeof(Frame));
implicitFrameStyle.Setters.Add(new Setter() { Property = property, Value = value });
var implicitBorderStyle = new Style(typeof(Border));
implicitBorderStyle.Setters.Add(new Setter() { Property = property, Value = value });

var page = new ContentPage();
page.Resources.Add(implicitFrameStyle);
page.Resources.Add(implicitBorderStyle);

var radioButton = new RadioButton() { ControlTemplate = RadioButton.DefaultTemplate };
page.Content = radioButton;

var root = (radioButton as IControlTemplated)?.TemplateRoot as Frame;
var root = (radioButton as IControlTemplated)?.TemplateRoot as Border;

Assert.NotNull(root);
Assert.NotEqual(value, root.GetValue(property));
Expand Down Expand Up @@ -84,10 +87,33 @@ public void RadioButtonStyleSetsPropertyOnTemplateRoot(BindableProperty property

var radioButton = new RadioButton() { ControlTemplate = RadioButton.DefaultTemplate, Style = radioButtonStyle };

var root = (radioButton as IControlTemplated)?.TemplateRoot as Frame;
var root = (radioButton as IControlTemplated)?.TemplateRoot as Border;

Assert.NotNull(root);
Assert.Equal(root.GetValue(property), value);
}

[Fact]
public void BorderSpecificRadioButtonStyleSetsPropertyOnTemplateRoot()
{
var borderColor = Colors.Magenta;
var borderWidthProperty = 4.3;
var cornerRadiusProperty = 5;
var radioButtonStyle = new Style(typeof(RadioButton));

radioButtonStyle.Setters.Add(new Setter() { Property = RadioButton.BorderColorProperty, Value = borderColor });
radioButtonStyle.Setters.Add(new Setter() { Property = RadioButton.BorderWidthProperty, Value = borderWidthProperty });
radioButtonStyle.Setters.Add(new Setter() { Property = RadioButton.CornerRadiusProperty, Value = cornerRadiusProperty });

var radioButton = new RadioButton() { ControlTemplate = RadioButton.DefaultTemplate, Style = radioButtonStyle };
var root = (radioButton as IControlTemplated)?.TemplateRoot as Border;

Assert.NotNull(root);
Assert.Equal(root.GetValue(Border.StrokeProperty), Brush.Magenta);
Assert.Equal(root.GetValue(Border.StrokeThicknessProperty), borderWidthProperty);

RoundRectangle rec = (RoundRectangle)root.StrokeShape;
Assert.Equal(rec.CornerRadius, cornerRadiusProperty);
}
}
}