mirror of
https://github.com/AtomUI/AtomUI.git
synced 2026-10-18 22:46:38 +08:00
feat(Rate): set Value to default to TwoWay binding with data validation support
This commit is contained in:
+15
@@ -15433,6 +15433,8 @@ namespace AtomUI.Theme.Language
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.HalfStarTitle] = global::AtomUIGallery.ShowCases.Rate.en_US.HalfStarTitle;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.OtherCharacterDescription] = global::AtomUIGallery.ShowCases.Rate.en_US.OtherCharacterDescription;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.OtherCharacterTitle] = global::AtomUIGallery.ShowCases.Rate.en_US.OtherCharacterTitle;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2ContentClear] = global::AtomUIGallery.ShowCases.Rate.en_US.P2ContentClear;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2ContentSetFourStars] = global::AtomUIGallery.ShowCases.Rate.en_US.P2ContentSetFourStars;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2TextIsallowclearFalse] = global::AtomUIGallery.ShowCases.Rate.en_US.P2TextIsallowclearFalse;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2TextIsallowclearTrue] = global::AtomUIGallery.ShowCases.Rate.en_US.P2TextIsallowclearTrue;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2TooltipBad] = global::AtomUIGallery.ShowCases.Rate.en_US.P2TooltipBad;
|
||||
@@ -15440,6 +15442,7 @@ namespace AtomUI.Theme.Language
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2TooltipNormal] = global::AtomUIGallery.ShowCases.Rate.en_US.P2TooltipNormal;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2TooltipTerrible] = global::AtomUIGallery.ShowCases.Rate.en_US.P2TooltipTerrible;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2TooltipWonderful] = global::AtomUIGallery.ShowCases.Rate.en_US.P2TooltipWonderful;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2TwoWayValueSummaryFormat] = global::AtomUIGallery.ShowCases.Rate.en_US.P2TwoWayValueSummaryFormat;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.PageDescription] = global::AtomUIGallery.ShowCases.Rate.en_US.PageDescription;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.PageSubtitle] = global::AtomUIGallery.ShowCases.Rate.en_US.PageSubtitle;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.ReadOnlyDescription] = global::AtomUIGallery.ShowCases.Rate.en_US.ReadOnlyDescription;
|
||||
@@ -15461,6 +15464,8 @@ namespace AtomUI.Theme.Language
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.TokenNameStarSizeSM] = global::AtomUIGallery.ShowCases.Rate.en_US.TokenNameStarSizeSM;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.TokenScopeComponent] = global::AtomUIGallery.ShowCases.Rate.en_US.TokenScopeComponent;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.TokenStatusStable] = global::AtomUIGallery.ShowCases.Rate.en_US.TokenStatusStable;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.TwoWayBindingDescription] = global::AtomUIGallery.ShowCases.Rate.en_US.TwoWayBindingDescription;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.TwoWayBindingTitle] = global::AtomUIGallery.ShowCases.Rate.en_US.TwoWayBindingTitle;
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
@@ -15515,6 +15520,8 @@ namespace AtomUI.Theme.Language
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.HalfStarTitle] = global::AtomUIGallery.ShowCases.Rate.zh_CN.HalfStarTitle;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.OtherCharacterDescription] = global::AtomUIGallery.ShowCases.Rate.zh_CN.OtherCharacterDescription;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.OtherCharacterTitle] = global::AtomUIGallery.ShowCases.Rate.zh_CN.OtherCharacterTitle;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2ContentClear] = global::AtomUIGallery.ShowCases.Rate.zh_CN.P2ContentClear;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2ContentSetFourStars] = global::AtomUIGallery.ShowCases.Rate.zh_CN.P2ContentSetFourStars;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2TextIsallowclearFalse] = global::AtomUIGallery.ShowCases.Rate.zh_CN.P2TextIsallowclearFalse;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2TextIsallowclearTrue] = global::AtomUIGallery.ShowCases.Rate.zh_CN.P2TextIsallowclearTrue;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2TooltipBad] = global::AtomUIGallery.ShowCases.Rate.zh_CN.P2TooltipBad;
|
||||
@@ -15522,6 +15529,7 @@ namespace AtomUI.Theme.Language
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2TooltipNormal] = global::AtomUIGallery.ShowCases.Rate.zh_CN.P2TooltipNormal;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2TooltipTerrible] = global::AtomUIGallery.ShowCases.Rate.zh_CN.P2TooltipTerrible;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2TooltipWonderful] = global::AtomUIGallery.ShowCases.Rate.zh_CN.P2TooltipWonderful;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2TwoWayValueSummaryFormat] = global::AtomUIGallery.ShowCases.Rate.zh_CN.P2TwoWayValueSummaryFormat;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.PageDescription] = global::AtomUIGallery.ShowCases.Rate.zh_CN.PageDescription;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.PageSubtitle] = global::AtomUIGallery.ShowCases.Rate.zh_CN.PageSubtitle;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.ReadOnlyDescription] = global::AtomUIGallery.ShowCases.Rate.zh_CN.ReadOnlyDescription;
|
||||
@@ -15543,6 +15551,8 @@ namespace AtomUI.Theme.Language
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.TokenNameStarSizeSM] = global::AtomUIGallery.ShowCases.Rate.zh_CN.TokenNameStarSizeSM;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.TokenScopeComponent] = global::AtomUIGallery.ShowCases.Rate.zh_CN.TokenScopeComponent;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.TokenStatusStable] = global::AtomUIGallery.ShowCases.Rate.zh_CN.TokenStatusStable;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.TwoWayBindingDescription] = global::AtomUIGallery.ShowCases.Rate.zh_CN.TwoWayBindingDescription;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.TwoWayBindingTitle] = global::AtomUIGallery.ShowCases.Rate.zh_CN.TwoWayBindingTitle;
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
@@ -15597,6 +15607,8 @@ namespace AtomUI.Theme.Language
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.HalfStarTitle] = global::AtomUIGallery.ShowCases.Rate.zh_TW.HalfStarTitle;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.OtherCharacterDescription] = global::AtomUIGallery.ShowCases.Rate.zh_TW.OtherCharacterDescription;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.OtherCharacterTitle] = global::AtomUIGallery.ShowCases.Rate.zh_TW.OtherCharacterTitle;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2ContentClear] = global::AtomUIGallery.ShowCases.Rate.zh_TW.P2ContentClear;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2ContentSetFourStars] = global::AtomUIGallery.ShowCases.Rate.zh_TW.P2ContentSetFourStars;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2TextIsallowclearFalse] = global::AtomUIGallery.ShowCases.Rate.zh_TW.P2TextIsallowclearFalse;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2TextIsallowclearTrue] = global::AtomUIGallery.ShowCases.Rate.zh_TW.P2TextIsallowclearTrue;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2TooltipBad] = global::AtomUIGallery.ShowCases.Rate.zh_TW.P2TooltipBad;
|
||||
@@ -15604,6 +15616,7 @@ namespace AtomUI.Theme.Language
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2TooltipNormal] = global::AtomUIGallery.ShowCases.Rate.zh_TW.P2TooltipNormal;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2TooltipTerrible] = global::AtomUIGallery.ShowCases.Rate.zh_TW.P2TooltipTerrible;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2TooltipWonderful] = global::AtomUIGallery.ShowCases.Rate.zh_TW.P2TooltipWonderful;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.P2TwoWayValueSummaryFormat] = global::AtomUIGallery.ShowCases.Rate.zh_TW.P2TwoWayValueSummaryFormat;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.PageDescription] = global::AtomUIGallery.ShowCases.Rate.zh_TW.PageDescription;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.PageSubtitle] = global::AtomUIGallery.ShowCases.Rate.zh_TW.PageSubtitle;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.ReadOnlyDescription] = global::AtomUIGallery.ShowCases.Rate.zh_TW.ReadOnlyDescription;
|
||||
@@ -15625,6 +15638,8 @@ namespace AtomUI.Theme.Language
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.TokenNameStarSizeSM] = global::AtomUIGallery.ShowCases.Rate.zh_TW.TokenNameStarSizeSM;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.TokenScopeComponent] = global::AtomUIGallery.ShowCases.Rate.zh_TW.TokenScopeComponent;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.TokenStatusStable] = global::AtomUIGallery.ShowCases.Rate.zh_TW.TokenStatusStable;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.TwoWayBindingDescription] = global::AtomUIGallery.ShowCases.Rate.zh_TW.TwoWayBindingDescription;
|
||||
dictionary[global::AtomUIGallery.Localization.RateShowCaseLangResourceKind.TwoWayBindingTitle] = global::AtomUIGallery.ShowCases.Rate.zh_TW.TwoWayBindingTitle;
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
|
||||
+6
-1
@@ -4872,6 +4872,8 @@ namespace AtomUIGallery.Localization
|
||||
HalfStarTitle,
|
||||
OtherCharacterDescription,
|
||||
OtherCharacterTitle,
|
||||
P2ContentClear,
|
||||
P2ContentSetFourStars,
|
||||
P2TextIsallowclearFalse,
|
||||
P2TextIsallowclearTrue,
|
||||
P2TooltipBad,
|
||||
@@ -4879,6 +4881,7 @@ namespace AtomUIGallery.Localization
|
||||
P2TooltipNormal,
|
||||
P2TooltipTerrible,
|
||||
P2TooltipWonderful,
|
||||
P2TwoWayValueSummaryFormat,
|
||||
PageDescription,
|
||||
PageSubtitle,
|
||||
ReadOnlyDescription,
|
||||
@@ -4899,7 +4902,9 @@ namespace AtomUIGallery.Localization
|
||||
TokenNameStarSizeLG,
|
||||
TokenNameStarSizeSM,
|
||||
TokenScopeComponent,
|
||||
TokenStatusStable
|
||||
TokenStatusStable,
|
||||
TwoWayBindingDescription,
|
||||
TwoWayBindingTitle
|
||||
}
|
||||
|
||||
public class RateShowCaseLangResourceExtension : LanguageResourceExtension<RateShowCaseLangResourceKind>
|
||||
|
||||
@@ -23,7 +23,7 @@ internal partial class en_US
|
||||
public const string ApiPropertyStarColor = "Overrides the filled rating item color.";
|
||||
public const string ApiPropertyStarBgColor = "Overrides the unselected rating item color.";
|
||||
public const string ApiPropertyCount = "Total number of rating items.";
|
||||
public const string ApiPropertyValue = "Current selected rating value.";
|
||||
public const string ApiPropertyValue = "Current selected rating value. It uses TwoWay binding by default and supports Avalonia data validation.";
|
||||
public const string ApiPropertyDefaultValue = "Initial rating value used when Value is not explicitly set.";
|
||||
public const string ApiPropertyIsKeyboardEnabled = "Controls whether keyboard interaction is enabled.";
|
||||
public const string ApiPropertyToolTips = "Tooltip text list displayed for each rating value.";
|
||||
@@ -45,6 +45,8 @@ internal partial class en_US
|
||||
public const string TokenStatusStable = "Stable";
|
||||
public const string BasicTitle = "Basic";
|
||||
public const string BasicDescription = "The simplest usage.";
|
||||
public const string TwoWayBindingTitle = "Two-way binding";
|
||||
public const string TwoWayBindingDescription = "Value uses TwoWay binding by default, so user selection and ViewModel updates stay synchronized.";
|
||||
public const string HalfStarTitle = "Half star";
|
||||
public const string HalfStarDescription = "Support select half star.";
|
||||
public const string ShowCopywritingTitle = "Show copywriting";
|
||||
@@ -57,6 +59,9 @@ internal partial class en_US
|
||||
public const string OtherCharacterDescription = "Replace the default star to other character like alphabet, digit, iconfont or even Chinese word.";
|
||||
public const string P2TextIsallowclearTrue = "IsAllowClear: true";
|
||||
public const string P2TextIsallowclearFalse = "IsAllowClear: false";
|
||||
public const string P2ContentSetFourStars = "Set 4 stars";
|
||||
public const string P2ContentClear = "Clear";
|
||||
public const string P2TwoWayValueSummaryFormat = "Selected value: {0:0.#}";
|
||||
public const string P2TooltipTerrible = "terrible";
|
||||
public const string P2TooltipBad = "bad";
|
||||
public const string P2TooltipNormal = "normal";
|
||||
|
||||
@@ -23,7 +23,7 @@ internal partial class zh_CN
|
||||
public const string ApiPropertyStarColor = "覆盖已选评分项颜色。";
|
||||
public const string ApiPropertyStarBgColor = "覆盖未选评分项颜色。";
|
||||
public const string ApiPropertyCount = "评分项总数。";
|
||||
public const string ApiPropertyValue = "当前选中的评分值。";
|
||||
public const string ApiPropertyValue = "当前选中的评分值,默认 TwoWay 绑定,并支持 Avalonia 数据验证。";
|
||||
public const string ApiPropertyDefaultValue = "未显式设置 Value 时使用的初始评分值。";
|
||||
public const string ApiPropertyIsKeyboardEnabled = "控制是否启用键盘交互。";
|
||||
public const string ApiPropertyToolTips = "每个评分值对应的提示文本列表。";
|
||||
@@ -45,6 +45,8 @@ internal partial class zh_CN
|
||||
public const string TokenStatusStable = "稳定";
|
||||
public const string BasicTitle = "基础用法";
|
||||
public const string BasicDescription = "最简单的用法。";
|
||||
public const string TwoWayBindingTitle = "双向绑定";
|
||||
public const string TwoWayBindingDescription = "Value 默认使用 TwoWay 绑定,用户评分和 ViewModel 更新会保持同步。";
|
||||
public const string HalfStarTitle = "半星";
|
||||
public const string HalfStarDescription = "支持选择半星。";
|
||||
public const string ShowCopywritingTitle = "显示文案";
|
||||
@@ -57,6 +59,9 @@ internal partial class zh_CN
|
||||
public const string OtherCharacterDescription = "将默认星形替换为其他字符,例如字母、数字、图标字体,甚至中文文字。";
|
||||
public const string P2TextIsallowclearTrue = "允许清除:true";
|
||||
public const string P2TextIsallowclearFalse = "允许清除:false";
|
||||
public const string P2ContentSetFourStars = "设为 4 星";
|
||||
public const string P2ContentClear = "清空";
|
||||
public const string P2TwoWayValueSummaryFormat = "当前评分:{0:0.#}";
|
||||
public const string P2TooltipTerrible = "糟糕";
|
||||
public const string P2TooltipBad = "不好";
|
||||
public const string P2TooltipNormal = "一般";
|
||||
|
||||
@@ -23,7 +23,7 @@ internal partial class zh_TW
|
||||
public const string ApiPropertyStarColor = "覆蓋已選評分項顏色。";
|
||||
public const string ApiPropertyStarBgColor = "覆蓋未選評分項顏色。";
|
||||
public const string ApiPropertyCount = "評分項總數。";
|
||||
public const string ApiPropertyValue = "當前選中的評分值。";
|
||||
public const string ApiPropertyValue = "當前選中的評分值,預設 TwoWay 綁定,並支援 Avalonia 資料驗證。";
|
||||
public const string ApiPropertyDefaultValue = "未顯式設置 Value 時使用的初始評分值。";
|
||||
public const string ApiPropertyIsKeyboardEnabled = "控制是否啟用鍵盤交互。";
|
||||
public const string ApiPropertyToolTips = "每個評分值對應的提示文本列表。";
|
||||
@@ -45,6 +45,8 @@ internal partial class zh_TW
|
||||
public const string TokenStatusStable = "穩定";
|
||||
public const string BasicTitle = "基礎用法";
|
||||
public const string BasicDescription = "最簡單的用法。";
|
||||
public const string TwoWayBindingTitle = "雙向綁定";
|
||||
public const string TwoWayBindingDescription = "Value 預設使用 TwoWay 綁定,使用者評分和 ViewModel 更新會保持同步。";
|
||||
public const string HalfStarTitle = "半星";
|
||||
public const string HalfStarDescription = "支持選擇半星。";
|
||||
public const string ShowCopywritingTitle = "顯示文案";
|
||||
@@ -57,6 +59,9 @@ internal partial class zh_TW
|
||||
public const string OtherCharacterDescription = "將默認星形替換為其他字符,例如字母、數字、圖標字體,甚至中文文字。";
|
||||
public const string P2TextIsallowclearTrue = "允許清除:true";
|
||||
public const string P2TextIsallowclearFalse = "允許清除:false";
|
||||
public const string P2ContentSetFourStars = "設為 4 星";
|
||||
public const string P2ContentClear = "清空";
|
||||
public const string P2TwoWayValueSummaryFormat = "目前評分:{0:0.#}";
|
||||
public const string P2TooltipTerrible = "糟糕";
|
||||
public const string P2TooltipBad = "不好";
|
||||
public const string P2TooltipNormal = "一般";
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Reactive;
|
||||
using AtomUI.Controls;
|
||||
using AtomUI.Data;
|
||||
using AtomUIGallery.Localization;
|
||||
@@ -20,6 +22,8 @@ public class RateViewModel : ReactiveObject, IRoutableViewModel
|
||||
private ObservableCollection<RateApiRow>? _apiRows;
|
||||
private ObservableCollection<RateDesignTokenRow>? _designTokenRows;
|
||||
private IList<string>? _tooltips;
|
||||
private double _twoWayValue;
|
||||
private string? _twoWayValueSummary;
|
||||
|
||||
public ObservableCollection<RateApiRow>? ApiRows
|
||||
{
|
||||
@@ -39,6 +43,27 @@ public class RateViewModel : ReactiveObject, IRoutableViewModel
|
||||
set => this.RaiseAndSetIfChanged(ref _tooltips, value);
|
||||
}
|
||||
|
||||
public double TwoWayValue
|
||||
{
|
||||
get => _twoWayValue;
|
||||
set
|
||||
{
|
||||
if (Math.Abs(_twoWayValue - value) < double.Epsilon)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.RaiseAndSetIfChanged(ref _twoWayValue, value);
|
||||
UpdateTwoWayValueSummary();
|
||||
}
|
||||
}
|
||||
|
||||
public string? TwoWayValueSummary
|
||||
{
|
||||
get => _twoWayValueSummary;
|
||||
set => this.RaiseAndSetIfChanged(ref _twoWayValueSummary, value);
|
||||
}
|
||||
|
||||
private string? _activeTooltip;
|
||||
|
||||
public string? ActiveTooltip
|
||||
@@ -47,9 +72,38 @@ public class RateViewModel : ReactiveObject, IRoutableViewModel
|
||||
set => this.RaiseAndSetIfChanged(ref _activeTooltip, value);
|
||||
}
|
||||
|
||||
public ReactiveCommand<Unit, Unit> SetFourStarsCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> ClearTwoWayValueCommand { get; }
|
||||
|
||||
public RateViewModel(IScreen screen)
|
||||
{
|
||||
HostScreen = screen;
|
||||
_twoWayValue = 2.0;
|
||||
SetFourStarsCommand = ReactiveCommand.Create(HandleSetFourStars);
|
||||
ClearTwoWayValueCommand = ReactiveCommand.Create(HandleClearTwoWayValue);
|
||||
UpdateTwoWayValueSummary();
|
||||
}
|
||||
|
||||
public void RefreshLocalizedState()
|
||||
{
|
||||
UpdateTwoWayValueSummary();
|
||||
}
|
||||
|
||||
private void HandleSetFourStars()
|
||||
{
|
||||
TwoWayValue = 4.0;
|
||||
}
|
||||
|
||||
private void HandleClearTwoWayValue()
|
||||
{
|
||||
TwoWayValue = 0.0;
|
||||
}
|
||||
|
||||
private void UpdateTwoWayValueSummary()
|
||||
{
|
||||
TwoWayValueSummary = string.Format(CultureInfo.CurrentCulture,
|
||||
RateShowCaseLanguage.Get(RateShowCaseLangResourceKind.P2TwoWayValueSummaryFormat, "Selected value: {0:0.#}"),
|
||||
TwoWayValue);
|
||||
}
|
||||
|
||||
public void EnsureApiRows()
|
||||
@@ -124,6 +178,11 @@ public class RateViewModel : ReactiveObject, IRoutableViewModel
|
||||
RateShowCaseLangResourceKind.ApiPropertyIsMotionEnabled => en_US.ApiPropertyIsMotionEnabled,
|
||||
RateShowCaseLangResourceKind.ApiPropertyValueChanged => en_US.ApiPropertyValueChanged,
|
||||
RateShowCaseLangResourceKind.ApiPropertyHoverValueChanged => en_US.ApiPropertyHoverValueChanged,
|
||||
RateShowCaseLangResourceKind.TwoWayBindingTitle => en_US.TwoWayBindingTitle,
|
||||
RateShowCaseLangResourceKind.TwoWayBindingDescription => en_US.TwoWayBindingDescription,
|
||||
RateShowCaseLangResourceKind.P2ContentSetFourStars => en_US.P2ContentSetFourStars,
|
||||
RateShowCaseLangResourceKind.P2ContentClear => en_US.P2ContentClear,
|
||||
RateShowCaseLangResourceKind.P2TwoWayValueSummaryFormat => en_US.P2TwoWayValueSummaryFormat,
|
||||
RateShowCaseLangResourceKind.TokenNameStarColor => en_US.TokenNameStarColor,
|
||||
RateShowCaseLangResourceKind.TokenNameStarSize => en_US.TokenNameStarSize,
|
||||
RateShowCaseLangResourceKind.TokenNameStarSizeSM => en_US.TokenNameStarSizeSM,
|
||||
|
||||
@@ -52,6 +52,29 @@
|
||||
</gallery:ShowCaseItem.DeferredContentTemplate>
|
||||
</gallery:ShowCaseItem>
|
||||
|
||||
<gallery:ShowCaseItem
|
||||
Title="{gallery:RateShowCaseLangResource TwoWayBindingTitle}"
|
||||
Description="{gallery:RateShowCaseLangResource TwoWayBindingDescription}"
|
||||
BadgeText="v6.0.8"
|
||||
IsDeferredContentEnabled="True">
|
||||
<gallery:ShowCaseItem.DeferredContentTemplate>
|
||||
<DataTemplate x:DataType="vm:RateViewModel">
|
||||
<StackPanel Orientation="Vertical" Spacing="10">
|
||||
<atom:Rate Value="{Binding TwoWayValue}" />
|
||||
<atom:TextBlock Text="{Binding TwoWayValueSummary}" />
|
||||
<StackPanel Orientation="Horizontal" Spacing="10">
|
||||
<atom:Button SizeType="Small"
|
||||
Command="{Binding SetFourStarsCommand}"
|
||||
Content="{gallery:RateShowCaseLangResource P2ContentSetFourStars}" />
|
||||
<atom:Button SizeType="Small"
|
||||
Command="{Binding ClearTwoWayValueCommand}"
|
||||
Content="{gallery:RateShowCaseLangResource P2ContentClear}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</gallery:ShowCaseItem.DeferredContentTemplate>
|
||||
</gallery:ShowCaseItem>
|
||||
|
||||
<gallery:ShowCaseItem
|
||||
Title="{gallery:RateShowCaseLangResource HalfStarTitle}"
|
||||
Description="{gallery:RateShowCaseLangResource HalfStarDescription}"
|
||||
|
||||
@@ -86,6 +86,7 @@ public partial class RateShowCase : GalleryReactiveUserControl<RateViewModel>
|
||||
RateShowCaseLanguage.Get(RateShowCaseLangResourceKind.P2TooltipWonderful, "wonderful"),
|
||||
};
|
||||
SyncActiveTooltip(viewModel, 0);
|
||||
viewModel.RefreshLocalizedState();
|
||||
}
|
||||
|
||||
private void HandleValueChanged(object? sender, RateValueChangedEventArgs e)
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
|
||||
本文档记录 Rate 控件级设计、API、主题契约、Token 和实现结构的变化。它不替代仓库根目录 `CHANGELOG.md`,也不作为正式版本发布说明。
|
||||
|
||||
## 2026-07-06
|
||||
|
||||
- Behavior
|
||||
- Make `Rate.Value` default to TwoWay binding and enable Avalonia data validation.
|
||||
- Keep user rating, Form value and ViewModel state on the same `Value` source of truth.
|
||||
- Gallery
|
||||
- Add a `v6.0.8` Rate binding example showing user selection and ViewModel updates staying synchronized.
|
||||
|
||||
## 2026-06-26
|
||||
|
||||
- Docs
|
||||
|
||||
@@ -72,6 +72,8 @@ Public API / ItemsSource / Command / Event
|
||||
- 伪类和 internal state 必须从单一 owner 推导,避免双向同步导致循环更新。
|
||||
- Gallery API 表中的状态说明应与源码实际状态流一致。
|
||||
|
||||
`AbstractRate.Value` 是评分控件的 Form value 和当前评分 source of truth。属性注册默认 `BindingMode.TwoWay` 并启用 Avalonia data validation;pointer、keyboard 和 Form set/clear 都应更新 `Value`,再由 `Value` 回放到 `EffectiveValue` 和 `RateItem.SelectedState`。
|
||||
|
||||
## 5. 生命周期与模板接入
|
||||
|
||||
生命周期规则:
|
||||
@@ -106,6 +108,7 @@ Rate 的交互事件应从输入源收敛到控件级语义事件:
|
||||
- Template part 重新应用时的状态回放。
|
||||
- 主题资源、Token 和 SharedToken 计算后的视觉更新。
|
||||
- 内容、命令和视觉状态在模板节点之间的同步。
|
||||
- `Value` 的 ViewModel 更新、用户评分、Form set/get/clear、`ValueChanged` 和 hover 状态通知。
|
||||
- 动效启停、初始加载阶段 transition 抑制和卸载取消。
|
||||
|
||||
实现文档不逐行解释私有方法。若某个私有算法成为稳定维护入口,应在本节补充算法不变量,而不是把代码复述为说明书。
|
||||
|
||||
@@ -78,6 +78,7 @@ Public API / inherited command / item source / user input
|
||||
|
||||
- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
|
||||
- selection/checked/active、input/value、motion、visual option 状态由控件实例或明确的数据 owner 推导,不能在 template part 之间双向竞争。
|
||||
- `Rate.Value` 是评分控件的外部值 owner,默认 `BindingMode.TwoWay` 并启用 Avalonia data validation;用户评分、键盘调整、Form value 和 ViewModel 更新必须收敛到同一份数值状态。
|
||||
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
|
||||
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。
|
||||
|
||||
@@ -138,6 +139,8 @@ Rate 与同分类控件共享尺寸、状态、Token、Gallery 展示和验证
|
||||
|
||||
Rate 的当前项状态必须由单一 owner 推导。public 选择属性、集合项容器和伪类之间只能做单向同步,集合替换、清空和模板重套用时必须回放当前状态。
|
||||
|
||||
`Value` 作为 Form value 和受控评分值暴露给用户。控件从 `Value` 同步 `EffectiveValue` 与每个 `RateItem.SelectedState`,也会在 pointer 或 keyboard 交互后更新 `Value`,默认绑定模式负责把该值回写到 ViewModel。
|
||||
|
||||
### 8.2 动效模型
|
||||
|
||||
Rate 的动效只表达状态变化反馈,不应改变 public API 语义。初始加载、禁用态和卸载路径应能抑制或取消动效,避免保留旧控件实例。
|
||||
|
||||
@@ -5,6 +5,7 @@ using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Metadata;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Input.Raw;
|
||||
using Avalonia.Media;
|
||||
@@ -39,7 +40,11 @@ public abstract class AbstractRate : TemplatedControl,
|
||||
AvaloniaProperty.Register<AbstractRate, int>(nameof(Count), 5);
|
||||
|
||||
public static readonly StyledProperty<double> ValueProperty =
|
||||
AvaloniaProperty.Register<AbstractRate, double>(nameof(Value), double.NaN);
|
||||
AvaloniaProperty.Register<AbstractRate, double>(
|
||||
nameof(Value),
|
||||
double.NaN,
|
||||
defaultBindingMode: BindingMode.TwoWay,
|
||||
enableDataValidation: true);
|
||||
|
||||
public static readonly StyledProperty<double> DefaultValueProperty =
|
||||
AvaloniaProperty.Register<AbstractRate, double>(nameof(DefaultValue), 0);
|
||||
@@ -543,4 +548,4 @@ public abstract class AbstractRate : TemplatedControl,
|
||||
{
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
using System.ComponentModel;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Threading;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using AvaloniaWindow = Avalonia.Controls.Window;
|
||||
|
||||
namespace AtomUI.Desktop.Controls.Tests.Rate;
|
||||
|
||||
public class RateBindingTests
|
||||
{
|
||||
static RateBindingTests()
|
||||
{
|
||||
AvaloniaTestApp.EnsureInitialized();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Value_Is_TwoWay_And_DataValidation_Enabled()
|
||||
{
|
||||
var metadata = Desktop.Controls.Rate.ValueProperty.GetMetadata(typeof(Desktop.Controls.Rate));
|
||||
|
||||
metadata.DefaultBindingMode.ShouldBe(BindingMode.TwoWay);
|
||||
metadata.EnableDataValidation.ShouldBe(true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Value_DefaultBindingMode_Updates_ViewModel()
|
||||
{
|
||||
var viewModel = new RateBindingViewModel
|
||||
{
|
||||
Value = 2.0
|
||||
};
|
||||
var rate = new Desktop.Controls.Rate();
|
||||
rate.Bind(
|
||||
Desktop.Controls.Rate.ValueProperty,
|
||||
new Binding(nameof(RateBindingViewModel.Value))
|
||||
{
|
||||
Source = viewModel
|
||||
});
|
||||
|
||||
ShowInWindow(rate, () =>
|
||||
{
|
||||
rate.Value.ShouldBe(2.0);
|
||||
|
||||
rate.Value = 4.0;
|
||||
Dispatcher.UIThread.RunJobs();
|
||||
|
||||
viewModel.Value.ShouldBe(4.0);
|
||||
});
|
||||
}
|
||||
|
||||
private static void ShowInWindow(Control content, Action assertion)
|
||||
{
|
||||
var window = new AvaloniaWindow
|
||||
{
|
||||
Width = 360,
|
||||
Height = 240,
|
||||
Content = content
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
window.Show();
|
||||
Dispatcher.UIThread.RunJobs();
|
||||
assertion();
|
||||
}
|
||||
finally
|
||||
{
|
||||
window.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class RateBindingViewModel : INotifyPropertyChanged
|
||||
{
|
||||
private double _value;
|
||||
|
||||
public double Value
|
||||
{
|
||||
get => _value;
|
||||
set
|
||||
{
|
||||
if (Math.Abs(_value - value) < double.Epsilon)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_value = value;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value)));
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,23 @@ Description="{gallery:RateShowCaseLangResource BasicDescription}">
|
||||
<atom:Rate/>
|
||||
</gallery:ShowCaseItem>
|
||||
<gallery:ShowCaseItem
|
||||
Title="{gallery:RateShowCaseLangResource TwoWayBindingTitle}"
|
||||
Description="{gallery:RateShowCaseLangResource TwoWayBindingDescription}"
|
||||
BadgeText="v6.0.8">
|
||||
<StackPanel Orientation="Vertical" Spacing="10">
|
||||
<atom:Rate Value="{Binding TwoWayValue}" />
|
||||
<atom:TextBlock Text="{Binding TwoWayValueSummary}" />
|
||||
<StackPanel Orientation="Horizontal" Spacing="10">
|
||||
<atom:Button SizeType="Small"
|
||||
Command="{Binding SetFourStarsCommand}"
|
||||
Content="{gallery:RateShowCaseLangResource P2ContentSetFourStars}" />
|
||||
<atom:Button SizeType="Small"
|
||||
Command="{Binding ClearTwoWayValueCommand}"
|
||||
Content="{gallery:RateShowCaseLangResource P2ContentClear}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</gallery:ShowCaseItem>
|
||||
<gallery:ShowCaseItem
|
||||
Title="{gallery:RateShowCaseLangResource HalfStarTitle}"
|
||||
Description="{gallery:RateShowCaseLangResource HalfStarDescription}">
|
||||
<atom:Rate DefaultValue="3.5" IsAllowHalf="True"/>
|
||||
|
||||
@@ -41,6 +41,8 @@ public class RateShowCasePageTests
|
||||
source.ShouldContain("Description=\"{gallery:RateShowCaseLangResource PageDescription}\"");
|
||||
source.ShouldContain("<gallery:ShowCaseItem");
|
||||
source.ShouldContain("RateShowCaseLangResource BasicTitle");
|
||||
source.ShouldContain("RateShowCaseLangResource TwoWayBindingTitle");
|
||||
source.ShouldContain("BadgeText=\"v6.0.8\"");
|
||||
source.ShouldContain("RateShowCaseLangResource HalfStarTitle");
|
||||
source.ShouldContain("RateShowCaseLangResource ShowCopywritingTitle");
|
||||
source.ShouldContain("RateShowCaseLangResource ReadOnlyTitle");
|
||||
@@ -67,6 +69,10 @@ public class RateShowCasePageTests
|
||||
pageSource.ShouldContain("Tag=\"DesignToken\"");
|
||||
pageSource.ShouldNotContain("ItemsSource=\"{Binding ApiRows}\"");
|
||||
pageSource.ShouldNotContain("ItemsSource=\"{Binding DesignTokenRows}\"");
|
||||
pageSource.ShouldContain("Value=\"{Binding TwoWayValue}\"");
|
||||
pageSource.ShouldContain("Text=\"{Binding TwoWayValueSummary}\"");
|
||||
pageSource.ShouldContain("Command=\"{Binding SetFourStarsCommand}\"");
|
||||
pageSource.ShouldContain("Command=\"{Binding ClearTwoWayValueCommand}\"");
|
||||
|
||||
codeBehindSource.ShouldContain("new GalleryShowCaseScenarioController");
|
||||
codeBehindSource.ShouldContain("_scenarioController.Attach(DataContext)");
|
||||
@@ -125,11 +131,14 @@ public class RateShowCasePageTests
|
||||
source.ShouldContain("ScenarioApi");
|
||||
source.ShouldContain("ScenarioDesignToken");
|
||||
source.ShouldContain("PageSubtitle");
|
||||
source.ShouldContain("TwoWayBindingTitle");
|
||||
source.ShouldContain("TwoWayBindingDescription");
|
||||
source.ShouldNotContain("InfoNamespaceLabel");
|
||||
source.ShouldContain("ApiPropertyIsAllowClear");
|
||||
source.ShouldContain("ApiPropertyIsAllowHalf");
|
||||
source.ShouldContain("ApiPropertyToolTips");
|
||||
source.ShouldContain("ApiPropertyValueChanged");
|
||||
source.ShouldContain("P2TwoWayValueSummaryFormat");
|
||||
source.ShouldContain("TokenNameStarColor");
|
||||
source.ShouldContain("TokenNameStarHoverScale");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user