首页 > 解决方案 > 在 XMAL/C# 中的页面之间传递“间接”对象

问题描述

我已经定义了一个 Player 对象,如下所示:

public class Player
    {
        [PrimaryKey, AutoIncrement]
        public int ID { get; set; }
        public string PlayerName { get; set; }
        public DateTime JoinDate { get; set; }
        public int TotalMatchesWon { get; set; }
        public int TotalMatchesLost { get; set; }
        public float WinPercentage { get; set; }
        public int Rank { get; set; }
    }

我将玩家存储在 SQLite 数据库中。我正在为 Xamarin 中的 android 应用程序实现以下页面:

名册 -> 名册详情 -> 编辑球员

这些页面具有以下功能:

名册: 1. Roster.xmal 包含一个球员列表视图(名册),由 Roster.xmal.cs 中的以下函数填充。

protected override async void OnAppearing()
        {
            base.OnAppearing();

            listView.ItemsSource = await App.Database.GetPlayersAsync();
        }
  1. 将新玩家添加到名册。这将使用 Roster.xmal.cs 文件中的以下代码调用带有空字段的 Edit Player 页面。
async void OnPlayerAddedClicked(object send, EventArgs e)
        {
            await Navigation.PushAsync(new RosterEntryPage
            {
                BindingContext = new Player()
            });
        }
  1. 您可以从列表中选择一个播放器。这需要 SelectedItem 并将 Player 发送到 PlayerDetailPage(也在 Roster.xmal.cs 中)
async void OnListViewItemSelected(object sender, SelectedItemChangedEventArgs e)
        {
            if (e.SelectedItem != null)
            {
                await Navigation.PushAsync(new PlayerDetailPage
                {
                    BindingContext = e.SelectedItem as Player
                });
            }
        }

名单详细信息: 1. 显示从 OnListViewItemSelected() 函数传递给它的 Player 对象。页面上没有指定它具有 Player 的代码,但我可以像这样引用 RosterDetails.xmal 文件中的 Player 字段(这就是我称之为“间接”对象的原因):

<Label Grid.Column="2" 
                               Text="{Binding PlayerName}"
                               FontSize="18"
                               FontAttributes="Bold" />
  1. 用户可以单击按钮来编辑播放器。这会再次调用 RosterEntryPage(就像添加一个新玩家一样),但这一次它传递了它已经拥有的玩家。这是坏掉的部分。如果我直接从 Roster.xmal 调用编辑页面,我可以预先填充所有空白字段(但那只是 PlayerName)并更新 SQLite 记录。我想将 RosterDetail.xmal 使用的间接对象传递给编辑页面来做同样的事情。

这是 RosterDetail.xmal.cs:

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

        async void OnEditPlayerButtonClicked(object send, EventArgs e)
        {
            await floatingButtonEdit.ScaleTo(1.5, 500);
            await floatingButtonEdit.ScaleTo(0, 500, Easing.SpringOut);

            await Navigation.PushAsync(new RosterEntryPage
            {
                BindingContext = new Player()
            });
        }

    }

我知道问题在于 BindingContext = new Player(),但我不知道用什么替换它。我尝试了各种方法,例如 BindingContext = e.Player ,但这给出了 e.Player 无效的错误。我还尝试更改函数参数以匹配 OnListViewItemSelected() 以便它是 OnEditPlayerButtonClicked(object sender, SelectedItemChangedEventArgs e) 但这会导致应用程序崩溃。

编辑 这是您进行数据输入以添加新播放器或编辑现有播放器的页面的 xmal。

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Foosball.RosterEntryPage"
             Title="Roster Entry">
    <StackLayout Margin="20">

        <Editor Placeholder="Enter player name"
                Text="{Binding PlayerName}"
                HeightRequest="50" />

        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Button Text="Save"
                            Clicked="OnRosterEntrySaveButtonClicked"
                            Grid.Row="1" />
            <Button Text="Delete"
                            Clicked="OnRosterEntryDeleteButtonClicked"
                            Grid.Row="1" 
                            Grid.Column="1"/>
        </Grid>
    </StackLayout>
</ContentPage>

这是关联的 .cs 文件

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

        public RosterEntryPage(Player newPlayer)
        {
            InitializeComponent();
        }

        async void OnRosterEntrySaveButtonClicked(object sender, EventArgs e)
        {
            var player = (Player)BindingContext;

            player.JoinDate = DateTime.UtcNow;
            await App.Database.SavePlayerAsync(player);

            await Navigation.PopAsync();
        }

        async void OnRosterEntryDeleteButtonClicked(object sender, EventArgs e)
        {
            var player = (Player)BindingContext;

            await App.Database.DeletePlayerAsync(player);

            await Navigation.PopAsync();
        }
    }

标签: c#androidxamarin

解决方案


BindingContext而不是在导航到页面之前设置页面,而是通过构造函数传递数据并让页面设置它自己的BindingContext

    Player ViewModel { get; set; }

    public PlayerDetailPage(Player player)
    {
        InitializeComponent();

        this.BindingContext = ViewModel = player;
    }

然后

    async void OnEditPlayerButtonClicked(object send, EventArgs e)
    {
        await floatingButtonEdit.ScaleTo(1.5, 500);
        await floatingButtonEdit.ScaleTo(0, 500, Easing.SpringOut);

        await Navigation.PushAsync(new RosterEntryPage(ViewModel));
    }

推荐阅读