自定义ContentView的DataBinding在Code behind中工作,但在Xaml中不工作

5gfr0r5j  于 2023-09-28  发布在  其他
关注(0)|答案(2)|浏览(96)

我正试图为一个类似于Bootstrap的Navigation Pills的控件创建一个自定义ContentView。
下面是我的自定义视图的代码。

PillToggle.xaml.cs

public partial class PillToggle : ContentView
{

    public PillToggle()
    {
        InitializeComponent();
        this.BindingContext = this;
        // BindableLayout.SetItemsSource(this.PillsContainer, this.Pills);
    }

    public ObservableCollection<Pill> Pills
    {
        get { return (ObservableCollection<Pill>)GetValue(PillsProperty); }
        set { SetValue(PillsProperty, value); }
    }

    public static readonly BindableProperty PillsProperty =
        BindableProperty.Create("Pills", typeof(ObservableCollection<Pill>), typeof(PillToggle), defaultValue: new ObservableCollection<Pill>());
}

PillToggle.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:REDACTED"
             x:Class="REDACTED.PillToggle">
    <Frame BackgroundColor="#eeeeee"  CornerRadius="24" Padding="4" MinimumHeightRequest="48">
        <HorizontalStackLayout x:Name="PillsContainer" BindableLayout.ItemsSource="{Binding Pills}"  HorizontalOptions="Start" >
            <BindableLayout.ItemTemplate>
                <DataTemplate x:DataType="controls:Pill">
                   ... snip
                </DataTemplate>
            </BindableLayout.ItemTemplate>
        </HorizontalStackLayout>
    </Frame>
</ContentView>

Pill.cs

public partial class Pill : ObservableObject
{
    [ObservableProperty]
    public string? label;

    [ObservableProperty]
    private bool selected = false;
}

当我尝试在ContentPage中使用此视图时,无法通过XAML绑定Pills集合。

MyPageViewModel.cs

public partial class MyViewModel : ObservableObject
{
    //Hard coded for now, but may become dynamic
    public IEnumerable<Pill> MyPills { get; } = new List<Pill>()
    {
        new Pill() { Label = "Test 1" },
        new Pill() { Label = "Test 2" }
    };
}

MyPage.xaml(用法)

<controls:PillToggle x:Name="MyToggle" Pills="{Binding MyPills}" >

MyPage.xaml.cs(构造函数)

public MyPage(MyViewModel viewModel)
{
    this.BindingContext = viewModel;
    InitializeComponent();
}

当我运行我的应用程序时,我没有看到任何药片。我可以看到Views的背景色,但HorizontalStackView似乎没有绑定到任何项目。
如果我从Xaml中删除Binding,并在构造函数后面的代码中手动绑定,它就可以工作。

MyToggle.Pills = new ObservableCollection<Controls.Pill>(viewModel.MyPills);

如果我像这样手动添加药片也会起作用。

<controls:PillToggle x:Name="MyToggle">
    <controls:PillToggle.Pills>
        <controls: Pill Label="Test 3" Selected="True" />
        <controls: Pill Label="Test 4" />
    </controls:PillToggle.Pills>
</controls:PillToggle>

如何让Binding在Xaml中为我的自定义ContentView工作?

g6ll5ycj

g6ll5ycj1#

首先,这是我的观点,你应该避免在你的控制中有一个绑定上下文。它会让你的生活变得简单得多。您拥有的bindingcontext从ContentPage(View)继承的视图模型。你也混合了很多东西,使它比它更难。所以让我们看看我们如何才能做到这一点。
首先,我们有ContentView逻辑,代码背后。你的BindableProperty有点错误。它需要有实际的propertyname作为它的第一个参数。同样是第二个参数,返回类型是错误的,很大程度上是因为你在所有这些中混合了ObservableCollection。它只需要一个列表。所以代码如下:

public partial class PillToggle : ContentView
{
    public static readonly BindableProperty PillsProperty =
        BindableProperty.Create(nameof(PillsProperty), typeof(List<Pill>), typeof(PillToggle));

    public List<Pill> Pills
    {
        get => (List<Pill>) GetValue(PillsProperty);
        set => SetValue(PillsProperty, value);
    }

    public PillToggle()
    {
        InitializeComponent();
    }

    private void OnPillTapped(object sender, TappedEventArgs e)
    {
        var label = sender as Label;
        var selectedPill = label?.BindingContext as Pill;

        if (selectedPill != null)
        {
            foreach (var child in PillsContainer.Children)
            {
                if (child is Label pillLabel)
                {
                    pillLabel.BackgroundColor = Colors.White;
                }
            }

            label.BackgroundColor = Colors.LightGray;

        }
    }
}

如果我们转到xaml页面,我刚刚更改了绑定并添加了一个TapGestureRecognizer,因为您谈到可以单击它。

<Frame
        Padding="4"
        BackgroundColor="#eeeeee"
        CornerRadius="24"
        MinimumHeightRequest="48">
    <HorizontalStackLayout
            x:Name="PillsContainer"
            BindableLayout.ItemsSource="{Binding Source={RelativeSource AncestorType={x:Type local:PillToggle}}, Path=Pills}"
            HorizontalOptions="Start">
        <BindableLayout.ItemTemplate>
            <DataTemplate x:DataType="models:Pill">
                <Label Text="{Binding Label}" >
                    <Label.GestureRecognizers>
                        <TapGestureRecognizer Tapped="OnPillTapped" NumberOfTapsRequired="1" />
                    </Label.GestureRecognizers>
                </Label>
            </DataTemplate>
        </BindableLayout.ItemTemplate>
    </HorizontalStackLayout>
</Frame>

我把你的模型也简化了。

public partial class Pill
{
    public string Label { get; set; }

    public bool Selected { get; set; } = false;
}

如果我们现在看一下实现,您将拥有带有控件的视图

<controls:PillToggle x:Name="MyToggle" Pills="{Binding MyPills}" />

它绑定到ViewModel中的MyPills

qkf9rpyu

qkf9rpyu2#

我修改了两件事你的代码就能在我这边工作了,

1,因为药丸typeof(ObservableCollection<Pill>。在MyViewModel中:

请更改

public IEnumerable<Pill> MyPills { get; } = new List<Pill>()

收件人:

public IEnumerable<Pill> MyPills { get; } = new ObservableCollection<Pill>()

2.在PillToggle.xaml.cs

变化

this.BindingContext = this;

Content.BindingContext = this;

希望能帮上忙!

相关问题