当我尝试将全局样式应用于Xamarin Forms中的自定义ContentView时发生NullReferenceException [英] NullReferenceException when I'm trying to apply global style to custom ContentView in Xamarin Forms
问题描述
我为我的项目创建了控件层次结构:抽象BaseSettingsElement
并继承了EntrySettingsElement
,PickerSettingsElement
,SwitchSettingsElement
等.基类提供了更改标题,子标题的文本,颜色,字体的属性.属性声明的示例:
I created hierarchy of controls for my project: abstract BaseSettingsElement
and inherited EntrySettingsElement
, PickerSettingsElement
, SwitchSettingsElement
etc. The base class provide the properties to change text, color, font of header/subheader. Example of property declaration:
public static readonly BindableProperty HeadTextColorProperty =
BindableProperty.Create("HeadTextColor", typeof(Color), typeof(BaseSettingsElement), Color.FromHex("#ffffff"),
propertyChanged: (bindable, oldValue, newValue) =>
{
(bindable as BaseSettingsElement).headText.TextColor = (Color)newValue;
});
//...
public Color HeadTextColor
{
get { return (Color)GetValue(HeadTextColorProperty); }
set { SetValue(HeadTextColorProperty, value); }
}
当我在xaml中创建此控件并将一些属性应用于它们时,没有问题:
There is no problem when I'm creating this controls in xaml and apply some properties to them:
<custom:EntrySettingsElement Grid.Row="16"
InputType="Number"
DividersVisibility="None"
IsEnabled="False"
HeadTextColor="Red"
Text="0" />
但是当我尝试将app.xaml中的全局样式应用于某些控件时,我在这里有一个NullRefferenceException
:
But when I'm trying to apply a global style in app.xaml to some of my controls I have a NullRefferenceException
here:
public static readonly BindableProperty HeadTextColorProperty =
BindableProperty.Create("HeadTextColor", typeof(Color), typeof(BaseSettingsElement), Color.FromHex("#ffffff"),
propertyChanged: (bindable, oldValue, newValue) =>
{
(bindable as BaseSettingsElement).headText.TextColor = (Color)newValue;
//Here is a NullReferenceException: bindable object is not null but the field 'headText' is null...
});
以防万一基本控件的xaml:
Just in case the xaml of the base control:
<?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="RemoteControlApp.CustomViews.BaseSettingsElement"
xmlns:custom="clr-namespace:RemoteControlApp.CustomViews;assembly=RemoteControlApp">
<ContentView.Content>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<custom:Divider x:Name="topDivider"
Grid.Row="0"
Grid.ColumnSpan="2"
BackgroundColor="{StaticResource grayColor}" />
<StackLayout x:Name="container"
Orientation="Vertical"
Spacing="0"
Grid.Column="0"
Margin="15,12,0,12"
Grid.Row="1"
VerticalOptions="Center">
<Label x:Name="headText" />
<Label x:Name="bodyText" />
</StackLayout>
<custom:Divider x:Name="bottomDivider"
Grid.Row="2"
Grid.ColumnSpan="2"
BackgroundColor="{StaticResource grayColor}" />
<ContentView x:Name="asideContainer"
Margin="0,0,15,0"
Grid.Column="1"
Grid.Row="1" />
</Grid>
</ContentView.Content>
</ContentView>
推荐答案
快速查看编译器生成的文件(.xaml.g.cs)-您会注意到这些专用字段是在InitializeComponent
方法中分配的:>
A quick look at compiler generated files (.xaml.g.cs) - you will notice that these private fields are assigned in InitializeComponent
method:
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Forms.Build.Tasks.XamlG", "0.0.0.0")]
private void InitializeComponent()
{
global::Xamarin.Forms.Xaml.Extensions.LoadFromXaml(this, typeof(BaseSettingsElement));
headText = global::Xamarin.Forms.NameScopeExtensions.FindByName<global::Xamarin.Forms.Label>(this, "headText");
}
因此,这似乎是由于调用InitializeComponent
和PropertyChangedHandler
的顺序引起的问题.
Therefore, this seems to be a problem caused due to the order in which InitializeComponent
and PropertyChangedHandler
are called.
在第一种情况下,显式设置属性时,这是方法被调用的顺序.
In first case, when property is explicitly set, this is the order the methods are called.
InitializeComponent -Start
InitializeComponent -End
HeadTextColor -PropertyChanged
在第二种情况下,使用全局样式设置属性时,顺序为:
While, in second case, while using global style to set properties, the order is:
HeadTextColor -PropertyChanged
InitializeComponent -Start
InitializeComponent -End
看起来该属性是在基类构造函数中的某个位置设置的.因此,NullReferenceException
是预期的.
Looks like the property is set somewhere in a base class constructor. So the NullReferenceException
is expected.
有两种解决方法.
解决方案1(推荐)
代替使用属性更改的处理程序,而使用Binding
设置内部子控件的属性,父节点为Source
.例如:
Instead of using property-changed handler, use Binding
to set properties on inner-child controls, with parent node as Source
. For e.g:
<?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="RemoteControlApp.CustomViews.BaseSettingsElement"
xmlns:custom="clr-namespace:RemoteControlApp.CustomViews;assembly=RemoteControlApp"
x:Name="_parent">
<ContentView.Content>
<!-- .... -->
<Label TextColor="{Binding HeadTextColor, Source={x:Reference _parent}}" />
解决方案2
或者,在InitializeComponent
之后设置内部控件的属性.
Or, set inner control's properties after InitializeComponent
.
public MyView()
{
Debug.WriteLine("InitializeComponent -Start");
InitializeComponent();
Debug.WriteLine("InitializeComponent -End");
//InitializeControl();
this.headText.TextColor = HeadTextColor;
}
注意::在第二种解决方案中,您仍然需要更改属性的处理程序.否则,在XAML中显式设置属性不会将更改传播到内部控件.我猜想是为了进行空检查.
Note: In second solution, you will still need the property-changed handler. Otherwise, explicitly setting properties in XAML will not propagate the change to inner controls. I guess a null check is in order for that.
这篇关于当我尝试将全局样式应用于Xamarin Forms中的自定义ContentView时发生NullReferenceException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!