BindingExtension如何知道其有效的绑定上下文?

问题描述

就像Xamarin.Forms.Xaml.BindingExtension尽其所能来获得有效的绑定上下文以返回与相关源/路径/模式相对应的BindingBase对象一样,我想创建一个扩展来执行它的伏都教徒也基于有效的绑定上下文。通过IProvideValueTarget,我设法获得了BindingContext,但是没有获得有效的绑定上下文,因此,在DataTemplate中使用时,它总是带给我父辈的有效绑定上下文,而不是比DataTemplate。我正在尝试在数据模板中找到有效的绑定上下文。

为了更清楚地说明这一点,我在一个简单的PoC中重新创建了问题。

我们有以下模型:

namespace App1.Models
{
    public class MyBeautifulModel
    {
        public string SomeData { get; set; }
        public override string ToString()
        {
            return SomeData;
        }
    }
}

以下视图模型:

using App1.Models;

using System.Collections.Generic;

namespace App1.viewmodels
{
    public class MainPageviewmodel : viewmodelBase
    {
        public MainPageviewmodel()
        {
            MyBeautifulModels = new[]
            {
                new MyBeautifulModel() { SomeData = "DataTemplate Data0" },new MyBeautifulModel() { SomeData = "DataTemplate Data1" },new MyBeautifulModel() { SomeData = "DataTemplate Data2" },};

            MyProperty = "Outer Template Data";
        }

        public IEnumerable<MyBeautifulModel> MyBeautifulModels { get; }

        public string MyProperty { get; }

        public override string ToString()
        {
            return MyProperty;
        }
    }
}

以下标记扩展名:

using System;
using System.Linq;
using System.Reflection;
using System.Text;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using Xamarin.Forms.Xaml.Internals;

namespace App1.MarkupExtensions
{
    public class PrintContextExtension : IMarkupExtension
    {
        public object ProvideValue(IServiceProvider serviceProvider)
        {
            if (serviceProvider.GetService<IProvideValueTarget>() is SimpleValueTargetProvider provider)
            {
                object[] objectAndParents = (object[])typeof(SimpleValueTargetProvider).GetField("objectAndParents",BindingFlags.Instance | BindingFlags.NonPublic).GetValue(provider);

                StringBuilder sb = new StringBuilder();
                sb.AppendLine("Object and all its parents:");
                foreach (object obj in objectAndParents)
                    sb.AppendLine(obj.ToString());

                sb.AppendLine("First object with binding context:");
                BindableObject bindable = (BindableObject)objectAndParents.First(b => b is BindableObject bindableObject && bindableObject.BindingContext != null);
                sb.AppendLine(bindable.ToString());

                sb.AppendLine("The binding context of that object:");
                sb.AppendLine(bindable.BindingContext.ToString());
                string result = sb.ToString();
                Console.WriteLine(result);
                return bindable.BindingContext.ToString();
            }
            throw new Exception();
        }
    }
}

以及以下视图:

<?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:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:vm="clr-namespace:App1.viewmodels"
             xmlns:ext="clr-namespace:App1.MarkupExtensions"
             mc:Ignorable="d"
             x:Class="App1.Views.MainPage"
             Padding="20">
    <ContentPage.BindingContext>
        <vm:MainPageviewmodel/>
    </ContentPage.BindingContext>
    <ContentPage.Resources>
        <Style targettype="Label">
            <Setter Property="FontSize" Value="Title"/>
            <Setter Property="BackgroundColor" Value="LightGreen"/>
        </Style>
    </ContentPage.Resources>
    <ContentPage.Content>
        <StackLayout>
            <Label>
                <Label.FormattedText>
                    <FormattedString>
                        <Span Text="Built-in Binding: "/>
                        <Span Text="{Binding MyProperty}"/>
                    </FormattedString>
                </Label.FormattedText>
            </Label>
            <Label>
                <Label.FormattedText>
                    <FormattedString>
                        <Span Text="My Binding: "/>
                        <Span Text="{ext:PrintContext MyProperty}"/>
                    </FormattedString>
                </Label.FormattedText>
            </Label>
            <Frame BorderColor="DimGray"
                   CornerRadius="15">
                <ListView ItemsSource="{Binding MyBeautifulModels}"
                      HasUnevenRows="True">
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <ViewCell>
                                <StackLayout Margin="0,20">
                                    <Label>
                                        <Label.FormattedText>
                                            <FormattedString>
                                                <Span Text="Built-in Binding: "/>
                                                <Span Text="{Binding SomeData}"/>
                                            </FormattedString>
                                        </Label.FormattedText>
                                    </Label>
                                    <Label BackgroundColor="PaleVioletRed">
                                        <Label.FormattedText>
                                            <FormattedString>
                                                <Span Text="My Binding: "/>
                                                <Span Text="{ext:PrintContext SomeData}"/>
                                            </FormattedString>
                                        </Label.FormattedText>
                                    </Label>
                                </StackLayout>
                            </ViewCell>
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>
            </Frame>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

运行此命令将产生以下控制台输出

Object and all its parents:
Xamarin.Forms.Span
My Binding: 
Xamarin.Forms.Label
Xamarin.Forms.StackLayout
Xamarin.Forms.ViewCell
Xamarin.Forms.DataTemplate
Xamarin.Forms.ListView
Xamarin.Forms.Frame
Xamarin.Forms.StackLayout
App1.Views.MainPage
First object with binding context:
Xamarin.Forms.ListView
The binding context of that object:
Outer Template Data

最后,这是运行时视图的外观:

runtime screenshot

很抱歉,这个问题看起来太长了,但我希望我的重现步骤能使问题更易于简化和说明。

p看着BindingExtension's implementation on GitHub,看来IServiceProvider甚至没有被使用。这对我来说似乎很奇怪-因为它怎么知道它的上下文!

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...