在 Xamarin.Forms 中绑定自定义视图

在 Xamarin.Forms 中绑定自定义视图

问题描述:

我在将 Xamarin 表单中的自定义视图中的数据绑定到包含页面的视图模型时遇到问题.

I have a problem binding data in a custom view in Xamarin forms to the view model of the containing page.

我的自定义视图很简单,一对标签代表一个键值对:

My Custom View is very simple, a pair of labels representing a key value pair:

<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="KeyValueView">
    <Grid VerticalOptions="Start">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <Label x:Name="KeyLabel" Text="{Binding Key}"  Grid.Column="0" HorizontalOptions="Start" />
        <Label x:Name="ValueLabel" Text="{Binding Value}" Grid.Column="1" HorizontalOptions="EndAndExpand" />
    </Grid>
</ContentView>

背后的代码:

public partial class KeyValueView : ContentView
{
    public KeyValueView()
    {
        InitializeComponent();
        this.VerticalOptions = LayoutOptions.Start;
        this.BindingContext = this;
    }

    public static readonly BindableProperty ValueProperty =
                BindableProperty.Create<KeyValueView, string>(w => w.Value, default(string));

    public string Value
    {
        get {return (string)GetValue(ValueProperty);}
        set {SetValue(ValueProperty, value);}
    }

    public static readonly BindableProperty KeyProperty =
        BindableProperty.Create<KeyValueView, string>(w => w.Key, default(string));

    public string Key
    {
        get {return (string)GetValue(KeyProperty);}
        set {SetValue(KeyProperty, value);}
    }
}

这在一个页面中被消耗如下:

This is consumed in a page as follows:

<views:KeyValueView Key="Order Number" Value="{Binding document_number}" />

问题是键字符串按预期显示,但值字符串不是.我试过在 document_number 属性上强制一个 PropertyChanged 事件,这没有帮助.我还尝试在自定义视图的键/值属性的 setter 期间显式设置标签上的 Text 属性:

The problem is that the Key string is displayed as expected, but the value string is not. I have tried forcing a PropertyChanged event on the document_number property, this did not help. I have also tried explicitly setting the Text property on the Labels during the setter of the Key/Value properties of the custom view:

public string Key
    {
        get {return (string)GetValue(KeyProperty);}
        set {
            SetValue(KeyProperty, value);
            KeyLabel.Text = value;
        }
    }

这再次没有帮助,setter 代码似乎永远不会被执行(我在它上面放置了一个断点,但它没有被命中)

Again this did not help, the setter code never seems to get executed (I placed a breakpoint on it and it was not hit)

如果我添加一个开箱即用的控件,例如直接绑定到页面中的属性的 Label,这将正确显示:

If I add an out of the box control such as a Label bound directly to the property in the page, this displays correctly:

<Label Text="{Binding document_number}"/>
<views:KeyValueView Key="Order Number" Value="{Binding document_number}" />

谁能解释为什么会发生这种情况(或者说没有发生)?

Can anyone explain why this is happening (or rather not happening)?

不要在自定义控件内部分配绑定.使用这个:

Don't assign bindings internally inside custom controls. Use this:

public partial class KeyValueView : ContentView
{
    public KeyValueView()
    {
        InitializeComponent();
        this.VerticalOptions = LayoutOptions.Start;
    }

    public static readonly BindableProperty ValueProperty =
        BindableProperty.Create<KeyValueView, string>(w => w.Value, default(string));

    public string Value
    {
        get {return (string)GetValue(ValueProperty);}
        set {SetValue(ValueProperty, value);}
    }

    public static readonly BindableProperty KeyProperty =
        BindableProperty.Create<KeyValueView, string>(w => w.Key, default(string));

    public string Key
    {
        get {return (string)GetValue(KeyProperty);}
        set {SetValue(KeyProperty, value);}
    }
    protected override void OnPropertyChanged(string propertyName)
    {
        base.OnPropertyChanged(propertyName);

        if (propertyName == ValueProperty.PropertyName)
        {
            ValueLabel.Text = Value;
        }
        if (propertyName == KeyProperty.PropertyName)
        {
            KeyLabel.Text = Key;
        }
    }
}

<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="KeyValueView">
    <Grid VerticalOptions="Start">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <Label x:Name="KeyLabel" Grid.Column="0" HorizontalOptions="Start" />
        <Label x:Name="ValueLabel" Grid.Column="1" HorizontalOptions="EndAndExpand" />
    </Grid>
</ContentView>

将属性(例如值)视为对 BindableProperty (ValueProperty) 的引用.如果您在 Value setter 中执行某些操作,则不会通知 BindableProperty 并且相反,如果您对 BindableProperty 执行某些操作(分配/更改 ValueProperty)-则不会调用属性 Value setter.

Treat properties (eg. Value) as references to BindableProperty (ValueProperty). If you do something inside Value setter BindableProperty won't be notified about it AND opposite if you do something with BindableProperty (assign/change ValueProperty) - property Value setter won't be called.

如果您想处理 PropertyChanged,您可以覆盖它 (OnPropertyChanged) 或 Actions(作为创建 BindableProperty 时的附加参数).

If you want to handle PropertyChanged you have an override for that (OnPropertyChanged) or Actions (as additional parameters when creating BindableProperty).