C#、Xamarin Forms:初始化时未引发自定义 TextChangedEvent

C#, Xamarin Forms: No Custom TextChangedEvent Raised on initialization

我正在创建一个 Xamarin.Forms MVVM 应用程序(仅使用 Android),只要它们的文本 属性 具有特定值,它就需要将某些按钮标为红色。 (目的:提醒用户按下按钮和 select 一个值,这将更改按钮文本 属性 并因此删除红色轮廓)

为此,我创建了以下文档:

扩展默认按钮的自定义按钮 CButton Button:

    public class CButton : Button
    {
        // this Hides the Default .Text-Property
        public string Text 
        {
            get => base.Text;
            set
            {
                base.Text = value;
                TextChangedEvent(this, new EventArgs());
            }

        }
        // The Raised Event
        protected virtual void TextChangedEvent(object sender, EventArgs e)
        {
            EventHandler<EventArgs> handler = TextChanged;
            handler(sender, e);
        }
        public event EventHandler<EventArgs> TextChanged;
    }

自定义行为利用了引发的 TextChangedEvent

    public class ButtonValBehavior : Behavior<CButton>
    {
        protected override void OnAttachedTo(CButton bindable)
        {
            bindable.TextChanged += HandleTextChanged;
            base.OnAttachedTo(bindable);
        }
        void HandleTextChanged(object sender, EventArgs e)
        {
            string forbidden = "hh:mm|dd.mm.yyyy";
            if (forbidden.Contains((sender as CButton).Text.ToLower()))
            {
                //Do when Button Text = "hh:mm" || "dd.mm.yyyy"
                (sender as CButton).BorderColor = Color.Gray;
            }
            else
            {
                //Do whenever Button.Text is any other value
                (sender as CButton).BorderColor = Color.FromHex("#d10f32");
            }
        }
        protected override void OnDetachingFrom(CButton bindable)
        {
            bindable.TextChanged -= HandleTextChanged;
            base.OnDetachingFrom(bindable);
        }
    }

ViewModel 的相关部分如下所示:

    public class VM_DIVI : VM_Base
    {
        public VM_DIVI(O_BasisProtokoll base)
        {
            Base = base;
        }

        private O_BasisProtokoll _base = null;
        public O_BasisProtokoll Base
        {
            get => _base;
            set
            {
                _base = value;
                OnPropertyChanged();
            }
        }

        Command _datePopCommand;
        public Command DatePopCommand
        {
            get
            {
                return _datePopCommand ?? (_datePopCommand = new Command(param => ExecuteDatePopCommand(param)));
            }
        }
        void ExecuteDatePopCommand(object param)
        {
            //launch popup
            var p = new PP_DatePicker(param);
            PopupNavigation.Instance.PushAsync(p);
        }
    }

.xmal 看起来如下(b 是命名空间的 xmlns):

    <b:CButton x:Name="BTN_ED_Datum"
               Text="{Binding Base.ED_datum, Mode=TwoWay}" 
               Grid.Column="1"
               Command="{Binding DatePopCommand}" 
               CommandParameter="{x:Reference BTN_ED_Datum}">
        <b:CButton.Behaviors>
            <b:ButtonValBehavior/>
        </b:CButton.Behaviors>
    </b:CButton>

只要输入是由用户交互引起的,此解决方案就可以正常工作。但是,在页面初始化期间分配值时,不会创建红色轮廓,实际上不会引发 TextChangedEvent。通过使用断点,我注意到在初始化期间从未设置 CButtonText 属性,尽管它实际上会在视图中。

尽管摆弄了我的解决方案,但我无法在初始化时完成这项工作。我试图通过在每个按钮的构造函数中默认勾勒出每个按钮的轮廓来解决这个问题,但是这会将每个按钮勾勒出红色,即使它们的文本值不需要它们。

我怎样才能实现最初的目标?

非常感谢!

已经有一段时间了,但如果我没记错的话,我最后做的是:

  • 将自定义按钮的新 Text-属性 更改为 CText
  • 确保我已为任何默认未启用的元素激活 Mode=TwoWay。 (在 msdn 上查找绑定模式以获取更多信息)
  • 使 CText 成为 CButton
  • 的可绑定 属性

我的自定义按钮现在如下所示:

using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;

namespace EORG_Anton.Model
{
    public class CButton : Button
    {
        public static readonly BindableProperty CTextProperty = 
            BindableProperty.Create(nameof(CText), 
            typeof(string), 
            typeof(CButton), 
            default(string), 
            BindingMode.TwoWay,
            propertyChanged: OnTextChanged);
        private static void OnTextChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var control = (CButton)bindable;
            var value = (string)newValue;
            control.CText = value;
        }
        public string CText
        {
            get => base.Text;
            set
            {
                base.Text = value;
                TextChangedEvent(this, new EventArgs());
            }

        }
        protected virtual void TextChangedEvent(object sender, EventArgs e)
        {
            EventHandler<EventArgs> handler = TextChanged;
            handler(sender, e);
        }
        public event EventHandler<EventArgs> TextChanged;
    }
}