自定义控件的 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.
就像我说的,我讨厌魔法。
我遇到了 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.
就像我说的,我讨厌魔法。