首页 > 解决方案 > 何时使用 Xamarin 绑定而不是将对象传递给页面构造函数?

问题描述

所以我有一个问题,我想以我认为非常标准的方式在我的应用程序周围传递数据,但是我找不到任何与我想要做的类似的示例或示例。

例如,我有一个类型为 person 的应用程序。

public class Person
    {
        public string Name;
        public int Age;

        public Person(string name, int age)
        {
            Name = name;
            Age = age;
        }
    }

我想创建一个人卡列表,每个卡都显示人的信息。然后,当卡片被点击时,它会将您带到一个包含该人信息的新页面。我在下面的代码中实现了这个功能,但是我尝试的 xamarin 原理不起作用,所以它更多地依赖于我的 c# 技能,我觉得这不是编写 xamarin 代码的标准方式。

我已将工作项目上传到GitHub 存储库

所以我的问题是,我的方法有哪些缺点,以及如何使用更多标准工具(如绑定)来实现相同的功能。

PersonCard.xaml:

<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="CustomViewListTest.Views.PersonCard"
             xmlns:System="clr-namespace:System;assembly=mscorlib">

    <ContentView.ControlTemplate>
        <ControlTemplate>
            <Frame BorderColor="#3F3F3F" Margin="2">
                <Frame.GestureRecognizers>
                    <TapGestureRecognizer Tapped="OnTapped"/>
                </Frame.GestureRecognizers>
                <StackLayout>
                    <Label Text="{TemplateBinding Name}"></Label>
                    <Label Text="{TemplateBinding Age}"></Label>
                </StackLayout>
            </Frame>
        </ControlTemplate>
    </ContentView.ControlTemplate>
</ContentView>

个人卡片.cs

[XamlCompilation(XamlCompilationOptions.Compile)] 公共部分类 PersonCard : ContentView {

    public PersonCard(Person person) 
    {
        Person = person;
        InitializeComponent();
    }

    public PersonCard()
    {
        InitializeComponent();
    }

    public static readonly BindableProperty PersonProperty =
       BindableProperty.Create(nameof(Person), typeof(Person), typeof(PersonCard), null);

    public static readonly BindableProperty NameProperty = 
        BindableProperty.Create(nameof(Name), typeof(string), typeof(PersonCard), string.Empty);
    public static readonly BindableProperty AgeProperty =
        BindableProperty.Create(nameof(Age), typeof(string), typeof(PersonCard), string.Empty);


    public Person Person
    {
        get => (Person)GetValue(PersonProperty);
        set => SetValue(PersonProperty, value);
    }

    public string Name
    {
        get { return Person.Name; }
    }

    public string Age
    {
        get { return Person.Age.ToString(); }
    }

    async void OnTapped(object sender, EventArgs args)
    {
        Page page = new NavigationPage(new PersonFullView(Person));
        await Navigation.PushAsync(new NavigationPage(page));
        
    }
}

PersonFullView.xaml

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="CustomViewListTest.Views.PersonFullView"
             xmlns:views ="clr-namespace:CustomViewListTest.Views">
    <StackLayout>
        <Label Text="This page shows a person's details"></Label>
        <Label x:Name="PersonName"></Label>
        <Label x:Name="PersonAge"></Label>
        <Label Text="End"></Label>


    </StackLayout>
</ContentPage>

PersonFullView.cs

[XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class PersonFullView : ContentPage
    {
        public PersonFullView()
        {
            InitializeComponent();
   
        }

        public Person Person;

        public PersonFullView(Person person)
        {
            Person = person;
            InitializeComponent();
            PersonName.Text = person.Name;
            PersonAge.Text = person.Age.ToString();
        }


       public static readonly BindableProperty NameProperty =
          BindableProperty.Create(nameof(Name), typeof(string), typeof(PersonFullView), "No name");
       public static readonly BindableProperty AgeProperty =
           BindableProperty.Create(nameof(Age), typeof(string), typeof(PersonFullView), "No age");


        public string Name
        {
            get { return Person.Name; }
        }

        public string Age
        {
            get { return Person.Age.ToString(); }
        }
    }

个人列表.xaml

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="CustomViewListTest.Views.PersonList"
             xmlns:views ="clr-namespace:CustomViewListTest.Views">
    <StackLayout x:Name="personStack">
        <Label Text="List of People"></Label>
    </StackLayout>

</ContentPage>

个人列表.cs

[XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class PersonList : ContentPage
    {
        public PersonList()
        {
            InitializeComponent();
        }

        public List<Person> People;

        public List<PersonCard> cards { get; set; }
        public PersonList(List<Person> people)
        {
            InitializeComponent();
            People = people;
            cards = new List<PersonCard>();

            foreach (var p in people)
            {
                personStack.Children.Add(new PersonCard(p));
            }

            BindingContext = this;
        }


    }

它的工作方式是在 personList 中定义 2 个 Person 对象,然后使用 PersonCard 构造函数,该构造函数接受一个 person 对象来创建卡片。我使用了 stackLayout,因为我无法使用我更喜欢的 listview 获得相同的功能。我认为问题源于列表视图无法保存内容视图,而是它保存对象列表,并且您给它一个可绑定的模板。我无法让它工作。

您可能还注意到我的可绑定属性适用于 PersonCard,但不适用于 PersonFullView。我尝试为后者使用基本相同的代码,但无济于事。我认为在启动时不存在的页面上的可绑定属性有些可疑。

所以,我很好奇如何使用 Bindings 来实现这一点,而不是为我的视图和页面创建非无参数构造器,我在任何示例中都没有真正看到。此外,我有兴趣将尽可能多的逻辑移至 xaml 文件。显然,您可以从后面的 cs 文件中完成所有操作,但我发现很难让布局正确。

标签: c#xamlxamarin

解决方案


由于您使用过 Data-Binding ,因此最好在 ViewModel 中处理逻辑(填充数据和导航)并使用Command而不是Click Event

另外,我有兴趣将尽可能多的逻辑移至 xaml 文件

我无法获得与我更喜欢的列表视图一起使用的相同功能。

因此,您可以修改和改进代码,如下所示。我使用ListView来显示数据。

在个人卡中

using CustomViewListTest.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace CustomViewListTest.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class PersonCard : ContentView
    {

     
        public PersonCard()
        {
            InitializeComponent();
        }

        public static readonly BindableProperty NameProperty = 
            BindableProperty.Create(nameof(Name), typeof(string), typeof(PersonCard), string.Empty);
        public static readonly BindableProperty AgeProperty =
            BindableProperty.Create(nameof(Age), typeof(string), typeof(PersonCard), string.Empty);


       
        public string Name
        {
            get => (string)GetValue(NameProperty);
            set => SetValue(NameProperty, value);
        }

        public string Age
        {
            get => (string)GetValue(AgeProperty);
            set => SetValue(AgeProperty, value);
        }


        public static readonly BindableProperty ClickCommandProperty =
            BindableProperty.Create(nameof(ClickCommand), typeof(ICommand), typeof(PersonCard));

        public ICommand ClickCommand
        {
            get => (ICommand)GetValue(ClickCommandProperty);
            set => SetValue(ClickCommandProperty, value);
        }

        public static BindableProperty ClickParameterProperty =
            BindableProperty.Create(nameof(ClickParameter), typeof(object), typeof(PersonCard));

        public object ClickParameter
        {
            get => (object)GetValue(ClickParameterProperty);
            set => SetValue(ClickParameterProperty, value);
        }

    }
}
<?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="CustomViewListTest.Views.PersonCard"
             xmlns:System="clr-namespace:System;assembly=mscorlib"
             x:Name="card"
             
             >

    <ContentView.Content>
        <Frame BorderColor="#3F3F3F" Padding="0" Margin="2">
            <Frame.GestureRecognizers>
                <TapGestureRecognizer Command="{Binding ClickCommand,Source={x:Reference card}}"  CommandParameter="{Binding ClickParameter,Source={x:Reference card}}"/>
            </Frame.GestureRecognizers>
            <StackLayout Orientation="Horizontal">
                <Label Text="{Binding Name,Source={x:Reference card}}" TextColor="Red"></Label>
                <Label Text="{Binding Age,Source={x:Reference card}}" TextColor="Green"></Label>
            </StackLayout>
        </Frame>
    </ContentView.Content>
</ContentView>

在人员列表中

using CustomViewListTest.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using System.Windows.Input;

namespace CustomViewListTest.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class PersonList : ContentPage
    {
        public PersonList()
        {
            InitializeComponent();

            BindingContext = new MyViewModel(this.Navigation);
        }
   

    }



    public class MyViewModel
    {
        public ICommand ClickCommand { get; private set; }

        INavigation CurrentNavigation;

        public ObservableCollection<Person> MyItems {get;set;}

        public MyViewModel( INavigation navigation)
        {

            CurrentNavigation = navigation;

            MyItems = new ObservableCollection<Person>() { new Person("Jack",23),new Person("Lucas",25) };

            ClickCommand = new Command(async (org) =>
            {
                var item = org as Person;
                PersonFullView page = new PersonFullView(item);
                await CurrentNavigation.PushAsync(page);

            });

        }

    }
}
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="CustomViewListTest.Views.PersonList"
             xmlns:views ="clr-namespace:CustomViewListTest.Views"
             x:Name="page"
             >
    <StackLayout>

        <ListView ItemsSource="{Binding MyItems}">
                <ListView.ItemTemplate>
                  <DataTemplate>
                    <ViewCell>
                        <views:PersonCard Name="{Binding Name}" Age="{Binding Age}" ClickCommand="{Binding Path=BindingContext.ClickCommand,Source={x:Reference page}}" ClickParameter="{Binding .}" />
                    </ViewCell>
                </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>


    </StackLayout>

</ContentPage>

应用内

MainPage = new NavigationPage(new PersonList());

在此处输入图像描述


推荐阅读