我有一个带有Required
属性的自定义Xamarin Forms组件,该属性在我的视图模型中设置为True
。在构造函数中,我调用方法CheckValidity()
,该方法检查是否需要该条目。由于某些原因,在我键入条目之前(触发Required
属性进行更新),或者单击条目的内部或外部(触发Text
事件),Unfocused
一直显示为假。
您知道为什么True
的初始Required
值在组件中发生某些活动后才生效吗?谢谢!
在视图中使用
<ui:ValidatingEntry Text="{Binding MyText}" Required="True" />
组件XAML
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="MyPackage.ValidatingEntry">
<ContentView.Content>
<StackLayout x:Name="entryContainer">
<Entry x:Name="entry" />
<Label x:Name="message" />
</StackLayout>
</ContentView.Content>
</ContentView>
组件C#
public partial class ValidatingEntry : ContentView
{
private enum ValidationErrorType
{
NONE,
REQUIRED,
CUSTOM
}
private ValidationErrorType validationErrorType;
public static readonly BindableProperty TextProperty = BindableProperty.Create("Text", typeof(string), typeof(ValidatingEntry), default(string), BindingMode.TwoWay);
public string Text
{
get
{
return (string)GetValue(TextProperty);
}
set
{
SetValue(TextProperty, value);
Debug.WriteLine("set text to: " + value);
CheckValidity();
UpdateMessage();
}
}
public static readonly BindableProperty RequiredProperty = BindableProperty.Create("Required", typeof(bool), typeof(ValidatingEntry), false);
public bool Required
{
get
{
Debug.WriteLine("getting required property: " + (bool)GetValue(RequiredProperty));
return (bool)GetValue(RequiredProperty);
}
set
{
SetValue(RequiredProperty, value);
//THIS NEVER PRINTS
Debug.WriteLine("set required property to: " + value);
CheckValidity();
}
}
public static readonly BindableProperty IsValidProperty = BindableProperty.Create("IsValid", typeof(bool), typeof(ValidatingEntry), true, BindingMode.OneWayToSource);
public bool IsValid
{
get
{
return (bool)GetValue(IsValidProperty);
}
set
{
SetValue(IsValidProperty, value);
}
}
private void CheckValidity()
{
Debug.WriteLine("checking validity");
Debug.WriteLine("required? " + Required); //prints False until Entry is unfocused or user types in Entry
Debug.WriteLine("string empty? " + string.IsNullOrEmpty(Text));
if (Required && string.IsNullOrEmpty(Text))
{
Debug.WriteLine("required but not provided");
IsValid = false;
validationErrorType = ValidationErrorType.REQUIRED;
}
else
{
IsValid = true;
validationErrorType = ValidationErrorType.NONE;
}
}
private void UpdateMessage()
{
switch (validationErrorType)
{
case ValidationErrorType.NONE:
message.Text = "";
break;
case ValidationErrorType.REQUIRED:
message.Text = "This field is required.";
break;
}
}
public ValidatingEntry()
{
InitializeComponent();
entry.SetBinding(Entry.TextProperty, new Binding("Text", source: this));
CheckValidity(); //at this point, Required is always false
entry.Unfocused += (sender, e) =>
{
CheckValidity();
UpdateMessage();
};
}
}
答案 0 :(得分:1)
在返回类型的构造函数之前,不会使用XAML中的值更新类型的属性,因此您要在构造函数返回后运行CheckValidity
。
最简单,最快的方法是启动后台线程以运行CheckValidity
,因为这将允许构造方法返回并使用XAML中设置的值填充属性。所以试试这个:
public ValidatingEntry()
{
InitializeComponent();
entry.SetBinding(Entry.TextProperty, new Binding("Text", source: this));
Task.Run(() => {
CheckValidity();
UpdateMessage();
});
entry.Unfocused += (sender, e) =>
{
CheckValidity();
UpdateMessage();
};
}
值得注意的是,这并不是Forms独有的。在默认(无参数)构造函数中,构造函数在运行时仅会设置默认值。因此,如果您希望默认值为true
,请在BindableProperty.Create(...)
方法调用的Required
属性中设置默认值,例如:
public static readonly BindableProperty RequiredProperty =
BindableProperty.Create(
"Required",
typeof(bool),
typeof(ValidatingEntry),
true);
举个例子,当您这样做时,可能会以为:
var x = new MyType { MyString = "New text" };
将在构造函数中设置MyString
,但这不是事实。上面是语法糖,在编译时更改为:
var x = new MyType();
x.MyString = "New text";
因此,构造函数完成,然后设置了属性。
但是,如果您有默认值,例如:
public class MyType
{
public string MyString { get; set; } = "Default text";
}
MyString
将设置为“默认文本”,并在构造函数中可用。
一个示例控制台应用程序来演示:
class MainClass
{
public static void Main(string[] args)
{
var x = new MyType { MyString = "New text" };
var y = Console.ReadKey();
}
}
public class MyType
{
public MyType()
{
Console.WriteLine($"Constructor: {MyString}");
Task.Run(() => Console.WriteLine($"Task: {MyString}"));
}
public string MyString { get; set; } = "Default text";
}
输出将是:
构造函数:默认文本
任务:新文本
答案 1 :(得分:1)
尝试将CheckValidity()
放在构造函数完成后引发的事件处理程序中,例如BindingContextChanged
:
public ValidatingEntry()
{
InitializeComponent();
...
BindingContextChanged += (sender, e) =>
{
CheckValidity();
};
}