Skip to content

Commit 4590c59

Browse files
Added RatingBar.InvertDirection to support inverting "fill" direction (#3116)
Adds RatingBar.InvertDirection which is then used in all calculations regarding value, fractional value, preview offsets (X and Y), and rating bar button gradient.
1 parent e3e5e7b commit 4590c59

File tree

4 files changed

+213
-51
lines changed

4 files changed

+213
-51
lines changed

MainDemo.Wpf/RatingBar.xaml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,55 @@
197197
VerticalAlignment="Top"
198198
Text="{Binding ElementName=CustomRatingBarFractionalPreview, Path=Value, StringFormat=Rating: {0}}" />
199199
</StackPanel>
200+
201+
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" Text="Rating bar with preview, fractional values, and InvertDirection=true" />
202+
203+
<StackPanel Margin="0,16,0,0" Orientation="Horizontal">
204+
<smtx:XamlDisplay Margin="5,0,0,5"
205+
VerticalContentAlignment="Top"
206+
UniqueKey="fractionalPreviewRatingBar_3">
207+
<materialDesign:RatingBar x:Name="BasicRatingBarFractionalPreview2"
208+
IsPreviewValueEnabled="True"
209+
Max="5"
210+
Min="0"
211+
InvertDirection="True"
212+
ValueIncrements="0.25"
213+
Value="0" />
214+
</smtx:XamlDisplay>
215+
216+
<TextBlock Margin="10,2,0,0"
217+
VerticalAlignment="Top"
218+
Text="{Binding ElementName=BasicRatingBarFractionalPreview2, Path=Value, StringFormat=Rating: {0}}" />
219+
220+
<smtx:XamlDisplay Margin="24,0,0,5" UniqueKey="fractionalPreviewRatingBar_4">
221+
<materialDesign:RatingBar x:Name="CustomRatingBarFractionalPreview2"
222+
IsPreviewValueEnabled="True"
223+
Max="3"
224+
Min="0"
225+
Orientation="Vertical"
226+
InvertDirection="True"
227+
ValueIncrements="0.25"
228+
Value="2">
229+
<materialDesign:RatingBar.ValueItemTemplate>
230+
<DataTemplate DataType="system:Int32">
231+
<Grid>
232+
<materialDesign:PackIcon Width="24"
233+
Height="24"
234+
Kind="Heart" />
235+
<TextBlock HorizontalAlignment="Center"
236+
VerticalAlignment="Center"
237+
FontSize="8"
238+
Foreground="{DynamicResource PrimaryHueMidForegroundBrush}"
239+
Text="{Binding}" />
240+
</Grid>
241+
</DataTemplate>
242+
</materialDesign:RatingBar.ValueItemTemplate>
243+
</materialDesign:RatingBar>
244+
</smtx:XamlDisplay>
245+
<TextBlock Margin="10,2,0,0"
246+
VerticalAlignment="Top"
247+
Text="{Binding ElementName=CustomRatingBarFractionalPreview2, Path=Value, StringFormat=Rating: {0}}" />
248+
</StackPanel>
200249
</StackPanel>
201250
</UserControl>
202251

MaterialDesignThemes.Wpf.Tests/RatingBarTests.cs

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,29 @@ public void TextBlockForegroundConverter_ShouldReturnFractionalGradientStops_Whe
231231
Assert.Equal(brush.Color.WithAlphaChannel(RatingBar.TextBlockForegroundConverter.SemiTransparent), stop2.Color);
232232
}
233233

234+
[Fact]
235+
public void TextBlockForegroundConverter_ShouldReturnFractionalGradientStops_WhenValueCovers10PercentOfButtonValueAndDirectionIsInverted()
236+
{
237+
// Arrange
238+
SolidColorBrush brush = Brushes.Red;
239+
IMultiValueConverter converter = RatingBar.TextBlockForegroundConverter.Instance;
240+
object[] values = Arrange_TextBlockForegroundConverterValues(brush, value: 1.1, buttonValue: 2, invertDirection: true);
241+
242+
// Act
243+
var result = converter.Convert(values, typeof(Brush), null, CultureInfo.CurrentCulture) as Brush;
244+
245+
// Assert
246+
Assert.IsAssignableFrom<LinearGradientBrush>(result);
247+
LinearGradientBrush resultBrush = (LinearGradientBrush)result!;
248+
Assert.Equal(2, resultBrush.GradientStops.Count);
249+
GradientStop stop1 = resultBrush.GradientStops[0];
250+
GradientStop stop2 = resultBrush.GradientStops[1];
251+
Assert.Equal(0.9, stop1.Offset, 10);
252+
Assert.Equal(brush.Color.WithAlphaChannel(RatingBar.TextBlockForegroundConverter.SemiTransparent), stop1.Color);
253+
Assert.Equal(0.9, stop2.Offset, 10);
254+
Assert.Equal(brush.Color, stop2.Color);
255+
}
256+
234257
[Fact]
235258
public void TextBlockForegroundConverter_ShouldReturnFractionalGradientStops_WhenValueCovers42PercentOfButtonValue()
236259
{
@@ -277,15 +300,15 @@ public void TextBlockForegroundConverter_ShouldReturnFractionalGradientStops_Whe
277300
Assert.Equal(brush.Color.WithAlphaChannel(RatingBar.TextBlockForegroundConverter.SemiTransparent), stop2.Color);
278301
}
279302

280-
private static object[] Arrange_TextBlockForegroundConverterValues(SolidColorBrush brush, double value, int buttonValue, Orientation orientation = Orientation.Horizontal) =>
281-
new object[] { brush, orientation, value, buttonValue };
303+
private static object[] Arrange_TextBlockForegroundConverterValues(SolidColorBrush brush, double value, int buttonValue, Orientation orientation = Orientation.Horizontal, bool invertDirection = false) =>
304+
new object[] { brush, orientation, invertDirection, value, buttonValue };
282305

283306
[Fact]
284307
public void PreviewIndicatorTransformXConverter_ShouldCenterPreviewIndicator_WhenFractionalValuesAreDisabledAndOrientationIsHorizontal()
285308
{
286309
// Arrange
287310
IMultiValueConverter converter = RatingBar.PreviewIndicatorTransformXConverter.Instance;
288-
object[] values = Arrange_PreviewIndicatorTransformXConverterValues(100, 20, Orientation.Horizontal, false, 1, 1);
311+
object[] values = Arrange_PreviewIndicatorTransformXConverterValues(100, 20, Orientation.Horizontal, false, false, 1, 1);
289312

290313
// Act
291314
double? result = converter.Convert(values, typeof(double), null, CultureInfo.CurrentCulture) as double?;
@@ -295,27 +318,29 @@ public void PreviewIndicatorTransformXConverter_ShouldCenterPreviewIndicator_Whe
295318
Assert.Equal(40.0, result); // 50% of 100 minus 20/2
296319
}
297320

298-
[Fact]
299-
public void PreviewIndicatorTransformXConverter_ShouldOffsetPreviewIndicatorByPercentage_WhenFractionalValuesAreEnabledAndOrientationIsHorizontal()
321+
[Theory]
322+
[InlineData(false, 15.0)] // 25% of 100 minus 20/2
323+
[InlineData(true, 65.0)] // 75% of 100 minus 20/2
324+
public void PreviewIndicatorTransformXConverter_ShouldOffsetPreviewIndicatorByPercentage_WhenFractionalValuesAreEnabledAndOrientationIsHorizontal(bool invertDirection, double expectedValue)
300325
{
301326
// Arrange
302327
IMultiValueConverter converter = RatingBar.PreviewIndicatorTransformXConverter.Instance;
303-
object[] values = Arrange_PreviewIndicatorTransformXConverterValues(100, 20, Orientation.Horizontal, true, 1.25, 1);
328+
object[] values = Arrange_PreviewIndicatorTransformXConverterValues(100, 20, Orientation.Horizontal, invertDirection, true, 1.25, 1);
304329

305330
// Act
306331
double? result = converter.Convert(values, typeof(double), null, CultureInfo.CurrentCulture) as double?;
307332

308333
// Assert
309334
Assert.NotNull(result);
310-
Assert.Equal(15.0, result); // 25% of 100 minus 20/2
335+
Assert.Equal(expectedValue, result);
311336
}
312337

313338
[Fact]
314339
public void PreviewIndicatorTransformXConverter_ShouldPlacePreviewIndicatorWithSmallMargin_WhenFractionalValuesAreDisabledAndOrientationIsVertical()
315340
{
316341
// Arrange
317342
IMultiValueConverter converter = RatingBar.PreviewIndicatorTransformXConverter.Instance;
318-
object[] values = Arrange_PreviewIndicatorTransformXConverterValues(100, 20, Orientation.Vertical, false, 1, 1);
343+
object[] values = Arrange_PreviewIndicatorTransformXConverterValues(100, 20, Orientation.Vertical, false, false, 1, 1);
319344
double expectedValue = -20 - RatingBar.PreviewIndicatorTransformXConverter.Margin;
320345

321346
// Act
@@ -331,7 +356,7 @@ public void PreviewIndicatorTransformXConverter_ShouldPlacePreviewIndicatorWithS
331356
{
332357
// Arrange
333358
IMultiValueConverter converter = RatingBar.PreviewIndicatorTransformXConverter.Instance;
334-
object[] values = Arrange_PreviewIndicatorTransformXConverterValues(100, 20, Orientation.Vertical, true, 1.25, 1);
359+
object[] values = Arrange_PreviewIndicatorTransformXConverterValues(100, 20, Orientation.Vertical, false, true, 1.25, 1);
335360
double expectedValue = -20 - RatingBar.PreviewIndicatorTransformXConverter.Margin;
336361

337362
// Act
@@ -344,15 +369,15 @@ public void PreviewIndicatorTransformXConverter_ShouldPlacePreviewIndicatorWithS
344369

345370

346371

347-
private static object[] Arrange_PreviewIndicatorTransformXConverterValues(double ratingBarButtonActualWidth, double previewValueActualWidth, Orientation orientation, bool isFractionalValueEnabled, double previewValue, int buttonValue) =>
348-
new object[] { ratingBarButtonActualWidth, previewValueActualWidth, orientation, isFractionalValueEnabled, previewValue, buttonValue };
372+
private static object[] Arrange_PreviewIndicatorTransformXConverterValues(double ratingBarButtonActualWidth, double previewValueActualWidth, Orientation orientation, bool invertDirection, bool isFractionalValueEnabled, double previewValue, int buttonValue) =>
373+
new object[] { ratingBarButtonActualWidth, previewValueActualWidth, orientation, invertDirection, isFractionalValueEnabled, previewValue, buttonValue };
349374

350375
[Fact]
351376
public void PreviewIndicatorTransformYConverter_ShouldPlacePreviewIndicatorWithSmallMargin_WhenFractionalValuesAreDisabledAndOrientationIsHorizontal()
352377
{
353378
// Arrange
354379
IMultiValueConverter converter = RatingBar.PreviewIndicatorTransformYConverter.Instance;
355-
object[] values = Arrange_PreviewIndicatorTransformYConverterValues(100, 20, Orientation.Horizontal, false, 1, 1);
380+
object[] values = Arrange_PreviewIndicatorTransformYConverterValues(100, 20, Orientation.Horizontal, false, false, 1, 1);
356381
double expectedValue = -20 - RatingBar.PreviewIndicatorTransformYConverter.Margin;
357382

358383
// Act
@@ -368,7 +393,7 @@ public void PreviewIndicatorTransformYConverter_ShouldPlacePreviewIndicatorWithS
368393
{
369394
// Arrange
370395
IMultiValueConverter converter = RatingBar.PreviewIndicatorTransformYConverter.Instance;
371-
object[] values = Arrange_PreviewIndicatorTransformYConverterValues(100, 20, Orientation.Horizontal, true, 1.25, 1);
396+
object[] values = Arrange_PreviewIndicatorTransformYConverterValues(100, 20, Orientation.Horizontal, false, true, 1.25, 1);
372397
double expectedValue = -20 - RatingBar.PreviewIndicatorTransformYConverter.Margin;
373398

374399
// Act
@@ -384,7 +409,7 @@ public void PreviewIndicatorTransformYConverter_ShouldCenterPreviewIndicator_Whe
384409
{
385410
// Arrange
386411
IMultiValueConverter converter = RatingBar.PreviewIndicatorTransformYConverter.Instance;
387-
object[] values = Arrange_PreviewIndicatorTransformYConverterValues(100, 20, Orientation.Vertical, false, 1, 1);
412+
object[] values = Arrange_PreviewIndicatorTransformYConverterValues(100, 20, Orientation.Vertical, false, false, 1, 1);
388413

389414
// Act
390415
double? result = converter.Convert(values, typeof(double), null, CultureInfo.CurrentCulture) as double?;
@@ -394,23 +419,25 @@ public void PreviewIndicatorTransformYConverter_ShouldCenterPreviewIndicator_Whe
394419
Assert.Equal(40.0, result); // 50% of 100 minus 20/2
395420
}
396421

397-
[Fact]
398-
public void PreviewIndicatorTransformYConverter_ShouldPreviewIndicatorByPercentage_WhenFractionalValuesAreEnabledAndOrientationIsVertical()
422+
[Theory]
423+
[InlineData(false, 15.0)] // 25% of 100 minus 20/2
424+
[InlineData(true, 65.0)] // 75% of 100 minus 20/2
425+
public void PreviewIndicatorTransformYConverter_ShouldPreviewIndicatorByPercentage_WhenFractionalValuesAreEnabledAndOrientationIsVertical(bool invertDirection, double expectedValue)
399426
{
400427
// Arrange
401428
IMultiValueConverter converter = RatingBar.PreviewIndicatorTransformYConverter.Instance;
402-
object[] values = Arrange_PreviewIndicatorTransformYConverterValues(100, 20, Orientation.Vertical, true, 1.25, 1);
429+
object[] values = Arrange_PreviewIndicatorTransformYConverterValues(100, 20, Orientation.Vertical, invertDirection, true, 1.25, 1);
403430

404431
// Act
405432
double? result = converter.Convert(values, typeof(double), null, CultureInfo.CurrentCulture) as double?;
406433

407434
// Assert
408435
Assert.NotNull(result);
409-
Assert.Equal(15.0, result); // 25% of 100 minus 20/2
436+
Assert.Equal(expectedValue, result);
410437
}
411438

412-
private static object[] Arrange_PreviewIndicatorTransformYConverterValues(double ratingBarButtonActualHeight, double previewValueActualHeight, Orientation orientation, bool isFractionalValueEnabled, double previewValue, int buttonValue) =>
413-
new object[] { ratingBarButtonActualHeight, previewValueActualHeight, orientation, isFractionalValueEnabled, previewValue, buttonValue };
439+
private static object[] Arrange_PreviewIndicatorTransformYConverterValues(double ratingBarButtonActualHeight, double previewValueActualHeight, Orientation orientation, bool invertDirection, bool isFractionalValueEnabled, double previewValue, int buttonValue) =>
440+
new object[] { ratingBarButtonActualHeight, previewValueActualHeight, orientation, invertDirection, isFractionalValueEnabled, previewValue, buttonValue };
414441
}
415442

416443
internal static class ColorExtensions

0 commit comments

Comments
 (0)