Не работает привязка к свойству MvxSpinner SelectedItem - PullRequest
0 голосов
/ 21 ноября 2018

Я использую MvxSpinner для отображения префиксов телефонов страны в комбинированном списке в приложении MvvmCross for Xamarin.Я могу правильно связать свойство ItemsSource, чтобы видеть список моих префиксов, но когда я назначаю свойство в моей модели представления, которое связывается со свойством SelectedItem MvxSpinner, оно не будет работать и всегда будет отображать первыйэлемент в списке в качестве выбранного элемента.

Способ, которым я это делаю, заключается в следующем.В моей ViewModel я получаю пользовательские данные с сервера и назначаю свойства для Country и PhonePrefix.Затем я получаю список всех стран и префиксов также с сервера и связываю их со свойствами списка, которые связаны со свойствами ItemSource ответного MvxSpinners (упрощенно):

    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

Я отлаживал, и у меня никогда не было нулевых значений в списках или в свойствах, которые я связывал ссвойства ItemSource и SelectedItem MvxSpinner.

Фактически, страны ItemSource и SelectedItem работают должным образом, поэтому, если пользователь сохранил свою страну в Аргентине, при загрузке его данных выбранным элементом в счетчике будет Аргентина.Обратите внимание, что я использую сущность Country следующим образом:

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 голосов
/ 27 ноября 2018

Свойства, которые вы используете, не уведомляют об изменениях для просмотра, для этого вы должны использовать:

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

0 голосов
/ 21 ноября 2018

Ошибка, которую вы получаете около null, заключается в том, что SelectedItem в счетчике не допускает null в качестве выбранного элемента.Поэтому, если вы хотите отобразить пустой элемент, одним из решений является добавление еще одного фиксированного элемента с пустым значением, то есть в случае PhonePrefix вы можете установить string.Empty и добавить его в список PhonePrefixes и в вашCountry вы можете установить первый по умолчанию или создать, например, заглушку Country с именем None и добавить ее в список стран.

Еще один момент, который необходимо учитывать, - это когда выЧтобы обновить представление, вы должны быть уверены, что уведомляете его в Главной теме.Вы пытаетесь обновить PhonePrefix в Task другого потока, чтобы представление не было замечено.

Вам необходимо обновить PhonePrefix, выполнив:

this.InvokeOnMainThread(() => PhonePrefix = userDataResult.user.country_code_phone; );

Это обеспечит выполнение набора PhonePrefix непосредственно в главном потоке, чтобы ваше представление было правильно уведомлено.


Обновление

После того, как вы лучше рассмотрите свой вопрос и свой собственный ответ и увидите, что вы используете PropertyChanged.Fody, я могу догадаться, что проблема была в том, как вы назначаете поведение PhonePrefix.

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;

PhonePrefix не является нулем или пробелом, поэтому он пытается переназначить одно и то же значение, но поскольку fody добавляет проверку на равенство, ему не назначается снова, и поэтомуэто не вызывает изменения значения, поэтому представление не получает уведомления.

Назначение в GetUserData() Я думаю, что это может быть сделано в другом потоке, и поэтому представление не получает уведомления.Согласно тому, что вы сказали, Country действительно обновляется в ProcessFormData(), поэтому для того, чтобы PhonePrefix тоже обновлялся в этом месте, вам нужно только добавить атрибут [DoNotCheckEquality] к свойству, чтобы избежать проверки на равенство, и это должно бытьвсе.

[DoNotCheckEquality]
public string PhonePrefix { get; set; }

Если это не работает, вы должны добавить вызов также в основном потоке (я советую вам посмотреть в отладке, в каком потоке выполняется метод, чтобы узнать, нужен ли вам вызов в основном потоке).нить).

HIH

0 голосов
/ 23 ноября 2018

Хотя это странное решение и до сих пор не совсем понятно, почему, я нашел способ заставить его работать.У меня такое ощущение, что это как-то связано с асинхронизмом.

Проблема была назначена в свойстве PhonePrefix в методе GetuserData (), а затем переназначена в ProcessFormData ().Итак, теперь работающий код выглядит следующим образом:

private string _phonePrefix;
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>();

public override async Task Initialize()
{
    await base.Initialize();
    IsUserLogedIn = await _authService.IsUserLoggedIn();
    if (IsUserLogedIn)
    {
        //get user data from server, show user data
        await GetUserData();
    }

    //get countries, prefixes
    if (Countries.Count <= 0 || Prefixes.Count <= 0)
    {
        _registrationFormData = await _registrationService.GetRegistrationFormData();
        ProcessFormData();
    }

    AddValidationRules();
}

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;
                    //can't bind it here to the public binded property because the SelectedItem binding fails. 
                    //I first assign it to a private field and then use it in the ProcessFormData method
                    _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);
    }
}



    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 SelectedItem fails
            Prefixes = prefixes;
            if (Prefixes.Count > 0 && !IsUserLogedIn && string.IsNullOrWhiteSpace(_phonePrefix))
            {
                PhonePrefix = Prefixes?[0];
            }
            else
            {
                PhonePrefix = _phonePrefix;
            }
        }
    }

Обратите внимание на добавление частного свойства _phonePrefix для хранения значения префикса телефона в GetUserData (), а затем присвойте его значение свойству, с которым оно связанопредставление, PhonePrefix.

На самом деле я не могу действительно объяснить, почему это происходит, поэтому было бы хорошо, если бы кто-то мог объяснить это поведение.

0 голосов
/ 21 ноября 2018

Это работает, так как PhonePrefix установлен в GetUserData, также вы должны установить Country.Из вашего кода Страна пуста, поэтому вы получаете первый элемент из выбранного списка или также ошибку.

...