将相对源根父控件的属性绑定到子类附加属性会导致应用程序崩溃。

3
我正在尝试将 Entry 控件的附加属性绑定到其根父级的属性上,该父级是一个 ContentView: (主要是为了在 NumBehaviors 类中使用/访问调用视图的 ViewModel(BindingContext)) 应用程序在调试过程中没有明显的问题,但在启动之前崩溃:

由于对象的当前状态,操作无效。

在 Xamarin.Forms.Binding.ApplyRelativeSourceBinding (Xamarin.Forms.BindableObject targetObject, Xamarin.Forms.BindableProperty targetProperty) [0x00041] in D:\a\1\s\Xamarin.Forms.Core\ Binding.cs:153

[ERROR] 致命未处理异常: System.InvalidOperationException: 由于对象的当前状态,操作无效。

1- 我是否做错了什么(可能是使用了BindingContext)?还是Xamarin在处理绑定方面还没有像WPF一样好(例如缺少ElementName)?

2- 为什么调试错误/异常不是那么明显?

MainView.xaml:

<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:Helpers="clr-namespace:App.Helpers"
             xmlns:Behaviors="clr-namespace:App.Behaviors"
             HorizontalOptions="FillAndExpand"
             VerticalOptions="Center"
             x:Class="App.Views.MainView">

<Entry>
     <Entry.Behaviors>
          <Behaviors:NumBehaviors Helpers:HelperClass.Vm=
           "{Binding Source={RelativeSource AncestorType={x:Type ContentView}}, Path=VM}"/>
     </Entry.Behaviors>
</Entry>

HelperClass.cs

public static BindableProperty VmProperty = 
       BindableProperty.Create("Vm", typeof(object), typeof(HelperClass), null);

public static object GetVm(BindableObject bindable)
{
    return (object)bindable.GetValue(VmProperty);
}

public static void SetVm(BindableObject bindable, object value)
{
    bindable.SetValue(VmProperty, value);
}

MainView.cs

public object VM {get; set;}

public MainView()
{
InitializeComponent();
VM  = (object) new MyViewModel();
BindingContext = VM;
}

NumBehaviors.cs

...
void OnEntryTextChanged(object sender, TextChangedEventArgs args) {
     var usedvm = HelperClass.GetVm((BindableObject)sender);
}

我可能可以使用以下内容,但我认为这是一个坏主意,当EntryBindingContext与其根父级不同时,它是无用的,而且看起来不够灵活:

...
void OnEntryTextChanged(object sender, TextChangedEventArgs args) {
     var usedvm = ((Entry)sender).BindingContext;
}

PS: 也许我的方法不太好,我愿意尝试更好的方法来实现它。

编辑

使用Xamarin.Forms 4.8.0.1364

还尝试过使用AncestorType={x:Type local:MainView}}代替AncestorType={x:Type ContentView}},但结果相同。

也许与Xamarin.Forms的这个已开放问题有关[Bug] Compiled bindings not working when using AncestorType #9839


我们可以在您提供的Github链接上查看详细信息。 - Wendy Zang - MSFT
1
相关链接: https://forums.xamarin.com/discussion/183883/behavior-crashes-operation-is-not-valid-due-to-the-current-state-of-the-object - Clay Brooks
@ClayBrooks 它正在工作,你能把它放在答案中让我接受吗?此外,我认为我犯了一个错误 Helpers:HelperClass.Vm.. 应该是 <Entry.. 的附加属性,而不是 Entry.Behaviors:NumBehaviors,否则 HelperClass.GetVm((BindableObject)sender); 将始终返回 null,除非我们以某种方式提供 Sender.Behaviors:NumBehaviors 作为参数,而不是 sender (=Entry)。 - Cfun
2个回答

2
RelativeSource可以找到其祖先的绑定上下文,但是行为不在当前可视树中。
我建议使用此方法来指示绑定上下文:
Command="{Binding BindingContext.YourCommand, Source={x:Reference PageName}}"

YourCommand必须从BindingContext可访问,而且包围ContentPage的属性中必须设置PageName,例如<ContentPage ... x:Name="PageName"

更多细节可以在这里找到。感谢那个链接的人提供了最初的解决方案。


请问您能否将您的解决方案调整为一个可行的答案,以适应问题示例上下文,这样用户就不会感到困惑了吗?最重要的是,在这种情况下,GetVm() 的参数对象将是什么? - Cfun

2
另一种避免这个问题的解决方案是通过将“Attached”属性从<Entry.Behaviors:NumBehaviors>移动到<Entry>来改变设计:
<Entry Helpers:HelperClass.Vm="{Binding Source={RelativeSource AncestorType={x:Type ContentView}},
                               Path=VM}">
     <Entry.Behaviors>
          <Behaviors:NumBehaviors/>
     </Entry.Behaviors>
</Entry>

这个操作还可以修复我在问题代码中的错误。我将属性 Vm 附加到对象 <Entry.Behaviors:NumBehaviors> 上,而我实际上是想从对象 Sender(=Entry) 中获取它(var usedvm = HelperClass.GetVm((BindableObject)sender);),但这总是返回 null。请注意,保留html标签。

我很高兴得知您已经自行解决了问题并与我们分享了解决方案,如果您将其接受为答案,对于其他遇到类似问题的社区将会非常有益。 - Zhanglong Wu - MSFT

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接