当我尝试将全局样式应用于Xamarin Forms中的自定义ContentView时发生NullReferenceException [英] NullReferenceException when I'm trying to apply global style to custom ContentView in Xamarin Forms

查看:93
本文介绍了当我尝试将全局样式应用于Xamarin Forms中的自定义ContentView时发生NullReferenceException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我为我的项目创建了控件层次结构:抽象BaseSettingsElement并继承了EntrySettingsElementPickerSettingsElementSwitchSettingsElement等.基类提供了更改标题,子标题的文本,颜色,字体的属性.属性声明的示例:

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");
}

因此,这似乎是由于调用InitializeComponentPropertyChangedHandler的顺序引起的问题.

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屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆