You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin Forms多层自定义View内列表的MVVM绑定方案咨询

Solution for MVVM Binding with Nested ContentViews in Xamarin.Forms

Alright, let's break this down step by step using the MVVM pattern (I'm assuming we're working with Xamarin.Forms given your mention of ContentPage and ContentView). Here's how you can implement both required bindings cleanly:

1. Define Your Model Classes

First, create your HeadQuarter and Person models. Implement INotifyPropertyChanged so the UI updates automatically if data changes later:

using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Collections.Generic;

public class HeadQuarter : INotifyPropertyChanged
{
    private string _name;
    public string Name
    {
        get => _name;
        set => SetProperty(ref _name, value);
    }

    // Add other properties like Address, etc. as needed
    public event PropertyChangedEventHandler PropertyChanged;

    protected void SetProperty<T>(ref T backingStore, T value,
        [CallerMemberName] string propertyName = "")
    {
        if (EqualityComparer<T>.Default.Equals(backingStore, value)) return;
        backingStore = value;
        OnPropertyChanged(propertyName);
    }

    protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class Person : INotifyPropertyChanged
{
    private string _fullName;
    public string FullName
    {
        get => _fullName;
        set => SetProperty(ref _fullName, value);
    }

    // Add other properties like Email, Department, etc. as needed
    public event PropertyChangedEventHandler PropertyChanged;

    protected void SetProperty<T>(ref T backingStore, T value,
        [CallerMemberName] string propertyName = "")
    {
        if (EqualityComparer<T>.Default.Equals(backingStore, value)) return;
        backingStore = value;
        OnPropertyChanged(propertyName);
    }

    protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

2. Create Your ViewModel

Build a ViewModel that holds your collections. Use ObservableCollection so the UI reacts to item additions/removals. For the Label (a single control), add a computed property to convert the HeadQuarter list into a display-friendly string:

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Linq;

public class MainViewModel : INotifyPropertyChanged
{
    // Collection for xyzContentView's ListView
    private ObservableCollection<Person> _personList;
    public ObservableCollection<Person> PersonList
    {
        get => _personList;
        set => SetProperty(ref _personList, value);
    }

    // Collection for ContentPage's Label
    private ObservableCollection<HeadQuarter> _headQuarterList;
    public ObservableCollection<HeadQuarter> HeadQuarterList
    {
        get => _headQuarterList;
        set
        {
            SetProperty(ref _headQuarterList, value);
            // Trigger update for the computed display text
            OnPropertyChanged(nameof(HeadQuarterDisplayText));
        }
    }

    // Computed property to format HeadQuarter list for Label
    public string HeadQuarterDisplayText => 
        _headQuarterList?.Any() == true 
            ? string.Join(", ", _headQuarterList.Select(hq => hq.Name)) 
            : "No headquarters available";

    // Initialize sample data in constructor
    public MainViewModel()
    {
        PersonList = new ObservableCollection<Person>
        {
            new Person { FullName = "John Doe" },
            new Person { FullName = "Jane Smith" },
            new Person { FullName = "Bob Williams" }
        };

        HeadQuarterList = new ObservableCollection<HeadQuarter>
        {
            new HeadQuarter { Name = "New York HQ" },
            new HeadQuarter { Name = "London HQ" },
            new HeadQuarter { Name = "Tokyo HQ" }
        };
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void SetProperty<T>(ref T backingStore, T value,
        [CallerMemberName] string propertyName = "")
    {
        if (EqualityComparer<T>.Default.Equals(backingStore, value)) return;
        backingStore = value;
        OnPropertyChanged(propertyName);
    }

    protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

3. Implement xyzContentView (With ListView)

Create your xyzContentView and define a Bindable Property for the person list. This lets you pass data from the parent abcContentView down to the internal ListView:

xyzContentView.xaml

<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="YourAppNamespace.xyzContentView">
    <ListView x:Name="PersonListView"
              Margin="0 10 0 0"
              HasUnevenRows="True">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <StackLayout Padding="10">
                        <Label Text="{Binding FullName}" FontSize="Medium" />
                        <!-- Add other Person properties here if needed -->
                    </StackLayout>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ContentView>

xyzContentView.xaml.cs

using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using System.Collections.ObjectModel;

[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class xyzContentView : ContentView
{
    // Bindable Property to receive Person list from parent
    public static readonly BindableProperty PersonItemsProperty =
        BindableProperty.Create(
            nameof(PersonItems),
            typeof(ObservableCollection<Person>),
            typeof(xyzContentView),
            null,
            propertyChanged: (bindable, oldValue, newValue) =>
            {
                var view = (xyzContentView)bindable;
                view.PersonListView.ItemsSource = newValue;
            });

    public ObservableCollection<Person> PersonItems
    {
        get => (ObservableCollection<Person>)GetValue(PersonItemsProperty);
        set => SetValue(PersonItemsProperty, value);
    }

    public xyzContentView()
    {
        InitializeComponent();
    }
}

4. Implement abcContentView (Nested ContentView)

Create abcContentView that nests xyzContentView. We'll bind the PersonItems property of xyzContentView directly to the ViewModel's PersonList:

abcContentView.xaml

<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:YourAppNamespace"
             x:Class="YourAppNamespace.abcContentView">
    <StackLayout>
        <!-- Add any other UI elements for abcContentView here -->
        <Label Text="Person List:" FontSize="Large" FontAttributes="Bold" />
        <local:xyzContentView PersonItems="{Binding PersonList}" />
    </StackLayout>
</ContentView>

abcContentView.xaml.cs

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

[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class abcContentView : ContentView
{
    public abcContentView()
    {
        InitializeComponent();
        // BindingContext inherits from parent ContentPage, no need to set here
    }
}

5. Setup the ContentPage

Finally, wire up your ContentPage to use the ViewModel, bind the Label to the computed HeadQuarterDisplayText property, and add abcContentView:

MainPage.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:local="clr-namespace:YourAppNamespace"
             x:Class="YourAppNamespace.MainPage"
             Title="MVVM Binding Demo">
    <StackLayout Padding="20">
        <!-- Label bound to HeadQuarter list (via computed property) -->
        <Label Text="Headquarters:" FontSize="Large" FontAttributes="Bold" />
        <Label Text="{Binding HeadQuarterDisplayText}" 
               FontSize="Medium" 
               Margin="0 5 0 20" />

        <!-- Nested custom ContentView -->
        <local:abcContentView />
    </StackLayout>
</ContentPage>

MainPage.xaml.cs

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

[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
        // Set BindingContext to our ViewModel
        BindingContext = new MainViewModel();
    }
}

Alternative: Use a Value Converter (Instead of Computed Property)

If you prefer to keep formatting logic out of the ViewModel, create an IValueConverter to convert the HeadQuarter collection to a string:

using System;
using System.Collections.ObjectModel;
using System.Globalization;
using Xamarin.Forms;
using System.Linq;

public class CollectionToStringConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is ObservableCollection<HeadQuarter> hqList)
        {
            return hqList.Any() 
                ? string.Join(", ", hqList.Select(hq => hq.Name)) 
                : "No headquarters available";
        }
        return string.Empty;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Then use it in your ContentPage XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:YourAppNamespace"
             x:Class="YourAppNamespace.MainPage"
             Title="MVVM Binding Demo">
    <ContentPage.Resources>
        <local:CollectionToStringConverter x:Key="CollectionToString" />
    </ContentPage.Resources>
    <StackLayout Padding="20">
        <Label Text="Headquarters:" FontSize="Large" FontAttributes="Bold" />
        <Label Text="{Binding HeadQuarterList, Converter={StaticResource CollectionToString}}" 
               FontSize="Medium" 
               Margin="0 5 0 20" />
        <local:abcContentView />
    </StackLayout>
</ContentPage>

内容的提问来源于stack exchange,提问作者Arun Gupta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:06:59