如何在 Xamarin Forms 中数据绑定指定的位置放置文本

How to place text in view at locations specified by databinding in Xamarin Forms

我正在寻找的功能是可点击的标签,其中文本和位置都是数据绑定的,并且位置与同时显示的图像相关。

我试过AbsoluteLayoutRelativeLayout,但标签似乎无法逃脱CollectionView的限制;即标签根据标签的大小不断向下或跨页面迭代。我不在乎标签是否相互重叠。事实上,如果标签的字体足够大并且数据绑定位置足够近,那么重叠标签是肯定的,点击事件在逻辑上可能发生在该像素位置顶部的任何标签上。我觉得没问题。

例如,我想要显示一张尺寸为 640x480 像素的图像。我想要放置在标签上的标签文本和位置需要进行数据绑定。这是示例 MockData.

        public MockDataStore()
        {
            items = new List<Item>()
            {
                new Item { Id = Guid.NewGuid().ToString(), Text = "A"  , Description="My A"  , X= 85, Y=130 },
                new Item { Id = Guid.NewGuid().ToString(), Text = "B"  , Description="My B"  , X=140, Y=150 },
                new Item { Id = Guid.NewGuid().ToString(), Text = "C"  , Description="My C"  , X=190, Y=165 },
                new Item { Id = Guid.NewGuid().ToString(), Text = "D"  , Description="My D"  , X=240, Y=175 },
                new Item { Id = Guid.NewGuid().ToString(), Text = "E"  , Description="My E"  , X=290, Y=180 },
                new Item { Id = Guid.NewGuid().ToString(), Text = "F"  , Description="My F"  , X=335, Y=185 }
            };
        }

这是我正在尝试的 XAML。

    <Grid x:Name="grid" 
          x:DataType="local:ItemsViewModel">
        <!--<Image Source="bg.png" Aspect="AspectFit" HorizontalOptions="Start" VerticalOptions="Start" x:Name="bg"/>-->
        <RefreshView x:DataType="local:ItemsViewModel" 
                        Command="{Binding LoadItemsCommand}" 
                        IsRefreshing="{Binding IsBusy, Mode=TwoWay}">
        <CollectionView x:Name="ItemsListView"
                        ItemsSource="{Binding Items}"
                        SelectionMode="None">
            <CollectionView.ItemTemplate>
                    <DataTemplate>
                        <RelativeLayout Padding="10" x:DataType="model:Item">
                            <Label Text="{Binding Text}" 
                                    RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=X, Constant={Binding X}}"
                                    RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Y, Constant={Binding Y}}"
                                    BackgroundColor="Transparent" 
                                    FontSize="16" />
                            <RelativeLayout.GestureRecognizers>
                                <TapGestureRecognizer 
                                    NumberOfTapsRequired="1"
                                    Command="{Binding Source={RelativeSource AncestorType={x:Type local:ItemsViewModel}}, Path=ItemTapped}"     
                                    CommandParameter="{Binding .}">
                                </TapGestureRecognizer>
                            </RelativeLayout.GestureRecognizers>
                        </RelativeLayout>
                    </DataTemplate>
                </CollectionView.ItemTemplate>
            </CollectionView>
        </RefreshView>
    </Grid>

发生的事情是标签的相对位置被放置在页面下方递增的位置。只要我还能得到 ItemTapped.

,我就不会使用 CollectionView

我也试过使用 BindableLayout 但没有成功,虽然我不确定我是否理解如何正确使用 class,而且数据源都是文本和位置。

我想要完成的类似于 GPS 应用程序如何显示地图并覆盖从数据加载并相对于地图内坐标的可点击兴趣点。

我想用 Xamarin 的 XAML 实现什么?

编辑:我发现 RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0, Constant=0}" TranslationX="{Binding X}" 会将标签放置在正确的 X 位置。但是,这个想法不适用于 Y 位置。 CollectionView 不断增加页面下方的 Y 位置。我希望 CollectionView 可以设置为不对控件进行任何定位,只允许我通过绑定设置标签的 x/y 位置。

will place the labels at the correct X location. However, this idea doesn't work with the Y location.

问题是您在 each CollectionView 项中显示标签,但它们具有不同的 RelativeLayout 父项。并且它有足够的宽度来设置 Y 值,但没有足够的宽度来设置 X 值。

对于这种情况,我们建议您使用 BindableLayout

撰写项目

例如

<ContentPage.BindingContext>
   <local:ItemsViewModel />
</ContentPage.BindingContext>

<RelativeLayout BindableLayout.ItemsSource="{Binding items}">
    <BindableLayout.ItemTemplate>
        <DataTemplate x:DataType="local:Item">
            <Label
                BackgroundColor="Transparent"
                FontSize="16"
                RelativeLayout.XConstraint="{Binding X}"
                RelativeLayout.YConstraint="{Binding Y}"
                Text="{Binding Text}" />
        </DataTemplate>
    </BindableLayout.ItemTemplate>
</RelativeLayout>

代码隐藏

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
    }

}
public class ItemsViewModel
{
    public ItemsViewModel()
    {
        MockDataStore();
    }
    public List<Item> items { get; set; }
    public void MockDataStore()
    {
        items = new List<Item>()
        {
            new Item { Id = Guid.NewGuid().ToString(), Text = "A"  , Description="My A"  ,X = Constraint.Constant(85),Y= Constraint.Constant(130)},
            new Item { Id = Guid.NewGuid().ToString(), Text = "B"  , Description="My B"  ,X = Constraint.Constant(140),Y= Constraint.Constant(150)},
            new Item { Id = Guid.NewGuid().ToString(), Text = "C"  , Description="My C"  ,X = Constraint.Constant(190),Y= Constraint.Constant(165)},
            new Item { Id = Guid.NewGuid().ToString(), Text = "D"  , Description="My D"  ,X = Constraint.Constant(240),Y= Constraint.Constant(175)},
            new Item { Id = Guid.NewGuid().ToString(), Text = "E"  , Description="My E"  ,X = Constraint.Constant(290),Y= Constraint.Constant(180)},
            new Item { Id = Guid.NewGuid().ToString(), Text = "F"  , Description="My F"  ,X = Constraint.Constant(335),Y= Constraint.Constant(185)}
        };
    }
}
public class Item
{
    public string Id { get; internal set; }
    public string Text { get; internal set; }
    public string Description { get; internal set; }
    public Constraint X { get; set; }
    public Constraint Y { get; set; }
}