Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
Badge.cs
1using System;
2using System.ComponentModel;
3using System.Threading.Tasks;
4using System.Windows.Input;
5using Microsoft.Maui;
6using Microsoft.Maui.Controls;
7using Microsoft.Maui.Controls.Shapes;
8using Microsoft.Maui.Graphics;
9
11{
62 public class Badge : Border
63 {
64 #region Fields
65
69 private readonly Label badgeLabel;
70
74 private const double indicatorSize = 10;
75
76 #endregion
77
78 #region Bindable Properties
79
83 public static readonly BindableProperty TextProperty =
84 BindableProperty.Create(
85 nameof(Text),
86 typeof(string),
87 typeof(Badge),
88 default(string),
89 propertyChanged: OnTextChanged);
90
94 public static readonly BindableProperty FontSizeProperty =
95 BindableProperty.Create(
96 nameof(FontSize),
97 typeof(double),
98 typeof(Badge),
99 12d,
100 propertyChanged: OnFontSizeChanged);
101
105 public static readonly BindableProperty TextColorProperty =
106 BindableProperty.Create(
107 nameof(TextColor),
108 typeof(Color),
109 typeof(Badge),
110 Colors.White,
111 propertyChanged: OnTextColorChanged);
112
116 public static readonly BindableProperty FontFamilyProperty =
117 BindableProperty.Create(
118 nameof(FontFamily),
119 typeof(string),
120 typeof(Badge),
121 default(string),
122 propertyChanged: OnFontFamilyChanged);
123
127 public static readonly BindableProperty CornerRadiusProperty =
128 BindableProperty.Create(
129 nameof(CornerRadius),
130 typeof(double),
131 typeof(Badge),
132 10d,
133 propertyChanged: OnCornerRadiusChanged);
134
138 public static readonly BindableProperty IsIndicatorProperty =
139 BindableProperty.Create(
140 nameof(IsIndicator),
141 typeof(bool),
142 typeof(Badge),
143 false,
144 propertyChanged: OnIsIndicatorChanged);
145
149 public static readonly BindableProperty CommandProperty =
150 BindableProperty.Create(
151 nameof(Command),
152 typeof(ICommand),
153 typeof(Badge),
154 null);
155
159 public static readonly BindableProperty TextMarginProperty =
160 BindableProperty.Create(
161 nameof(TextMargin),
162 typeof(Thickness),
163 typeof(Badge),
164 new Thickness(4),
165 propertyChanged: OnTextMarginChanged);
166
167 #endregion
168
169 #region Properties
170
174 public string Text
175 {
176 get => (string)this.GetValue(TextProperty);
177 set => this.SetValue(TextProperty, value);
178 }
179
183 public double FontSize
184 {
185 get => (double)this.GetValue(FontSizeProperty);
186 set => this.SetValue(FontSizeProperty, value);
187 }
188
192 public Color TextColor
193 {
194 get => (Color)this.GetValue(TextColorProperty);
195 set => this.SetValue(TextColorProperty, value);
196 }
197
201 public string FontFamily
202 {
203 get => (string)this.GetValue(FontFamilyProperty);
204 set => this.SetValue(FontFamilyProperty, value);
205 }
206
210 public double CornerRadius
211 {
212 get => (double)this.GetValue(CornerRadiusProperty);
213 set => this.SetValue(CornerRadiusProperty, value);
214 }
215
219 public bool IsIndicator
220 {
221 get => (bool)this.GetValue(IsIndicatorProperty);
222 set => this.SetValue(IsIndicatorProperty, value);
223 }
224
228 public ICommand Command
229 {
230 get => (ICommand)this.GetValue(CommandProperty);
231 set => this.SetValue(CommandProperty, value);
232 }
233
237 public Thickness TextMargin
238 {
239 get => (Thickness)this.GetValue(TextMarginProperty);
240 set => this.SetValue(TextMarginProperty, value);
241 }
242
243 #endregion
244
245 #region Constructors
246
250 public Badge()
251 {
252 // Initially use the defined CornerRadius property.
253 this.StrokeShape = new RoundRectangle { CornerRadius = new CornerRadius(this.CornerRadius) };
254
255 this.badgeLabel = new Label
256 {
257 HorizontalOptions = LayoutOptions.Center,
258 VerticalOptions = LayoutOptions.Center,
259 Text = this.Text,
260 FontSize = this.FontSize,
261 TextColor = this.TextColor,
262 FontFamily = this.FontFamily
263 };
264
265 this.Content = this.badgeLabel;
266
267 TapGestureRecognizer TapGesture = new TapGestureRecognizer();
268 TapGesture.Tapped += this.OnBadgeTapped;
269 this.GestureRecognizers.Add(TapGesture);
270 }
271
272 #endregion
273
274 #region Event Handlers
275
281 private void OnBadgeTapped(object? sender, EventArgs e)
282 {
283 if (this.Command?.CanExecute(null) == true)
284 {
285 this.Command.Execute(null);
286 }
287 }
288
289 #endregion
290
291 #region Bindable Property Changed Callbacks
292
299 private static void OnTextMarginChanged(BindableObject bindable, object oldValue, object newValue)
300 {
301 if (bindable is Badge Badge && newValue is Thickness NewMargin)
302 {
303 Badge.badgeLabel.Margin = NewMargin;
304 Badge.UpdateSizeAndShape();
305 }
306 }
307
314 private static void OnTextChanged(BindableObject bindable, object oldValue, object newValue)
315 {
316 if (bindable is Badge Badge)
317 {
318 string TextValue = newValue?.ToString() ?? string.Empty;
319 // Update visibility based on the new text value.
320 Badge.UpdateVisibility();
321
322 if (!Badge.IsIndicator)
323 {
324 Badge.badgeLabel.Text = TextValue;
325 _ = Badge.AnimateBounceAsync();
326 }
327
328 Badge.UpdateSizeAndShape();
329 }
330 }
331
338 private static void OnFontSizeChanged(BindableObject bindable, object oldValue, object newValue)
339 {
340 if (bindable is Badge Badge)
341 {
342 Badge.badgeLabel.FontSize = (double)newValue;
343 Badge.UpdateSizeAndShape();
344 }
345 }
346
350 private static void OnTextColorChanged(BindableObject bindable, object oldValue, object newValue)
351 {
352 if (bindable is Badge Badge)
353 {
354 Badge.badgeLabel.TextColor = (Color)newValue;
355 }
356 }
357
361 private static void OnFontFamilyChanged(BindableObject bindable, object oldValue, object newValue)
362 {
363 if (bindable is Badge Badge)
364 {
365 Badge.badgeLabel.FontFamily = newValue?.ToString();
366 }
367 }
368
372 private static void OnCornerRadiusChanged(BindableObject bindable, object oldValue, object newValue)
373 {
374 if (bindable is Badge Badge && newValue is double)
375 {
376 // When the user sets CornerRadius explicitly, update the shape.
377 Badge.UpdateCornerRadius();
378 }
379 }
380
384 private static void OnIsIndicatorChanged(BindableObject bindable, object oldValue, object newValue)
385 {
386 if (bindable is Badge Badge)
387 {
388 bool IsIndicatorValue = (bool)newValue;
389 Badge.badgeLabel.Text = IsIndicatorValue ? string.Empty : Badge.Text;
390 Badge.UpdateSizeAndShape();
391 Badge.UpdateVisibility();
392 }
393 }
394
395 #endregion
396
397 #region Private Methods
398
403 private async Task AnimateBounceAsync()
404 {
405 await this.ScaleToAsync(1.2, 50);
406 await this.ScaleToAsync(1.0, 50);
407 }
408
412 private void UpdateVisibility()
413 {
414 if (this.IsIndicator)
415 {
416 this.IsVisible = true;
417 }
418 else
419 {
420 if (string.IsNullOrEmpty(this.Text))
421 {
422 this.IsVisible = false;
423 }
424 else if (int.TryParse(this.Text, out int Count))
425 {
426 this.IsVisible = Count > 0;
427 }
428 else
429 {
430 this.IsVisible = true;
431 }
432 }
433 }
434
441 private void UpdateCornerRadius()
442 {
443 if (this.IsIndicator)
444 {
445 this.StrokeShape = new RoundRectangle { CornerRadius = new CornerRadius(indicatorSize / 2) };
446 }
447 else if (this.Height > 0)
448 {
449 if (!string.IsNullOrEmpty(this.Text) && this.Text.Length == 1)
450 {
451 this.StrokeShape = new Ellipse();
452 }
453 else
454 {
455 // Use the smaller value between the user-set CornerRadius and half the control's height.
456 double EffectiveCornerRadius = Math.Min(this.CornerRadius, this.Height / 2);
457 this.StrokeShape = new RoundRectangle { CornerRadius = new CornerRadius(EffectiveCornerRadius) };
458 }
459 }
460 }
461
465 private void UpdateSizeAndShape()
466 {
467 if (this.IsIndicator)
468 {
469 this.badgeLabel.Text = string.Empty;
470 this.HeightRequest = indicatorSize;
471 this.WidthRequest = indicatorSize;
472 this.StrokeShape = new RoundRectangle { CornerRadius = new CornerRadius(indicatorSize / 2) };
473 return;
474 }
475
476 string TextValue = this.Text ?? string.Empty;
477 if (TextValue.Length == 1)
478 {
479 Size Request = this.badgeLabel.Measure(double.PositiveInfinity, double.PositiveInfinity);
480 double DesiredWidth = Request.Width + this.TextMargin.HorizontalThickness;
481 double DesiredHeight = Request.Height + this.TextMargin.VerticalThickness;
482 double Diameter = Math.Max(DesiredWidth, DesiredHeight);
483 if (Diameter > 0)
484 {
485 this.WidthRequest = Diameter;
486 this.HeightRequest = Diameter;
487 }
488
489 this.StrokeShape = new Ellipse();
490 return;
491 }
492
493 this.WidthRequest = -1;
494 this.HeightRequest = -1;
495 this.UpdateCornerRadius();
496 }
497
498 #endregion
499
500 #region Overrides
501 protected override void OnBindingContextChanged()
502 {
503 base.OnBindingContextChanged();
504
505 // Reapply visibility and corner radius adjustments.
506 this.UpdateVisibility();
507 this.UpdateSizeAndShape();
508
509 }
510
511 protected override void OnSizeAllocated(double width, double height)
512 {
513 base.OnSizeAllocated(width, height);
514
515 // Update the shape based on the new size without resetting width/height requests.
516 this.UpdateCornerRadius();
517
518 // For multi-character text, ensure the width is at least the measured text width plus padding,
519 // and also at least equal to the height (to maintain a pill shape).
520 if (!this.IsIndicator && !string.IsNullOrEmpty(this.Text) && this.Text.Length > 1)
521 {
522 // Measure the label's requested size.
523 Size Request = this.badgeLabel.Measure(double.PositiveInfinity, double.PositiveInfinity);
524 double DesiredWidth = Math.Max(Request.Width + this.TextMargin.HorizontalThickness, height);
525
526 // Only override if the current width is smaller than desired.
527 if (width < DesiredWidth)
528 {
529 this.WidthRequest = DesiredWidth;
530 }
531 }
532 }
533
534
535 #endregion
536 }
537}
Represents a badge control that displays a short text (e.g., a notification count) or a simple indica...
Definition: Badge.cs:63
double CornerRadius
Gets or sets the corner radius of the badge.
Definition: Badge.cs:211
static readonly BindableProperty TextProperty
Backing store for the Text property.
Definition: Badge.cs:83
double FontSize
Gets or sets the font size of the badge text.
Definition: Badge.cs:184
static readonly BindableProperty CommandProperty
Backing store for the Command property.
Definition: Badge.cs:149
static readonly BindableProperty CornerRadiusProperty
Backing store for the CornerRadius property.
Definition: Badge.cs:127
static readonly BindableProperty FontFamilyProperty
Backing store for the FontFamily property.
Definition: Badge.cs:116
string Text
Gets or sets the text displayed on the badge.
Definition: Badge.cs:175
static readonly BindableProperty IsIndicatorProperty
Backing store for the IsIndicator property.
Definition: Badge.cs:138
Badge()
Initializes a new instance of the Badge class.
Definition: Badge.cs:250
static readonly BindableProperty TextMarginProperty
Backing store for the TextMargin property.
Definition: Badge.cs:159
Color TextColor
Gets or sets the color of the badge text.
Definition: Badge.cs:193
bool IsIndicator
Gets or sets a value indicating whether the badge is displayed as an indicator.
Definition: Badge.cs:220
ICommand Command
Gets or sets the command to be executed when the badge is tapped.
Definition: Badge.cs:229
static readonly BindableProperty TextColorProperty
Backing store for the TextColor property.
Definition: Badge.cs:105
static readonly BindableProperty FontSizeProperty
Backing store for the FontSize property.
Definition: Badge.cs:94
Thickness TextMargin
Gets or sets the margin for the badge text.
Definition: Badge.cs:238
string FontFamily
Gets or sets the font family of the badge text.
Definition: Badge.cs:202
Definition: ImplTypes.g.cs:58