C# Xamarin表单转换器:句柄转换错误

C# Xamarin表单转换器:句柄转换错误,c#,xamarin,xamarin.forms,prism,ivalueconverter,C#,Xamarin,Xamarin.forms,Prism,Ivalueconverter,我有一个将字节[]转换为字符串的函数,反之亦然。只有当字符串格式正确时,转换器才能将其从用户给定的字符串转换为字节。现在我只是在转换失败时返回原始对象,这将给我一个 绑定:“4”无法转换为类型“System.Byte[]”。 日志中出现错误。这没关系,但我想通过在编辑器周围显示红色边框并禁用“发送”按钮来通知用户,他编写的字符串格式不正确 是否可以通过MVVM模式(PRISM)告诉UI转换失败?在WPF中有一个可以使用的,我没有发现任何类似的Xamarin 转换器: public class B

我有一个将
字节[]
转换为
字符串的函数,反之亦然。只有当字符串格式正确时,转换器才能将其从用户给定的
字符串
转换为
字节
。现在我只是在转换失败时返回原始对象,这将给我一个

绑定:“4”无法转换为类型“System.Byte[]”。

日志中出现错误。这没关系,但我想通过在编辑器周围显示红色边框并禁用“发送”按钮来通知用户,他编写的字符串格式不正确

是否可以通过MVVM模式(PRISM)告诉UI转换失败?在WPF中有一个可以使用的,我没有发现任何类似的Xamarin

转换器:

public class ByteArrayConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value is byte[] b)
            return BitConverter.ToString(b);//Success
        return value;//Failed
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value is string str && (str.Length - 2) % 3 == 0)
        {
            int len = (str.Length + 1) / 3;
            byte[] byteArray = new byte[len];

            for (int i = 0; i < len; i++)
                byteArray[i] = System.Convert.ToByte(str.Substring(3 * i, 2), 16);
            return byteArray;//Success
        }
        return value;//Failed
    }
}
公共类ByteArrayConverter:IValueConverter
{
公共对象转换(对象值、类型targetType、对象参数、System.Globalization.CultureInfo区域性)
{
if(值为字节[]b)
返回BitConverter.ToString(b);//成功
返回值;//失败
}
公共对象转换回(对象值、类型targetType、对象参数、System.Globalization.CultureInfo区域性)
{
if(值为字符串str&&(str.Length-2)%3==0)
{
int len=(str.Length+1)/3;
字节[]字节数组=新字节[len];
对于(int i=0;i
XAML:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:prism="http://prismlibrary.com"
             xmlns:vc="clr-namespace:XXX.ValueConverter"
             prism:ViewModelLocator.AutowireViewModel="True"
             x:Class="XXX.Views.Device.SendMessagePage">
    <ContentPage.Resources>
        <vc:ByteArrayConverter x:Key="byteArrayConverter"/>
    </ContentPage.Resources>
    <Editor Text="{Binding Payload, Converter={StaticResource byteArrayConverter}}"></Editor>
    
</ContentPage>

对于Xamarin.Forms,可以使用IValidationRule执行此操作

首先,创建一个从IValidationRule接口派生的类来指定验证规则

 public interface IValidationRule<T>
{
    string ValidationMessage { get; set; }
    bool Check(T value);
}

 public class IsNotNullOrEmptyRule<T> : IValidationRule<T>
{
    public string ValidationMessage { get; set; }

    public bool Check(T value)
    {
        if (value == null)
        {
            return false;
        }

        var str = $"{value }";
        return !string.IsNullOrWhiteSpace(str);
    }
}

public class HasValidAgeRule<T> : IValidationRule<T>
{
    public string ValidationMessage { get; set; }

    public bool Check(T value)
    {
        if (value is DateTime bday)
        {
            DateTime today = DateTime.Today;
            int age = today.Year - bday.Year;
            return (age >= 18);
        }

        return false;
    }
}
公共接口验证规则
{
字符串验证消息{get;set;}
布尔检查(T值);
}
公共类不是null或mptyrule:IValidationRule
{
公共字符串验证消息{get;set;}
公共布尔检查(T值)
{
如果(值==null)
{
返回false;
}
var str=$“{value}”;
return!string.IsNullOrWhiteSpace(str);
}
}
公共类HasValidAgeRule:IValidationRule
{
公共字符串验证消息{get;set;}
公共布尔检查(T值)
{
if(值为DateTime bday)
{
DateTime today=DateTime.today;
int age=今天.Year-bday.Year;
返回(年龄>=18岁);
}
返回false;
}
}
其次,向属性添加验证规则

public interface IValidatable<T> : INotifyPropertyChanged
{
    List<IValidationRule<T>> Validations { get; }

    List<string> Errors { get; set; }

    bool Validate();

    bool IsValid { get; set; }
}

 public class ValidatableObject<T> : IValidatable<T>
{
    public event PropertyChangedEventHandler PropertyChanged;

    public List<IValidationRule<T>> Validations { get; } = new List<IValidationRule<T>>();

    public List<string> Errors { get; set; } = new List<string>();

    public bool CleanOnChange { get; set; } = true;

    T _value;
    public T Value
    {
        get => _value;
        set
        {
            _value = value;

            if (CleanOnChange)
                IsValid = true;
        }
    }

    public bool IsValid { get; set; } = true;

    public virtual bool Validate()
    {
        Errors.Clear();

        IEnumerable<string> errors = Validations.Where(v => !v.Check(Value))
            .Select(v => v.ValidationMessage);

        Errors = errors.ToList();
        IsValid = !Errors.Any();

        return this.IsValid;
    }
    public override string ToString()
    {
        return $"{Value}";
    }
}

 public class validationmodel: INotifyPropertyChanged
{
    public ValidatableObject<string> FirstName { get; set; } = new ValidatableObject<string>();
    public ValidatableObject<string> LastName { get; set; } = new ValidatableObject<string>();
    public ValidatableObject<DateTime> BirthDay { get; set; } = new ValidatableObject<DateTime>() { Value = DateTime.Now };
    public validationmodel()
    {

        FirstName.Value = null;
        
        
        AddValidationRules();
        AreFieldsValid();
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void AddValidationRules()
    {
        FirstName.Validations.Add(new IsNotNullOrEmptyRule<string> { ValidationMessage = "First Name Required" });
        LastName.Validations.Add(new IsNotNullOrEmptyRule<string> { ValidationMessage = "Last Name Required" });
        BirthDay.Validations.Add(new HasValidAgeRule<DateTime> { ValidationMessage = "You must be 18 years of age or older" });
    }

    bool AreFieldsValid()
    {
        bool isFirstNameValid = FirstName.Validate();
        bool isLastNameValid = LastName.Validate();
        bool isBirthDayValid = BirthDay.Validate();
        return isFirstNameValid && isLastNameValid && isBirthDayValid;
    }



 }
公共接口IValidTable:INotifyPropertyChanged
{
列表验证{get;}
列出错误{get;set;}
bool验证();
bool是有效的{get;set;}
}
公共类validatable对象:IValidatable
{
公共事件属性更改事件处理程序属性更改;
公共列表验证{get;}=new List();
公共列表错误{get;set;}=new List();
public bool CleanOnChange{get;set;}=true;
T_值;
公共价值
{
获取=>\u值;
设置
{
_价值=价值;
如果(CleanOnChange)
IsValid=true;
}
}
public bool IsValid{get;set;}=true;
公共虚拟bool Validate()
{
错误。清除();
IEnumerable errors=验证。其中(v=>!v.Check(Value))
.选择(v=>v.ValidationMessage);
Errors=Errors.ToList();
IsValid=!Errors.Any();
返回此文件。此文件无效;
}
公共重写字符串ToString()
{
返回$“{Value}”;
}
}
公共类验证模型:INotifyPropertyChanged
{
public ValidatableObject FirstName{get;set;}=new ValidatableObject();
public ValidatableObject LastName{get;set;}=new ValidatableObject();
public ValidatableObject生日{get;set;}=new ValidatableObject(){Value=DateTime.Now};
公共验证模型()
{
FirstName.Value=null;
AddValidationRules();
AreFieldsValid();
}
公共事件属性更改事件处理程序属性更改;
public void AddValidationRules()
{
FirstName.Validations.Add(新的IsNotNullOrEmptyRule{ValidationMessage=“First Name Required”});
添加(新的IsNotNullOrEmptyRule{ValidationMessage=“Last Name Required”});
添加(新的HasValidAgeRule{ValidationMessage=“您必须年满18岁”});
}
bool AreFieldsValid()
{
bool isfirstnamefalid=FirstName.Validate();
bool isLastNameValid=LastName.Validate();
bool isBirthDayValid=BirthDay.Validate();
返回isFirstNameValid&&isLastNameValid&&isBirthDayValid;
}
}
突出显示包含无效数据的控件:

public class FirstValidationErrorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        ICollection<string> errors = value as ICollection<string>;
        return errors != null && errors.Count > 0 ? errors.ElementAt(0) : null;
    }

   

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

 public class InverseBoolConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (!(value is bool))
        {
            throw new InvalidOperationException("The target must be a boolean");
        }

        return !(bool)value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return null;
    }
}

public class BehaviorBase<T> : Behavior<T>
where T : BindableObject
{
    #region Properties
    public T AssociatedObject
    {
        get;
        private set;
    }
    #endregion
    #region NormalMethods
    private void OnBindingContextChanged(object sender, EventArgs e)
    {
        OnBindingContextChanged();
    }
    #endregion
    #region Overrides
    protected override void OnAttachedTo(T bindable)
    {
        base.OnAttachedTo(bindable);
        AssociatedObject = bindable;
        if (bindable.BindingContext != null)
        {
            BindingContext = bindable.BindingContext;
        }

        bindable.BindingContextChanged += OnBindingContextChanged;
    }
    protected override void OnDetachingFrom(T bindable)
    {
        base.OnDetachingFrom(bindable);
        bindable.BindingContextChanged -= OnBindingContextChanged;
        AssociatedObject = null;
    }
    protected override void OnBindingContextChanged()
    {
        base.OnBindingContextChanged();
        BindingContext = AssociatedObject.BindingContext;
    }
    #endregion
}

 public class EntryLineValidationBehaviour : BehaviorBase<Entry>
{
    #region StaticFields
    public static readonly BindableProperty IsValidProperty = BindableProperty.Create(nameof(IsValid), typeof(bool), typeof(EntryLineValidationBehaviour), true, BindingMode.Default, null, (bindable, oldValue, newValue) => OnIsValidChanged(bindable, newValue));
    #endregion
    #region Properties
    public bool IsValid
    {
        get
        {
            return (bool)GetValue(IsValidProperty);
        }
        set
        {
            SetValue(IsValidProperty, value);
        }
    }
    #endregion
    #region StaticMethods
    private static void OnIsValidChanged(BindableObject bindable, object newValue)
    {
        if (bindable is EntryLineValidationBehaviour IsValidBehavior &&
             newValue is bool IsValid)
        {
            IsValidBehavior.AssociatedObject.PlaceholderColor = IsValid ? Color.Default : Color.Red;
        }
    }

    #endregion
}
公共类FirstValidationErrorConverter:IValueConverter
{
公共对象转换(对象值、类型targetType、对象参数、CultureInfo区域性)
{
ICollection errors=作为ICollection的值;
返回错误!=null&&errors.Count>0?errors.ElementAt(0):null;
}
公共对象转换回(对象值、类型targetType、对象参数、CultureInfo区域性)
{
抛出新的NotImplementedException();
}
}
公共类InverseBoolConverter:IValueConverter
{
公共对象转换(对象值、类型targetType、对象参数、CultureInfo区域性)
{
如果(!(值为bool))
{
抛出新的InvalidOperationException(“目标必须是布尔值”);
}
返回!(bool)值;
}
公共对象转换回(对象值、类型targetType、对象参数、CultureInfo区域性)
{
返回null;
}
}
公共类行为数据库:行为
T:Bind在哪里
<ContentPage
x:Class="validationapp.MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:behaviour="clr-namespace:validationapp.Behaviors"
xmlns:converter="clr-namespace:validationapp.converters"
xmlns:local="clr-namespace:validationapp">
<ContentPage.Resources>
   
    <converter:InverseBoolConverter x:Key="InverseBoolConverter" />
    <converter:FirstValidationErrorConverter x:Key="FirstValidationErrorConverter" />
    <Style x:Key="ErrorTextStyle" TargetType="Label">
        <Setter Property="TextColor" Value="Red" />
        <Setter Property="FontSize" Value="12" />
    </Style>
</ContentPage.Resources>
<StackLayout>
    <!--  First Name  -->
    <Entry Placeholder="First Name" Text="{Binding FirstName.Value}">
        <Entry.Behaviors>
            <behaviour:EntryLineValidationBehaviour IsValid="{Binding FirstName.IsValid}" />
        </Entry.Behaviors>
    </Entry>

    <Label
        IsVisible="{Binding FirstName.IsValid, Converter={StaticResource InverseBoolConverter}}"
        Style="{StaticResource ErrorTextStyle}"
        Text="{Binding FirstName.Errors, Converter={StaticResource FirstValidationErrorConverter}}" />
    <!--  /First Name  -->

    <!--  Last Name  -->
    <Entry Placeholder="Last Name" Text="{Binding LastName.Value}">
        <Entry.Behaviors>
            <behaviour:EntryLineValidationBehaviour IsValid="{Binding LastName.IsValid}" />
        </Entry.Behaviors>
    </Entry>

    <Label
        IsVisible="{Binding LastName.IsValid, Converter={StaticResource InverseBoolConverter}}"
        Style="{StaticResource ErrorTextStyle}"
        Text="{Binding LastName.Errors, Converter={StaticResource FirstValidationErrorConverter}}" />
    <!--  /Last Name  -->


    <!--  Birthday  -->
    <DatePicker Date="{Binding BirthDay.Value}" />
    <Label
        IsVisible="{Binding BirthDay.IsValid, Converter={StaticResource InverseBoolConverter}}"
        Style="{StaticResource ErrorTextStyle}"
        Text="{Binding BirthDay.Errors, Converter={StaticResource FirstValidationErrorConverter}}" />
    <!--  Birthday  -->
</StackLayout>