c# - 何时使用 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 文件中完成所有操作,但我发现很难让布局正确。
解决方案
由于您使用过 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());
推荐阅读
- machine-learning - k折交叉验证的最佳折数
- python - 使用列的 POS(在 pandas 中)
- arrays - 在 C#.net 中使用数组内容检查输入值
- go - Go 中的堆排序实现
- bash - 在 bash 脚本中运行 gnuplot 的“无效表达式”
- xml - 如何使用Powershell修改xml中不包含任何内容的空值/两个双引号?
- pandas - 剥离熊猫数据框中的左括号和斜杠字符串
- arrays - 如何从 JsSuccess 解开数组对象
- sql - 有效地从 postgresql 中的垂直列表进行 Sql 查询,如数据透视表
- swift - Xcode不断在视频和图像文件上抛出错误“没有这样的文件或目录”