自定义控件的 Xamarin 数据绑定值计算为 Xamarin.Forms.Binding

Xamarin databinding value for custom control evaluates as Xamarin.Forms.Binding

我遇到了 Xamarin.Forms 的问题,使用它的人都知道它与 WPF 非常相似。不幸的是,我 运行 遇到了一个我从未遇到过的 WPF 问题。

场景如下:

我的视图模型:

public class ShellViewModel : ViewModelBase
{
    public ShellViewModel()
    {
        ActiveViewModel = new ProjectActionOverviewViewModel();
    }

    private ShellViewChildModel _activeViewModel;

    public ShellViewChildModel ActiveViewModel
    {
        get { return _activeViewModel; }
        set
        {
            Debug.WriteLine("Updating ActiveViewModel.");
            SetValue(ref _activeViewModel, value);
            Task.Run(() => value.ActivateAsync());
            Debug.WriteLine("Updated ActiveViewModel.");
        }
    }

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:viewModels="clr-namespace:MyNamespace.XamarinForms.ViewModels;assembly=MyNamespace.XamarinForms"
             xmlns:views="clr-namespace:MyNamespace.XamarinForms.Views;assembly=MyNamespace.XamarinForms"
             xmlns:resources="clr-namespace:MyNamespace.XamarinForms.Resources;assembly=MyNamespace.XamarinForms"
             xmlns:custom="clr-namespace:MyNamespace.XamarinForms.Views.Custom;assembly=MyNamespace.XamarinForms"
             x:Class="MyNamespace.XamarinForms.Views.Pages.ShellView">
    <ContentPage.BindingContext>
        <viewModels:ShellViewModel></viewModels:ShellViewModel>
    </ContentPage.BindingContext>
    <Grid BackgroundColor="{x:Static resources:Colors.DefaultBackground}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid Grid.Row="0" BackgroundColor="{x:Static resources:Colors.CiRed}" Padding="14" IsVisible="{Binding ActiveViewModel.IsHeaderVisible}">
            <Label TextColor="{x:Static resources:Colors.BrightForeground}" FontSize="20" Text="{Binding ActiveViewModel.HeaderText}"/>
        </Grid>
        <Grid Grid.Row="1" BackgroundColor="{x:Static resources:Colors.DefaultBackground}">
            <custom:ShellChildPresenter ViewModel="{Binding Path=ActiveViewModel}"></custom:ShellChildPresenter>
        </Grid>
        <Grid IsVisible="{Binding ActiveViewModel.IsBusy}" Grid.Row="0" Grid.RowSpan="2" BackgroundColor="{x:Static resources:Colors.DarkBackground}" Opacity="0.5">
            <ActivityIndicator IsRunning="{Binding ActiveViewModel.IsBusy}" VerticalOptions="Center" HorizontalOptions="Center"></ActivityIndicator>
        </Grid>
    </Grid>
</ContentPage>

我的自定义控件:

public class ShellChildPresenter : ContentView
{
    public ShellChildPresenter()
    {
    }

    private static readonly BindableProperty ViewModelProperty = BindableProperty.Create<ShellChildPresenter, ShellViewChildModel>(
        presenter => presenter.ViewModel, 
        defaultValue: null,
        defaultBindingMode: BindingMode.OneWay,
        propertyChanged: ViewModelPropertyChanged);

    private static void ViewModelPropertyChanged(BindableObject bindable, ShellViewChildModel oldValue, ShellViewChildModel newValue)
    {
        if (newValue != null)
        {
            var current = bindable as ShellChildPresenter;
            if (current == null)
                return;

            var view = newValue.CreateContentView();
            view.BindingContext = newValue;
            current.Content = view;
        }
    }

    public ShellViewChildModel ViewModel
    {
        get { return (ShellViewChildModel)GetValue(ViewModelProperty); }
        set { SetValue(ViewModelProperty, value); }
    }
}

起初,这一直抛出异常,宣布 "ViewModel" 属性 的类型不匹配。为了尝试解决此问题,我将 属性 类型更改为 "object",此时 ShellViewModel.ViewModel 中的 setter 收到了 Xamarin.Forms.Binding.[ 的实例。 =14=]

谁能发现我做错了什么,这就解释了为什么我得到的是 Binding 实例而不是 ShellViewChildModel?奇怪的是,对于我 xaml 中的其他控件,我的内容显示得很好。

欢迎任何想法。我开始 运行 没有想法,到目前为止,文档没有指出我的代码有任何明显的缺陷:/

我讨厌魔法。似乎 "easier" 编程变得越多 "MAGIC" 事情就发生了。不止一次,它最终把我搞砸了。抱歉,在回答之前的简短评论。

改变

 private static readonly BindableProperty ViewModelProperty

 public static readonly BindableProperty ViewModelProperty

为什么?

的赋值

ShellChildPresenter ViewModel="{Binding Path=ActiveViewModel}"

由 Xaml 解析器解释。首先,它创建一个绑定元素,如“{Binding Path=ActiveViewModel}”所述。其次,它将单词 属性 附加到末尾并扫描绑定上下文中的对象以查找 .....属性 以神奇地将其连接到。

因为您的 .......属性 是私有的,解析器看不到它。所以它继续前进并按照你告诉它的去做 ViewModel=Binding 而不是你想要的 ViewModel属性=Binding.

就像我说的,我讨厌魔法。