为什么我的 Xamarin.Forms ListView 数据绑定不会反映在其他视图中?

Why won't my Xamarin.Forms ListView Data Binding reflect in other Views?

我的 MainPage.xaml 页面绑定到 ClientsViewModel.cs。此页面有一个 ListView 绑定到 ObservableCollection 属性.

NewClient.xaml 页面和输入字段也绑定到 ClientsViewModel.cs

当我使用 NewClient.xaml 表单保存新客户端并导航回 MainPage.xaml(使用导航后退箭头)时,我希望在 MainPage.xaml 中看到新添加的客户端ListView 但是我没有看到这种变化。

为什么 MainPage.xaml 中的 ListView 没有显示最新更新的记录?我哪里错了?

可能值得一提的是,我的实际项目将使用 SQLite,因此 ObseravbleCollection 最终将直接从 SQLite 数据库中获取记录,因此也将不胜感激任何有关此的帮助或建议。

参考下面的代码,或者从我的 GitHub 存储库中克隆 https://github.com/minlopalis/XamarinForms-ListView-DataBinding.git

(型号)Client.cs

    public class Client
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Phone { get; set; }
    }

(视图模型)BaseViewModel.cs

    public class BaseViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

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

    }

(查看模型)ClientViewModel.cs

 public class ClientViewModel : BaseViewModel
    {
        private ObservableCollection<Client> clients;
        public ObservableCollection<Client> Clients 
        {
            get { return clients; } 
            set
            {
                clients = value;
                OnPropertyChanged();
            }
        }

        public Command SaveClientCommand { get; }

        public ClientViewModel()
        {
            this.Clients = new ObservableCollection<Client>();

            SaveClientCommand = new Command(()=> {

                Client client = new Client()
                {
                    Name = Name,
                    Phone = Phone
                };

                Clients.Add(client);
                OnPropertyChanged(nameof(Clients));
            });
        }



        private int id;
        public int Id 
        {
            get { return id; }
            set
            {
                id = value;
                OnPropertyChanged();
            }
        }

        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();
            }
        }

    }

(查看)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:viewModels="clr-namespace:DataBinding.ViewModels"
             x:Class="DataBinding.MainPage">
    
    <ContentPage.BindingContext>
        <viewModels:ClientViewModel/>
    </ContentPage.BindingContext>
    
    <StackLayout>
        <Label Text="Client List"></Label>

        <ListView ItemsSource="{Binding Clients}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <StackLayout>
                        <Label Text="{Binding Name}"/>
                        <Label Text="{Binding Phone}"/>
                    </StackLayout>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

        <Button Text="Add Client"
                Clicked="AddClientButton_Clicked"/>
    </StackLayout>

</ContentPage>

(查看)NewClient.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:viewModels="clr-namespace:DataBinding.ViewModels"
             x:Class="DataBinding.Views.NewClient">

    <ContentPage.BindingContext>
        <viewModels:ClientViewModel/>
    </ContentPage.BindingContext>
    
    <ContentPage.Content>
        <StackLayout>
            <Label Text="Add New Client" />

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

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

            <Button Text="Save"
                    Command="{Binding SaveClientCommand}"/>

            <!-- Added ListView -->
            <ListView ItemsSource="{Binding Clients}">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <StackLayout>
                                <Label Text="{Binding Name}"/>
                                <Label Text="{Binding Phone}"/>
                            </StackLayout>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>

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

我已经从 repo 下载了你的代码,我认为它有一个很大的缺陷导致了这个问题。您在两个页面上的 XAML 中都设置了 BindingContext。如果您在 ClientViewModel 的构造函数中设置断点,您会注意到它被调用了两次:一次是在应用程序启动时,一次是在您单击“添加客户端”时。

这意味着您正在查看此 class 的两个单独实例,因此您的 Client 处于错误的实例中。您想确保您正在查看相同的视图模型。

更重要的是,您甚至可能希望通过创建一个额外的东西来更好地分离关注点,即:CreateClientViewModel 它只负责创建客户端并将该对象返回给 ClientViewModel 然后依次将其添加到集合中。

希望对您有所帮助!

根据你的描述,你想在页面之间传递数据,我建议你可以使用MessagingCenter

主页:

 <StackLayout>
    <Label Text="Client List" />

    <ListView ItemsSource="{Binding Clients}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <StackLayout>
                        <Label Text="{Binding Name}" />
                        <Label Text="{Binding Phone}" />
                    </StackLayout>
                </ViewCell>

            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
    <Button Command="{Binding SaveClientCommand}" Text="Add Client" />
</StackLayout>

public partial class Page9 : ContentPage
{
    private ClientViewModel _clientmodel;
    public ClientViewModel clientmodel
    {
        get { return _clientmodel; }
        set
        {
            _clientmodel = value;
        }
    }

    public Page9()
    {
        InitializeComponent();
        this.BindingContext = new ClientViewModel(this.Navigation);
    }      
}

 public class ClientViewModel 
{       
    public ObservableCollection<Client> Clients { get; set; }   
    public Command SaveClientCommand { get; }
    private INavigation _navigation;

    public ClientViewModel(INavigation navitation)
    {
        Clients = new ObservableCollection<Client>();
        Clients.Add(new Client() { Name = "client1", Phone = "123" });

        _navigation = navitation;

        SaveClientCommand = new Command(async() => {

            await _navigation.PushAsync(new NewClient());
        });
        MessagingCenter.Subscribe<string, string[]>("test", "Add", (sender, values) =>
        {
            Client client = new Client() { Name=values[0],Phone=values[1]};
            Clients.Add(client);
        });
    } 
      
}

NewClient.xaml:

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

    private void Button_Clicked(object sender, EventArgs e)
    {
        string name = entry1.Text;
        string phone = entry2.Text;

        string[] values = { name,phone};
        MessagingCenter.Send<string, string[]>("test", "Add", values);
        Navigation.PopAsync();

    }
}

顺便说一下,您不需要为 ObservableCollection 调用 PropertyChanged,因为 ObservableCollection Class 表示一个动态数据集合,它在添加、删除项目或刷新整个列表时提供通知。

谢谢大家的帮助,我的问题已经解决了

我的代码有两个问题。

1.两个 ViewModel 实例

正如 Gerald Versluis 所指出的,我有两个 ViewModel 实例。我通过在我的 App.xaml 页面的 Application.Resources 中创建我的视图模型实例来解决这个问题。

<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DataBinding.App"
             xmlns:ClientViewModel="clr-namespace:DataBinding.ViewModels">
    <Application.Resources>
        <ClientViewModel:ClientViewModel x:Key="ClientViewModel" />
    </Application.Resources>
</Application>

并将每个页面绑定到静态资源(如下)

<?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:ViewModels="clr-namespace:DataBinding.ViewModels"
             x:Class="DataBinding.Views.NewClient">

    <ContentPage.BindingContext>
        <StaticResource Key="ClientViewModel"/>
    </ContentPage.BindingContext>

    <ContentPage.Content>
        <StackLayout>
            <Label Text="Add New Client" />

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

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

            <Button Text="Save"
                    Command="{Binding SaveClientCommand}"/>

            
            <!-- Added ListView -->
            <ListView ItemsSource="{Binding ClientList}">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <StackLayout>
                                <Label Text="{Binding Name}"/>
                                <Label Text="{Binding Phone}"/>
                            </StackLayout>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
            
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

感谢 Gerald Versluis 的帮助。查看他的 YouTube 频道 here

2。缺少 ViewCell

我的 MainPage.xamlListView 中缺少一个 ViewCell。这是一个简单的打字疏忽,但抛出了“'指定的转换无效”错误。非常感谢 Alexander Fauland for his reply to this 线程帮助我解决了我丢失的 ViewCell 问题。