【问题标题】:Why won't changes reflect in my Xamarin.Forms project or SQLite database为什么更改不会反映在我的 Xamarin.Forms 项目或 SQLite 数据库中
【发布时间】:2021-07-15 00:05:00
【问题描述】:

更新 - 问题 #1 已解决,问题 #2 仍未解决 你可以在https://www.youtube.com/watch?v=5_6KJ0QJouM观看我的问题的一个非常粗略的演示视频

我正在使用 MVVM 设计模式和 C# 构建一个带有 SQLite 数据库的 Xamarin.Forms 应用程序>

当尝试将记录从 View 保存到数据库时,更新/保存似乎没有保存到 SQLite 数据库或反映在其他 Views 中。

我知道数据库Save 方法确实有效,因为我在应用程序首次加载(在App.xaml.cs 中)时使用DeveloperData.cs 文件创建了一些虚拟数据。

我有两个问题。

(已解决)问题 1 - 数据未保存到数据库 当我从使用MerchandiserEditPageViewModel.cs ViewModel 的MerchandiserEditPage.xaml 调用Save 命令时,似乎没有保存记录。

问题 2 - 反映在其他视图中的更改 将更新的数据保存到数据库后,如何在其他视图中反映该更改?在我保存来自MerchandiserEditPage 的记录后,View 从堆栈“弹出”并且用户返回到MerchandiserProfileView。我希望更新的数据反映在堆栈上的所有其他视图中。但这似乎没有发生? (我使用硬编码数据对此进行了测试,并且出现了同样的问题,因此问题与问题 1 没有直接关系)

我的项目中有很多文件,可以从我的GitHub repository 查看/下载,但我将在这个问题中专注于以下内容。

  • MerchandiserEditPage.xaml(查看)
  • MerchandiserProfilePage.xaml(查看)
  • MerchandiserDatabase.cs(数据库函数)x
  • MerchandiserEditPageViewModel.cs x

查看我的GitHub repository 了解完整项目。

MerchandiserDatabase.cs(数据库函数)

using SQLite;

namespace MobileApp.Database
{
    public class MerchandiserDatabase
    {
        private static SQLiteConnection database = DependencyService.Get<IDatabaseConnection>().DbConnection(); 
        private readonly static object collisionLock = new object();

        public MerchandiserDatabase()
        {
            database.CreateTable<Models.Merchandiser>();
        }


        public static void SaveMerchandiser(Models.Merchandiser merchandiser)
        {
            lock (collisionLock)
            {
                if (merchandiser.Id != 0)
                {
                    database.Update(merchandiser);
                }
                else
                {
                    database.Insert(merchandiser);
                }
            }
        }

    }
}

MerchandiserEditPageViewModel.cs (ViewModel) 已更新

using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;

namespace MobileApp.ViewModels
{
    public class MerchandiserEditPageViewModel : BaseViewModel
    { 
        public string PageTitle { get; } = "Edit Merchandiser Profile";

        public Command SaveCommand { get; set; }

        private string name;
        public string Name
        {
            get { return name; }
            set
            {
                name = value;
                OnPropertyChanged();
            }
        }

        private string phone;
        public string Phone
        {
            get { return phone; }
            set
            {
                phone = value;
                OnPropertyChanged();
            }
        }

        private string email;
        public string Email
        {
            get { return email; }
            set
            {
                email = value;
                OnPropertyChanged();
            }
        }

        public MerchandiserEditPageViewModel(Models.Merchandiser selectedMerchandiser)
        {
            Name = selectedMerchandiser.Name;
            Phone = selectedMerchandiser.Phone;
            Email = selectedMerchandiser.Email;

            SaveCommand = new Command( async ()=> {

                selectedMerchandiser.Name = this.Name;
                selectedMerchandiser.Phone = this.Phone;
                selectedMerchandiser.Email = this.Email;

                Database.MerchandiserDatabase.SaveMerchandiser(selectedMerchandiser);
                await Application.Current.MainPage.Navigation.PopModalAsync();
            });
        }
    }
}

MerchandiserEditPage.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"
             x:Class="MobileApp.Views.MerchandiserEditPage">
    <ContentPage.Content>
        <StackLayout>

            <!--Page Heading-->
            <StackLayout Spacing="0">
                <Label Text="{Binding PageTitle}"
                       Style="{StaticResource PageTitle}"/>
                <BoxView HeightRequest="1" Color="LightGray" />
            </StackLayout>

            <!-- Merchandiser Profile -->
            <StackLayout Margin="10">

                <Label Text="Name"/>
                <Entry Text="{Binding Name}"/>

                <Label Text="Phone"/>
                <Entry Text="{Binding Phone}"/>

                <Label Text="Email"/>
                <Entry Text="{Binding Email}"/>

                <StackLayout Orientation="Horizontal"
                             HorizontalOptions="Center">
                    <Button Text="Cancel"
                            Clicked="CancelButton_Clicked"/>
                    <Button Text="Save"
                            Command="{Binding SaveCommand}"/>
                </StackLayout>
                
            </StackLayout>

        </StackLayout>
    </ContentPage.Content>
</ContentPage>

MerchandiserEditPage.xaml.cs(查看 - 代码隐藏)

public partial class MerchandiserEditPage : ContentPage
    {
        Models.Merchandiser SelectedMerchandiser { get; set; }

        public MerchandiserEditPage (Models.Merchandiser selectedMerchandiser)
        {
            InitializeComponent ();
            SelectedMerchandiser = selectedMerchandiser;
            this.BindingContext = new ViewModels.MerchandiserEditPageViewModel(selectedMerchandiser);
        }

        async private void CancelButton_Clicked(object sender, EventArgs e)
        {
            await Navigation.PopModalAsync();
        }

    }

MerchandiserProfilePage.xaml(查看 - 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"
             x:Class="MobileApp.Views.MerchandiserProfilePage"
             NavigationPage.HasNavigationBar="False">
    <ContentPage.Content>
        <StackLayout>

            <!--Page Heading-->
            <StackLayout Spacing="0">
                <Label Text="{Binding PageTitle}"
                       Style="{StaticResource PageTitle}"/>
                <BoxView HeightRequest="1" Color="LightGray" />
            </StackLayout>
            
            <!-- Merchandiser Profile -->
            <StackLayout Margin="10">
                
                <Label Text="Name"/>
                <Entry Text="{Binding Name}"
                       IsEnabled="False"/>

                <Label Text="Phone"/>
                <Entry Text="{Binding Phone}"
                   IsEnabled="False"/>

                <Label Text="Email"/>
                <Entry Text="{Binding Email}"
                   IsEnabled="False"/>

                <StackLayout Orientation="Horizontal"
                             HorizontalOptions="Center">
                    <Button Text="Back"
                            Clicked="BackButton_Clicked"/>
                    <Button Text="Edit"
                            Clicked="EditButton_Clicked"/>
                </StackLayout>

                <Button Text="Delete"
                        Command="{Binding DeleteCommand}"/>

            </StackLayout>

        </StackLayout>
    </ContentPage.Content>
</ContentPage>

MerchandiserProfilePage.xaml.cs -(查看 - 代码隐藏)

    public partial class MerchandiserProfilePage : ContentPage
    {
        private Models.Merchandiser SelectedMerchandister { get; set; }

        public MerchandiserProfilePage (Models.Merchandiser selectedMerchandiser)
        {
            InitializeComponent ();
            SelectedMerchandister = selectedMerchandiser;
            this.BindingContext = new ViewModels.MerchandiserProfilePageViewModel(selectedMerchandiser);
        }

        async private void BackButton_Clicked(object sender, EventArgs e)
        {
            await Navigation.PopModalAsync();
        }

        async private void EditButton_Clicked(object sender, EventArgs e)
        {
            await Navigation.PushModalAsync(new Views.MerchandiserEditPage(SelectedMerchandister));
        }
    }

MerchandiserProfilePageViewModel.cs (ViewModel)

using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;

namespace MobileApp.ViewModels
{
    public class MerchandiserProfilePageViewModel : BaseViewModel
    {
        public string PageTitle { get; } = "Merchandiser Profile";

        public Command DeleteCommand { get; }

        private string name;
        public string Name
        {
            get { return name;  }
            set
            {
                name = value;
                OnPropertyChanged();
            }
        }

        private string phone;
        public string Phone
        {
            get { return phone; }
            set
            {
                phone = value;
                OnPropertyChanged();
            }
        }

        private string email;
        public string Email
        {
            get { return email; }
            set
            {
                email = value;
                OnPropertyChanged();
            }
        }
        
        public MerchandiserProfilePageViewModel(Models.Merchandiser selectedMerchandiser)
        {
            Name = selectedMerchandiser.Name;
            Phone = selectedMerchandiser.Phone;
            Email = selectedMerchandiser.Email;
            DeleteCommand = new Command( async()=> {

                bool deleteConfirmed = await Application.Current.MainPage.DisplayAlert("Confirm Delete",$"Are you sure you want to delete {selectedMerchandiser.Name} as a Merchandiser?","Yes","No");

                if (deleteConfirmed)
                {
                    // TODO: Delete Merchandiser
                    await Application.Current.MainPage.Navigation.PopModalAsync();
                }
            });

        }
    }
}

【问题讨论】:

  • 您的虚拟机会创建您正在编辑的对象的副本,因此对其所做的任何更改都不会反映在绑定到原始副本的其他视图中物体。您可以更改此设置,也可以使用事件或 MessagingCenter 等机制通知他们需要刷新数据副本
  • 我尝试将selectedMerchandiserMerchandiserEditPageViewModel 传递到Save Command,但这没有用。所以我创建了Merchandisernew 实例进行测试,希望这会添加另一条记录(但我忘记在发布之前恢复到原始代码)。这是你说的吗?我不想学习诸如 MessagingCenter 之类的新东西,因为这是明天到期的学校项目,我没有时间学习新东西。您能否提供一些代码来告诉我需要进行哪些更改?
  • 你的 repo 中的代码在启动时崩溃了,所以我无法告诉你需要更改什么
  • 我更新了我的代码以反映更改。我的代码似乎为我执行。你添加了 SQLite 吗?我使用 VS2017 和 Android Nexus 5 Oreo 8.1 API 27 作为我的模拟器。我的目标框架是 .NET2.0。老实说,除了 Xamarin.Forms 和在其他设备上使用我的代码之外,我什么都没有。这个问题对我来说似乎也不是孤立的。许多其他学生也面临同样的问题
  • 另外,您设置的是私有变量而不是公共变量 this.email = selectedMerchandiser.Email; 而不是 Email = selectedMerchandiser.Email; 所以 OnPropertyChanged() 不会触发以通知 UI 任何更改

标签: c# sqlite xamarin xamarin.forms mvvm


【解决方案1】:

你的虚拟机中有一组硬编码的数据,而不是从数据库中加载它

public MerchandisersPageViewModel()
{
    //Merchandisers = new ObservableCollection<Models.Merchandiser>(Database.MerchandiserDatabase.GetMerchandisers());
    Merchandisers = new ObservableCollection<Models.Merchandiser>()
    {
       new Models.Merchandiser { Id=1, Name="Barney Rubble", Phone="021 321 654", Email="barney@rubble.com"},
       new Models.Merchandiser { Id=2, Name="Frank Grimes", Phone="022 456 789", Email="grimey@homersfriend.com"},
       new Models.Merchandiser { Id=3, Name="Perry Platypus", Phone="023 789 456", Email="perry@agentp.com"},
    };
}

更新:

MerchandiserProfilePageViewModel 中,去掉NamePhoneEMail 的属性

然后在MerchandiserProfilePage.xaml 中更改绑定

<Entry Text="{Binding SelectedMerchandiser.Name}" IsEnabled="False"/>

【讨论】:

  • 谢谢 Jason,我们一起发现了这个问题。 :) 问题 #1 已解决。我创建了一个新视频,概述了剩余的问题youtube.com/watch?v=miYxKM3d10Y
  • 更新的视频概述了问题 #2 youtube.com/watch?v=5_6KJ0QJouM
  • 每个 VM 都有自己的名称/电话/电子邮件属性,对一个 VM 上的这些属性的更改不会影响另一个 VM。相反,每个 VM 应该只有一个 Merchandiser 属性,所有这些属性都引用同一个对象,因此对其中一个对象的更改会影响另一个。 Merchandiser 模型应该实现 INotifyPropertyChanged
  • Jason,我非常感谢你的帮助,事情慢慢开始变得有意义,我已经接近解决这个问题了。我更新了 Merchandiser 模型,其他部分代码和我的应用程序的一部分按预期工作,但其他部分没有。 .请参阅youtube.com/watch?v=vfO9zvOjizU 的更新视频基本上EditPageProfilePage 似乎没有同步,但ListView 正在更新!我在 StackOverflow github.com/minlopalis/XamarinFormsMobileAppProject.git 中更新了我的代码
  • 查看我的扩展示例
猜你喜欢
  • 2020-07-19
  • 2021-07-02
  • 1970-01-01
  • 2020-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多