using System.Collections; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Markup; using System.Windows.Shapes; using PCL.Core.App.Localization; namespace PCL; [ContentProperty("Inlines")] public partial class MyRadioBox : IMyRadio { public delegate void PreviewChangeEventHandler(object sender, ModBase.RouteEventArgs e); public delegate void PreviewCheckEventHandler(object sender, ModBase.RouteEventArgs e); // 指向动画 private const int animationTimeOfMouseIn = 100; // 鼠标指向动画长度 private const int animationTimeOfMouseOut = 200; // 鼠标指向动画长度 private const int animationTimeOfCheck = 150; // 勾选状态变更动画长度 // 在使用 XAML 设置 Checked 属性时,不会触发 Checked_Set 方法,所以需要在这里手动触发 UI 改变 public static readonly DependencyProperty CheckedProperty = DependencyProperty.Register("Checked", typeof(bool), typeof(MyRadioBox), new PropertyMetadata(false, (dRaw, e) => { var d = (MyRadioBox)dRaw; if (!d.IsLoaded) d.SyncUI(); })); public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(MyRadioBox), new PropertyMetadata((sender, e) => { if (sender is not null) ((MyRadioBox)sender).LabText.Text = (string)e.NewValue; })); private bool allowMouseDown = true; // 点击事件 private bool mouseDowned; // 基础 public int Uuid = ModBase.GetUuid(); public MyRadioBox() { InitializeComponent(); MouseLeftButtonUp += (_, _) => Radiobox_MouseUp(); MouseLeftButtonDown += (_, _) => Radiobox_MouseDown(); MouseLeave += (_, _) => Radiobox_MouseLeave(); IsEnabledChanged += (_, _) => Radiobox_IsEnabledChanged(); MouseEnter += (_, _) => Radiobox_MouseEnterAnimation(); MouseLeave += (_, _) => Radiobox_MouseLeaveAnimation(); } // 自定义属性 public bool Checked { get => (bool)GetValue(CheckedProperty); set => SetChecked(value, false); } public InlineCollection Inlines => LabText.Inlines; public string Text { get => (string)GetValue(TextProperty); set => SetValue(TextProperty, value); } // 内容 public event IMyRadio.CheckEventHandler? Check; public event IMyRadio.ChangedEventHandler? Changed; public event PreviewCheckEventHandler? PreviewCheck; public event PreviewChangeEventHandler? PreviewChange; /// /// 手动设置 Checked 属性。 /// /// 新的 Checked 属性。 /// 是否由用户引发。 public void SetChecked(bool value, bool user) { try { // Preview 事件 if (value && user) { var e = new ModBase.RouteEventArgs(user); PreviewCheck?.Invoke(this, e); if (e.handled) { Radiobox_MouseLeave(); return; } } // 自定义属性基础 var isChanged = false; if (IsLoaded && value != Checked) PreviewChange?.Invoke(this, new ModBase.RouteEventArgs(user)); if (value != Checked) { SetValue(CheckedProperty, value); isChanged = true; } // 保证只有一个单选框选中 if (Parent is null) return; var radioboxList = new List(); var checkedCount = 0; foreach (var Control in ((Panel)Parent).Children) // 收集控件列表与选中个数 if (Control is MyRadioBox radioBox) { radioboxList.Add(radioBox); if (radioBox.Checked) checkedCount += 1; } switch (checkedCount) // 判断选中情况 { case 0: { // 没有任何单选框被选中,选择第一个 radioboxList[0].Checked = true; break; } case var @case when @case > 1: { // 选中项目多于 1 个 if (Checked) { // 如果本控件选中,则取消其他所有控件的选中 foreach (var Control in radioboxList) if (Control.Checked && !Control.Equals(this)) Control.Checked = false; } else { // 如果本控件未选中,则只保留第一个选中的控件 var firstChecked = false; foreach (var Control in radioboxList) if (Control.Checked) { if (firstChecked) Control.Checked = false; // 修改 Checked 会自动触发 Change 事件,所以不用额外触发 else firstChecked = true; } } break; } } // 触发事件 if (isChanged) { if (Checked) Check?.Invoke(this, new ModBase.RouteEventArgs(user)); Changed?.Invoke(this, new ModBase.RouteEventArgs(user)); ModMain.RaiseCustomEvent(this); } // 更改动画 SyncUI(); } catch (Exception ex) { ModBase.Log( ex, "单选框勾选改变错误", ModBase.LogLevel.Hint, userSummary: Lang.Text("Application.Control.Error.OperationFailed")); } } private void SyncUI() { if (ControlVisualHelpers.ShouldAnimate(this)) // 防止默认属性变更触发动画 { if (Checked) { // 由无变有 if (ShapeDot.Opacity < 0.01d) ShapeDot.Opacity = 1d; ModAnimation.AniStart( new[] { ModAnimation.AaScale(ShapeBorder, 10d - ShapeBorder.Width, animationTimeOfCheck, ease: new ModAnimation.AniEaseOutFluent(ModAnimation.AniEasePower.Weak), absolute: true), ModAnimation.AaScale(ShapeBorder, 8d, animationTimeOfCheck * 2, (int)Math.Round(animationTimeOfCheck * 0.6d), new ModAnimation.AniEaseOutBack(), absolute: true) }, "MyRadioBox Border " + Uuid); ModAnimation.AniStart( new[] { ModAnimation.AaScale(ShapeDot, 9d - ShapeDot.Width, (int)Math.Round(animationTimeOfCheck * 2.6d), ease: new ModAnimation.AniEaseOutBack(ModAnimation.AniEasePower.Weak), absolute: true), ModAnimation.AaOpacity(ShapeDot, 1d - ShapeDot.Opacity, (int)Math.Round(animationTimeOfCheck * 0.5d), (int)Math.Round(animationTimeOfCheck * 0.6d)) }, "MyRadioBox Dot " + Uuid); ModAnimation.AniStart( ModAnimation.AaColor(ShapeBorder, Shape.StrokeProperty, IsMouseOver ? "ColorBrush3" : IsEnabled ? "ColorBrush2" : "ColorBrushGray4", animationTimeOfCheck), "MyRadioBox BorderColor " + Uuid); } else { // 由有变无 ModAnimation.AniStart( new[] { ModAnimation.AaScale(ShapeBorder, 18d - ShapeBorder.Width, animationTimeOfCheck, ease: new ModAnimation.AniEaseOutFluent(), absolute: true) }, "MyRadioBox Border " + Uuid); ModAnimation.AniStart( new[] { ModAnimation.AaScale(ShapeDot, -ShapeDot.Width, animationTimeOfCheck, ease: new ModAnimation.AniEaseInFluent(), absolute: true), ModAnimation.AaOpacity(ShapeDot, -ShapeDot.Opacity, (int)Math.Round(animationTimeOfCheck * 0.5d), (int)Math.Round(animationTimeOfCheck * 0.2d)) }, "MyRadioBox Dot " + Uuid); ModAnimation.AniStart( ModAnimation.AaColor(ShapeBorder, Shape.StrokeProperty, IsMouseOver ? "ColorBrush3" : IsEnabled ? "ColorBrush1" : "ColorBrushGray4", animationTimeOfCheck), "MyRadioBox BorderColor " + Uuid); } } else { // 不使用动画 ModAnimation.AniStop("MyRadioBox Border " + Uuid); ModAnimation.AniStop("MyRadioBox Dot " + Uuid); ModAnimation.AniStop("MyRadioBox BorderColor " + Uuid); if (Checked) { ShapeDot.Width = 9d; ShapeDot.Height = 9d; ShapeDot.Opacity = 1d; ShapeDot.Margin = new Thickness(5.5d, 0d, 0d, 0d); ShapeBorder.SetResourceReference(Shape.StrokeProperty, IsEnabled ? "ColorBrush2" : "ColorBrushGray4"); } else { ShapeDot.Width = 0d; ShapeDot.Height = 0d; ShapeDot.Opacity = 0d; ShapeDot.Margin = new Thickness(10d, 0d, 0d, 0d); ShapeBorder.SetResourceReference(Shape.StrokeProperty, IsEnabled ? "ColorBrush1" : "ColorBrushGray4"); } } } private void Radiobox_MouseUp() { if (!mouseDowned) return; ModBase.Log("[Control] 按下单选框:" + Text); SetChecked(true, true); mouseDowned = false; ModAnimation.AniStart(ModAnimation.AaColor(ShapeBorder, Shape.FillProperty, "ColorBrushHalfWhite", 100), "MyRadioBox Background " + Uuid); } private void Radiobox_MouseDown() { mouseDowned = true; Focus(); ModAnimation.AniStart(ModAnimation.AaColor(ShapeBorder, Shape.FillProperty, "ColorBrushBg1", 100), "MyRadioBox Background " + Uuid); if (!Checked) ModAnimation.AniStart( ModAnimation.AaScale(ShapeBorder, 16.5d - ShapeBorder.Width, 1000, ease: new ModAnimation.AniEaseOutFluent(ModAnimation.AniEasePower.Strong), absolute: true), "MyRadioBox Border " + Uuid); } private void Radiobox_MouseLeave() { if (!mouseDowned) return; mouseDowned = false; ModAnimation.AniStart(ModAnimation.AaColor(ShapeBorder, Shape.FillProperty, "ColorBrushHalfWhite", 100), "MyRadioBox Background " + Uuid); if (!Checked) ModAnimation.AniStart( ModAnimation.AaScale(ShapeBorder, 18d - ShapeBorder.Width, ease: new ModAnimation.AniEaseOutFluent(ModAnimation.AniEasePower.Strong), absolute: true), "MyRadioBox Border " + Uuid); } private void Radiobox_IsEnabledChanged() { if (ControlVisualHelpers.ShouldAnimate(this)) // 防止默认属性变更触发动画 { // 有动画 if (IsEnabled) { // 可用 Radiobox_MouseLeaveAnimation(); } else { // 不可用 ModAnimation.AniStart( ModAnimation.AaColor(ShapeBorder, Shape.StrokeProperty, ThemeManager.colorGray4 - ShapeBorder.Stroke, animationTimeOfMouseOut), "MyRadioBox BorderColor " + Uuid); ModAnimation.AniStart( ModAnimation.AaColor(LabText, TextBlock.ForegroundProperty, ThemeManager.colorGray4 - LabText.Foreground, animationTimeOfMouseOut), "MyRadioBox TextColor " + Uuid); } } else { // 无动画 ModAnimation.AniStop("MyRadioBox BorderColor " + Uuid); ModAnimation.AniStop("MyRadioBox TextColor " + Uuid); LabText.SetResourceReference(TextBlock.ForegroundProperty, IsEnabled ? "ColorBrush1" : "ColorBrushGray4"); ShapeBorder.SetResourceReference(Shape.StrokeProperty, IsEnabled ? Checked ? "ColorBrush2" : "ColorBrush1" : "ColorBrushGray4"); } } private void Radiobox_MouseEnterAnimation() { ModAnimation.AniStart( ModAnimation.AaColor(ShapeBorder, Shape.StrokeProperty, "ColorBrush3", animationTimeOfMouseIn), "MyRadioBox BorderColor " + Uuid); ModAnimation.AniStart( ModAnimation.AaColor(LabText, TextBlock.ForegroundProperty, "ColorBrush3", animationTimeOfMouseIn), "MyRadioBox TextColor " + Uuid); } private void Radiobox_MouseLeaveAnimation() { if (!IsEnabled) return; // MouseLeave 比 IsEnabledChanged 后执行,所以如果自定义事件修改了 IsEnabled,将导致显示错误 if (ControlVisualHelpers.ShouldAnimate(this)) { ModAnimation.AniStart( ModAnimation.AaColor(ShapeBorder, Shape.StrokeProperty, IsEnabled ? Checked ? "ColorBrush2" : "ColorBrush1" : "ColorBrushGray4", animationTimeOfMouseOut), "MyRadioBox BorderColor " + Uuid); ModAnimation.AniStart( ModAnimation.AaColor(LabText, TextBlock.ForegroundProperty, IsEnabled ? "ColorBrush1" : "ColorBrushGray4", animationTimeOfMouseOut), "MyRadioBox TextColor " + Uuid); } else { ModAnimation.AniStop("MyRadioBox BorderColor " + Uuid); ModAnimation.AniStop("MyRadioBox TextColor " + Uuid); ShapeBorder.SetResourceReference(Shape.StrokeProperty, IsEnabled ? Checked ? "ColorBrush2" : "ColorBrush1" : "ColorBrushGray4"); LabText.SetResourceReference(TextBlock.ForegroundProperty, IsEnabled ? "ColorBrush1" : "ColorBrushGray4"); } } }