Skip to content

Commit

Permalink
[RadioButton] Fix Issue with BorderWidth, Incorrect spacing in Defaul…
Browse files Browse the repository at this point in the history
…t Control Template. (#13407)

### Description of Change
This adapts a previously submitted PR that was closed before it was
reviewed: #8292 The underlying Frame
was changed to a Border to support BorderWidth attribute not working.

An addition of column spacing to the underlying grid fixes the ellipse
being too close to the content text, and additional padding of the grid
prevents it from clipping.
|Before|After
|-|-|
|<img
src="https://user-images.githubusercontent.com/89540402/219554040-bb99be54-1ac4-42ca-8bc9-e74dc25a6379.png"
width="500">|<img
src="https://user-images.githubusercontent.com/89540402/219552560-cd800385-614a-4e45-90fe-b52fbca836fb.png"
width="500">|

|Before|After
|-|-|
|<img
src="https://user-images.githubusercontent.com/89540402/219554003-224ce46f-1fd0-40dc-8018-d796ef684faf.png"
width="500">|<img
src="https://user-images.githubusercontent.com/89540402/219552652-65d4d6f8-0082-4d75-8061-e51476101e92.png"
width="500">|

Question that arose: 
According to docs for RadioButton, CornerRadius is set to be an integer:
[CornerRadius](https://learn.microsoft.com/en-us/dotnet/api/microsoft.maui.controls.radiobutton.cornerradius?view=net-maui-7.0#microsoft-maui-controls-radiobutton-cornerradius)
however, Corner Radius use double values: [Corner Radius
Constructor](https://github.com/dotnet/maui/blob/05602026058836d0c1895e070aae4cb12c408c1d/src/Core/src/Primitives/CornerRadius.cs#L24).
Should I change the API to reflect this?
### Issues Fixed
Fixes #8291
  • Loading branch information
PureWeen authored Mar 3, 2023
2 parents 79fb87a + 372d949 commit 3992ae9
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 29 deletions.
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);
}
}
}

0 comments on commit 3992ae9

Please sign in to comment.