【问题标题】:Xamarin Android Picker binding issue to ObservableCollectionXamarin Android Picker 绑定到 ObservableCollection 的问题
【发布时间】:2020-11-19 19:46:03
【问题描述】:

我有一个简单的 Xamarin.Forms 项目,带有 Picker。

我的视图绑定到一个继承自 INotifyPropertyChanged 的视图模型。

我定义了一个ObservableCollection,比如:

public ObservableCollection<User> UserList { get; set; }

由 HTTP 客户端 Get 方法填充:

UserList = JsonConvert.DeserializeObject<ObservableCollection<User>>(s);

我已经验证UserList 填充了预期的数据。

这是我的选择器:

<Picker Title="User"
        Grid.Row="0"
        Grid.Column="0"
        ItemsSource="{Binding UserList}"
        ItemDisplayBinding="{Binding UserName}"
        SelectedItem="{Binding UserSelectedItem}"/>

这是我的SelectedItem 绑定属性:

public User UserSelectedItem { get; set; }

我没有使用OnPropertyChanged 的UserSelectedItem。

问题: 我的Picker 中没有任何值,即使我已经验证UserList 中确实包含预期的数据。

编辑

我修改了我的ObservableCollection 以使用 OnPropertyChanged:

private ObservableCollection<User> _userList;
public ObservableCollection<User> UserList
{
    get
    {
        return _userList;
    }
    set
    {
        _userList = value;
        OnPropertyChanged(nameof(UserList));
    }
}

但是数据仍然没有进入选择器...

编辑 2 我仍然无法弄清楚这一点。 这是我的 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="PTSX.Views.JoinCrew">
    <ContentPage.Content>
        <StackLayout>
            <Label Text="Join a Crew" 
                HorizontalOptions="CenterAndExpand" />

            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="AUTO"/>
                    <RowDefinition Height="200"/>
                    <RowDefinition Height="200"/>
                    <RowDefinition Height="100"/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
            </Grid>
            
            
            <Picker x:Name ="UserPicker" 
                    Title="User"
                    Grid.Row="0"
                    Grid.Column="0"
                    ItemsSource="{Binding UserList}"
                    ItemDisplayBinding="{Binding UserName}"
                    SelectedItem="{Binding UserSelectedItem}"/>
            
            <Picker x:Name="CrewPicker" 
                    Title="Crew"
                    Grid.Row="1"
                    Grid.Column="0"
                    ItemsSource="{Binding CrewList}"
                    ItemDisplayBinding="{Binding CrewName}"
                    SelectedItem="{Binding CrewSelectedItem}"/>

            <Button x:Name="buttonJoinCrew"
                    Text="Join"
                    Grid.Row="2"
                    Grid.Column="0"
                    Command="{Binding JoinCrewCommand}"/>

            <Button x:Name="buttonCreateCrew"
                    Text="Add Crew"
                    Grid.Row="3"
                    Grid.Column="0"
                    Command="{Binding AddCrewCommand}"/>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

这是我的 XAML 代码:

public partial class JoinCrew : ContentPage
    {
        private JoinCrewViewModel viewModel { get; set; }
        public JoinCrew()
        {
            InitializeComponent();
            viewModel = new JoinCrewViewModel();
            BindingContext = viewModel;
        }
    }

这是我的视图模型:

public class JoinCrewViewModel : INotifyPropertyChanged
    {
        #region Fields
        private string _crewName;
        private string _crewId;
        private string _userName;
        private string _userId;
        private IList<User> _userList;
        private IList<DailyCrew> _crewList;
        private User _userSelectedItem;
        private DailyCrew _crewSelectedItem;
        #endregion

        #region Properties
        public string CrewName
        {
            get
            {
                return _crewName;
            }
            set
            {
                _crewName = value;
                OnPropertyChanged();
            }
        }

        public string CrewId
        {
            get
            {
                return _crewId;
            }
            set
            {
                _crewId = value;
                OnPropertyChanged();
            }
        }

        public string UserName
        {
            get
            {
                return _userName;
            }
            set
            {
                _userName = value;
                OnPropertyChanged();
            }
        }

        public string UserId
        {
            get
            {
                return _userId;
            }
            set
            {
                _userId = value;
                OnPropertyChanged();
            }
        }

        public DailyCrew CrewSelectedItem
        {
            get => _crewSelectedItem;
            set
            {
                _crewSelectedItem = value;
                OnPropertyChanged();
            }
        }
        public User UserSelectedItem
        {
            get => _userSelectedItem;
            set
            {
                _userSelectedItem = value;
                OnPropertyChanged();
            }
        }

        public IList<DailyCrew> CrewList
        {
            get
            {
                return _crewList;
            }
            set
            {
                _crewList = value;
                OnPropertyChanged();
            }
        }
        public IList<User> UserList
        {
            get
            {
                return _userList;
            }
            set
            {
                _userList = value;
                OnPropertyChanged();
            }
        }

        #endregion

        #region Events
        public event PropertyChangedEventHandler PropertyChanged;

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

        #region Delegates
        public Command JoinCrewCommand { get; }
        public Command AddCrewCommand { get; }
        #endregion

        #region Contructor

        public JoinCrewViewModel()
        {
            JoinCrewCommand = new Command(JoinCrewAction);
            AddCrewCommand = new Command(AddCrewAction);
            async Task FillUsersAsync();
        }

        private bool CanExecuteCommand(object commandParameter)
        {
            return true;
        }

        #endregion

        #region Commands
        private async void JoinCrewAction()
        {
            await JoinCrew();
        }

        #endregion

        #region Private Methods
        

        private async Task<IList<User>> FillUsersAsync()
        {
            IList<User> result = null;
            using (var client = new HttpClient())
            {
                HttpResponseMessage response = await client.GetAsync($"https://xxx/api/mobile/getuserslist");
                if (response.IsSuccessStatusCode)
                {
                    string s = await response.Content.ReadAsStringAsync();
                    result = JsonConvert.DeserializeObject<IList<User>>(s);
                }
            }
            return result;
        }
}

我可以像以前一样确认我有数据通过 Http 客户端返回到我的 IList

我不明白为什么选择器没有被填满。这似乎应该很容易做到。

【问题讨论】:

  • UserList 设置器是否调用 PropertyChanged?如果不是,那么在分配数据时 UI 将不会更新。您可以尝试通过在获取数据时手动设置 ItemsSource 而不是使用绑定来验证这一点。
  • 嗨 Jason,我曾假设 ObservableCollection 是双向绑定。我理解错了吗?
  • OC 仅在添加/删除/清除项目时更新。因为你在获取数据的时候是在创建一个OC的新实例,所以它需要通过PropertyChanged告诉UI集合对象本身已经改变了
  • 嗨,Jason,我在帖子中添加了一些代码。我修改了 OC 以使用 OnPropertyChanged,但仍然没有得到任何数据。
  • 您是否尝试手动设置 ItemsSource?

标签: c# xaml xamarin.forms inotifypropertychanged


【解决方案1】:

我编写了一个简单的示例,它对我来说非常有效。

通过点击按钮,绑定到 Picker 的 ItemsSource 的属性会被加载,并且由于 OnPropertyChanged() 调用,会通知选择器集合已更改并正确加载值。

请查看示例,如果仍有问题,请分享更多详细信息...

MainPage.xaml.cs

using System;
using Xamarin.Forms;

namespace PickerOC
{
    public partial class MainPage : ContentPage
    {

        MainPageViewModel viewModel { get; set; }

        public MainPage()
        {
            InitializeComponent();
        }

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

            viewModel = new MainPageViewModel();
            BindingContext = viewModel;
        }

        private void Button_Clicked(object sender, EventArgs e)
        {
            viewModel.PickerData = new System.Collections.ObjectModel.ObservableCollection<UserX>()
            {
                new UserX("John L."), new UserX("Paul M."), new UserX("George H."), new UserX("Ringo S.")
            };
        }
    }
}

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"
             x:Class="PickerOC.MainPage">

    <StackLayout>
        <Picker ItemsSource="{Binding PickerData}"
                ItemDisplayBinding="{Binding UserName}"/>
        <Button Text="Load Picker Data!"
                Clicked="Button_Clicked"/>
    </StackLayout>

</ContentPage>

视图模型

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace PickerOC
{
    class MainPageViewModel : INotifyPropertyChanged
    {

        private ObservableCollection<UserX> _PickerData;
        public ObservableCollection<UserX> PickerData
        { 
            get => _PickerData;
            set
            {
                _PickerData = value;
                OnPropertyChanged();
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged([CallerMemberName] string name = "")
        {
            var propertyChanged = PropertyChanged;

            propertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
    }

    public class UserX
    {
        public UserX(String name)
        {
            UserName = name;
        }
        public String UserName { get; set; }
    }
}

【讨论】:

    【解决方案2】:

    我使用了您发布的代码并让它在我身边工作。

    注意,我用一个简单的异步函数模拟了 FillUsersAsync,该函数等待 5 秒,然后返回用户集合。

    XAML

    <ContentPage.Content>
            <StackLayout>
                <Label Text="Join a Crew" 
                    HorizontalOptions="CenterAndExpand" />
    
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="AUTO"/>
                        <RowDefinition Height="200"/>
                        <RowDefinition Height="200"/>
                        <RowDefinition Height="100"/>
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>
                </Grid>
    
    
                <Picker x:Name ="UserPicker" 
                        Title="User"
                        Grid.Row="0"
                        Grid.Column="0"
                        ItemsSource="{Binding UserList}"
                        ItemDisplayBinding="{Binding UserName}"
                        SelectedItem="{Binding UserSelectedItem}"/>
    
                <Picker x:Name="CrewPicker" 
                        Title="Crew"
                        Grid.Row="1"
                        Grid.Column="0"
                        ItemsSource="{Binding CrewList}"
                        ItemDisplayBinding="{Binding CrewName}"
                        SelectedItem="{Binding CrewSelectedItem}"/>
    
                <Button x:Name="buttonJoinCrew"
                        Text="Join"
                        Grid.Row="2"
                        Grid.Column="0"
                        Command="{Binding JoinCrewCommand}"/>
    
                <Button x:Name="buttonCreateCrew"
                        Text="Add Crew"
                        Grid.Row="3"
                        Grid.Column="0"
                        Command="{Binding AddCrewCommand}"/>
            </StackLayout>
        </ContentPage.Content>
    

    代码隐藏

    using Xamarin.Forms;
    
    namespace PickerOC
    {
        public partial class MainPage : ContentPage
        {
    
            MainPageViewModel viewModel { get; set; }
    
            public MainPage()
            {
                InitializeComponent();
                viewModel = new MainPageViewModel();
                BindingContext = viewModel;
            }
    
            
        }
    }
    

    查看模型

    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Threading.Tasks;
    using Xamarin.Forms;
    
    namespace PickerOC
    {
        class MainPageViewModel : INotifyPropertyChanged
        {
            #region Fields
            private string _crewName;
            private string _crewId;
            private string _userName;
            private string _userId;
            private IList<User> _userList;
            private IList<DailyCrew> _crewList;
            private User _userSelectedItem;
            private DailyCrew _crewSelectedItem;
            #endregion
    
            #region Properties
            public string CrewName
            {
                get
                {
                    return _crewName;
                }
                set
                {
                    _crewName = value;
                    OnPropertyChanged();
                }
            }
    
            public string CrewId
            {
                get
                {
                    return _crewId;
                }
                set
                {
                    _crewId = value;
                    OnPropertyChanged();
                }
            }
    
            public string UserName
            {
                get
                {
                    return _userName;
                }
                set
                {
                    _userName = value;
                    OnPropertyChanged();
                }
            }
    
            public string UserId
            {
                get
                {
                    return _userId;
                }
                set
                {
                    _userId = value;
                    OnPropertyChanged();
                }
            }
    
            public DailyCrew CrewSelectedItem
            {
                get => _crewSelectedItem;
                set
                {
                    _crewSelectedItem = value;
                    OnPropertyChanged();
                }
            }
            public User UserSelectedItem
            {
                get => _userSelectedItem;
                set
                {
                    _userSelectedItem = value;
                    OnPropertyChanged();
                }
            }
    
            public IList<DailyCrew> CrewList
            {
                get
                {
                    return _crewList;
                }
                set
                {
                    _crewList = value;
                    OnPropertyChanged();
                }
            }
            public IList<User> UserList
            {
                get
                {
                    return _userList;
                }
                set
                {
                    _userList = value;
                    OnPropertyChanged();
                }
            }
    
            #endregion
    
            #region Events
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
            {
                var propertyChanged = PropertyChanged;
                propertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
            #endregion
    
            #region Delegates
            public Command JoinCrewCommand { get; }
            public Command AddCrewCommand { get; }
            #endregion
    
            #region Contructor
    
            public MainPageViewModel()
            {
                JoinCrewCommand = new Command(JoinCrewAction);
                AddCrewCommand = new Command(AddCrewAction);
                Task.Run(async () => UserList = await FillUsersAsync());
                //async Task FillUsersAsync();
            }
    
            private bool CanExecuteCommand(object commandParameter)
            {
                return true;
            }
    
            #endregion
    
            #region Commands
            private async void AddCrewAction()
            {
                await Task.Delay(1000);
            }
    
            private async void JoinCrewAction()
            {
                await Task.Delay(1000);
            }
            #endregion
    
            #region Private Methods
    
    
            private async Task<IList<User>> FillUsersAsync()
            {
                IList<User> result = null;
                //using (var client = new HttpClient())
                //{
                //    HttpResponseMessage response = await client.GetAsync($"https://xxx/api/mobile/getuserslist");
                //    if (response.IsSuccessStatusCode)
                //    {
                //        string s = await response.Content.ReadAsStringAsync();
                //        result = JsonConvert.DeserializeObject<IList<User>>(s);
                //    }
                //}
    
                await Task.Delay(5000);
    
                result = new List<User>()
                {
                    new User() {UserName = "John L."},
                    new User() {UserName = "Paul M."},
                };
    
                return result;
            }
    
            #endregion
        }
    
        public class User
        {
    
            public string UserID { get; set; }
            public string UserName { get; set; }
    
        }
    
        public class DailyCrew
        {
            public string CrewId { get; set; }
            public string CrewName { get; set; }
        }
    }
    

    【讨论】:

    • 所以我在这个答案中使用了你的代码/我的代码,并创建了一个全新的项目。所有代码在新项目中都很好用。我还用我的 http 客户端代码进行了测试。工作得很好。我不知道我原来的项目可能有什么问题。
    【解决方案3】:

    我使用了您发布的代码,并将其用于用户选择器。

    ViewModel.cs

    public class JoinCrewViewModel : INotifyPropertyChanged
        {
            #region Fields
            private ObservableCollection<User> _userList;
            private User _userSelectedItem;
            #endregion
    
            #region Properties
    
            public User UserSelectedItem
            {
                get => _userSelectedItem;
                set
                {
                    _userSelectedItem = value;
                    OnPropertyChanged();
                }
            }
    
            
            public ObservableCollection<User> UserList
            {
                get
                {
                    return _userList;
                }
                set
                {
                    _userList = value;
                    OnPropertyChanged();
                }
            }
    
            #endregion
    
            #region Events
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
            {
                var propertyChanged = PropertyChanged;
                propertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
            #endregion
    
            
    
            #region Contructor
    
            public JoinCrewViewModel()
            {
               FillUsersAsync();
            }
    
            #endregion
    
    
            private async Task<ObservableCollection<User>> FillUsersAsync()
            {
    
                _userList = new ObservableCollection<User>() { 
                      new User()
                      {
                          UserName = "test",
                          UserId = "1"
                      },
                      new User()
                      {
                          UserName = "test1",
                          UserId = "2"
                      },
                      new User()
                      {
                          UserName = "test2",
                          UserId = "3"
                      },
                      new User()
                      {
                          UserName = "test3",
                          UserId = "4"
                      },
                      new User()
                      {
                          UserName = "test3",
                          UserId = "5"
                      }
                };
    
                _userSelectedItem = _userList[2];
    
                return _userList;
            }
        }
    
        public class User
        {
            public string UserName { get; set; }
            public string UserId { get; set; }
        }
    

    Home.xaml

    <StackLayout>
            <Label Text="Join a Crew" 
                    HorizontalOptions="CenterAndExpand" />
    
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="AUTO"/>
                    <RowDefinition Height="200"/>
                    <RowDefinition Height="200"/>
                    <RowDefinition Height="100"/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
            </Grid>
    
    
            <Picker x:Name ="UserPicker" 
                        Title="User"
                        Grid.Row="0"
                        Grid.Column="0"
                        ItemsSource="{Binding UserList}"
                        ItemDisplayBinding="{Binding UserName}"
                        SelectedItem="{Binding UserSelectedItem}"/>
    
        </StackLayout>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-21
      • 2018-10-03
      • 1970-01-01
      • 2018-01-21
      • 2020-04-08
      相关资源
      最近更新 更多