绑定到MvxSpinner SelectedItem属性不起作用

时间:2018-11-21 09:11:22

标签: c# mvvmcross selecteditem mvxbind mvxspinner

我使用MvxSpinner在MvvmCross for Xamarin应用程序的组合框中显示国家/地区电话前缀。我可以正确绑定到ItemsSource属性,因此可以看到前缀列表,但是当我在视图模型中分配绑定到MvxSpinner的SelectedItem属性的属性时,它将无法正常工作,并且始终显示第一个列表中的元素作为选定项。

我的操作方式如下。在我的ViewModel中,我从服务器获取用户数据,并为Country和PhonePrefix分配属性。然后,我还从服务器获取所有国家/地区的列表和前缀,并将它们绑定到列表属性,该列表属性绑定到可重复使用的MvxSpinners的ItemSource属性(简化):

    public string PhonePrefix { get; set; }
    public string PhoneNumber { get; set; }
    public Country Country { get; set; } = new Country();

    public List<Country> Countries { get; set; } = new List<Country>();
    public List<string> Prefixes { get; set; } = new List<string>();

    private async Task GetUserData()
    {
        try
        {
            var userDataResult = await _registrationService.GetLoggedInUserData();

            if (userDataResult != null)
            {
                if (!userDataResult.HTTPStatusCode.Equals(HttpStatusCode.OK))
                {
                    Mvx.IoCProvider.Resolve<IUserDialogs>().Alert(userDataResult.Error?.Message);
                }
                else
                {

                        PhonePrefix = userDataResult.user.country_code_phone;
                        PhoneNumber = userDataResult.user.phone;
                        Country.id = userDataResult.user.person.addresses[0].country_id;
                        Country.name = userDataResult.user.person.addresses[0].country_name;
                }
            }
        }
        catch (Exception e)
        {
            Log.Error<RegistrationViewModel>("GetUserData", e);
        }
    }

    /// <summary>
    /// Populates the view model properties for Countries and Prefixes with information retrieved from the server
    /// </summary>
    private void ProcessFormData()
    {
        if (_registrationFormData != null)
        {
            Countries = _registrationFormData.Countries?.ToList();
            var userCountry = Countries?.Where(c => c.id == Country?.id).FirstOrDefault();
            Country = IsUserLogedIn && Country != null ? userCountry : Countries?[0];

            var prefixes = new List<string>();
            if (Countries != null)
            {
                foreach (var country in Countries)
                {
                    //spinner binding doesn't allow null values
                    if (country.phone_prefix != null)
                    {
                        prefixes.Add(country.phone_prefix);
                    }
                }
            }

            //we need to assign the binded Prefixes at once otherwise the binding for the ItemSource fails
            Prefixes = prefixes;
            PhonePrefix = Prefixes.Count > 0 && !IsUserLogedIn && string.IsNullOrWhiteSpace(PhonePrefix) ? Prefixes?[0] : PhonePrefix;
        }
    }

在axml布局中:

    <MvxSpinner
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:minHeight="25dp"
            android:layout_marginTop="5dp"
            android:layout_marginBottom="10dp"
            android:id="@+id/spnrCountry"                   
            local:MvxBind="ItemsSource Countries; SelectedItem Country"/>
    <MvxSpinner
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:minHeight="25dp"
            android:layout_marginBottom="10dp"
            android:id="@+id/spnrPrefix"
            local:MvxBind="ItemsSource Prefixes; SelectedItem PhonePrefix"/>

在输出窗口中,我可以看到有关MvxBinding的以下错误:

(MvxBind) Null values not permitted in spinner SelectedItem binding currently

我已调试,并且列表中或绑定到MvxSpinner的ItemSource和SelectedItem属性的属性中都没有任何Null值。

实际上,Countrys ItemSource和SelectedItem可以正常工作,因此,如果用户将其国家/地区保存为阿根廷,则在加载数据时,微调器中的选定项目将是Argentina。请注意,我使用的是这样的国家/地区实体:

public class Country
{
    public int id { get; set; }
    public bool favorite { get; set; }
    public string name { get; set; }
    public string name_de { get; set; }
    public string code { get; set; }
    public int rzl_code { get; set; }
    public string phone_prefix { get; set; }
    public string updated_at { get; set; }
    public string created_at { get; set; }

    public override string ToString()
    {
        return name;
    }
}

我还尝试在包裹字符串值的它自己的实体中设置电话前缀,但这也不起作用。

有人知道我在做什么错吗?为什么对于正在使用的国家/地区而不是前缀?

我使用PropertyChanged.Fody。

谢谢!

4 个答案:

答案 0 :(得分:1)

您遇到的关于null的错误是因为微调器中的SelectedItem不允许null作为选定项。因此,如果要显示一个空项目,一个解决方案是添加另一个具有空值的固定项目,即,对于PhonePrefix,您可以设置string.Empty并将其添加到{{ 1}},然后在您的PhonePrefixes中将第一个设置为默认值,或创建一个名称为Country的存根Country并将其添加到国家/地区列表中。

要考虑的另一点是,当您要更新视图时,必须确保在主线程中进行了通知。您正在尝试更新另一个线程的None中的PhonePrefix,以使视图不会引起注意。

您应该执行以下操作来更新Task

PhonePrefix

这将直接在主线程上完成this.InvokeOnMainThread(() => PhonePrefix = userDataResult.user.country_code_phone; );的设置,以便正确通知您的视图。


更新

在仔细查看了您的问题和自己的答案并看到您使用PhonePrefix之后,我可以猜到问题出在实际上是您如何分配PropertyChanged.Fody

PhonePrefix的默认行为是添加Equality Checking来替换您的媒体资源代码

PropertyChanged.Fody

类似

public string PhonePrefix { get; set; }

因此,当您在private string _phonePrefix; public string PhonePrefix { get { return _phonePrefix; } set { if (!String.Equals(_phonePrefix, value)) { _phonePrefix = value; OnPropertyChanged("PhonePrefix"); } } } 中进行操作时:

GetUserData()

PhonePrefix = userDataResult.user.country_code_phone;

ProcessFormData()

PhonePrefix = Prefixes.Count > 0 && !IsUserLogedIn && string.IsNullOrWhiteSpace(PhonePrefix) ? Prefixes?[0] : PhonePrefix; 不是null或空格,因此它尝试重新分配相同的值,但是由于fody添加了相等性检查,因此不会再次分配它,因此它不会引发值的更改,因此视图会没有得到通知。

PhonePrefix中的分配,我认为它可能是在另一个线程中完成的,这就是为什么视图未得到通知的原因。 根据您所说的,GetUserData()确实在Country中进行了更新,因此,为了在该位置也可以对ProcessFormData()进行更新,您应该只将PhonePrefix属性添加到该属性中避免进行相等性检查,仅此而已。

[DoNotCheckEquality]

如果不起作用,您也应该在主线程上添加调用(我建议您在调试中查看正在执行该方法的线程,以查看是否确实需要在主线程上进行调用)。

HIH

答案 1 :(得分:0)

它确实起作用,因为在GetUserData中设置了PhonePrefix,所以您也应该设置Country。从您的代码“国家/地区”为空,这就是为什么您从所选列表中获得第一项,或者也出错的原因。

答案 2 :(得分:0)

尽管这是一个奇怪的解决方案,但仍然不十分了解为什么,但我找到了一种使之起作用的方法。我觉得它与异步性有关。

该问题是在GetuserData()方法的PhonePrefix属性中分配的,然后在ProcessFormData()中对其进行重新分配的。因此,现在可以运行的代码如下:

IsPrimary

请注意,添加了一个私有属性_phonePrefix来将电话前缀值保存在GetUserData()中,然后将其值分配给它在视图中绑定的属性PhonePrefix。

实际上,我无法真正解释为什么会发生这种情况,因此,如果有人可以解释这种行为,那就太好了。

答案 3 :(得分:0)

您正在使用的属性不会通知要查看的更改,为此您必须使用:

string _phonePrefix; public string PhonePrefix { get => _phonePrefix; set => SetProperty(ref _phonePrefix, value); }

相关问题