2016-10-19 2 views
4

Я пытаюсь достичь идеального MVVM в Xamarin.Forms со Entry.Двусторонняя вяжущая запись, которая поддерживает базовые типы и типы с нулевым значением

В моей модели содержатся свойства, тип которых включает строку, int ?, decimal ?, bool ?, etc. Всякий раз, когда я привязываюсь к строковому типу, двухсторонняя привязка работает, потому что свойство text имеет тип строки (они соответствуют). Но как только вы попытаетесь привязать себя к модели, а свойство - int или int?, Оно не обновляет значение свойства модели.

В ходе моих исследований и с помощью поддержки Xamarin, это был очень полезный нить о том, как обрабатывать NULLABLE типов:

Nullable type in x:TypeArguments

XAML код:

<controls:NullableIntEntry Grid.Column="1" Grid.Row="14" NumericText="{Binding BusinessOwnership, Mode=TwoWay}" x:Name="lblBusinessOwnership"></controls:NullableIntEntry>

BindableEntry (Расширение ввода) код:

using System; 
 
using System.Collections; 
 
using System.Collections.Specialized; 
 
using System.Reflection; 
 
using Xamarin.Forms; 
 

 
namespace CreditBuilderApp.Controls 
 
{ 
 
    public class BindableEntry<T> : Entry 
 
    { 
 
     static bool firstLoad; 
 

 
     public static readonly BindableProperty NumericTextProperty = 
 
      BindableProperty.Create("NumericText", typeof(T), typeof(BindableEntry<T>), 
 
       null, BindingMode.TwoWay, propertyChanged: OnNumericTextChanged); 
 

 
     static void OnNumericTextChanged(BindableObject bindable, object oldValue, object newValue) 
 
     { 
 
      var boundEntry = (BindableEntry<T>)bindable; 
 
      if (firstLoad && newValue != null) 
 
      { 
 
       firstLoad = false; 
 
       boundEntry.Text = newValue.ToString(); 
 
      } 
 
     } 
 

 
     public T NumericText 
 
     { 
 
      get { return (T)GetValue(NumericTextProperty); } 
 
      set { SetValue(NumericTextProperty, value); } 
 
     } 
 

 
     public BindableEntry() 
 
     { 
 
      firstLoad = true; 
 
      this.TextChanged += BindableEntry_TextChanged; 
 
     } 
 

 
     private void BindableEntry_TextChanged(object sender, TextChangedEventArgs e) 
 
     { 
 
      if (!String.IsNullOrEmpty(e.NewTextValue)) 
 
      { 
 
       this.NumericText = (T)Convert.ChangeType(e.NewTextValue, typeof(T)); 
 
      } 
 
      else 
 
      { 
 
       this.NumericText = default(T); 
 
      } 
 
     } 
 
    } 
 
}

NullableIntEntry и (расширение Bindable запись с указанием типа) NullableDecimalEntry:

using System; 
 
using System.Collections.Generic; 
 
using System.Linq; 
 
using System.Text; 
 
using System.Threading.Tasks; 
 

 
namespace CreditBuilderApp.Controls 
 
{ 
 
    public class NullableIntEntry : BindableEntry<Int32?> 
 
    { 
 

 
    } 
 

 
    public class NullableDecimalEntry : BindableEntry<Decimal?> 
 
    { 
 

 
    } 
 
}

Модель:

 private int? _businessOwnership { get; set; } 
 
     public int? BusinessOwnership 
 
     { 
 
      get { return _businessOwnership; } 
 
      set 
 
      { 
 
       if (_businessOwnership != value) 
 
       { 
 
        _businessOwnership = value; 
 
        RaisePropertyChanged(); 
 
       } 
 
      } 
 
     }

Я на самом деле способен связываться с целым, десятичной, поплавки, в основном любой тип, который не является строкой, которая является шагом в правильном направлении. НО, для этого я должен создать BindableEntry выше и указать, какой тип он есть. (Заменить Т с междунар ?, T с десятичной ?, и т.д. ,. наряду с указанием, как e.NewTextValue отливают

ПРОБЛЕМА:. Преобразование ниже изменение типа ломает два способа связывания

this.NumericText = (T)Convert.ChangeType(e.NewTextValue, typeof(T));
.

Но это дает мне ошибку (очевидно), так как this.NumericText имеет тип T до времени выполнения.

Так что, если я хотел запись работать обнуляемого чисел, я должен был бы заменить весь тип T с int? А также изменить приведенный выше код:

Convert.ToInt32(e.NewTextValue)

Когда я шаг через код, всякий раз, когда я достичь Convert.ChangeType до Т линии, он выходит из кадра. Нет ошибки, и страница отображается, но каждый элемент управления после этой конкретной записи с возможностью связывания не имеет значений.

After stepping through the ConvertType function

Позвольте мне знать, если я пропустил какую-либо информацию. Помощь будет оценена!

ответ

1

Я рекомендую использовать преобразователь значений, это намного проще, чем создавать настраиваемые элементы управления и подтверждает шаблон MVVM.

Вы можете найти фрагмент кода здесь - https://forums.xamarin.com/discussion/comment/60076/#Comment_60076

+0

Преобразователи ценности работают, но не для типов с нулевым значением. Итак, если я конвертирую между int? (Nullable ) и строкой, я не могу вернуть null. Что заставляет работать, если я возвращаю default (int), который равен 0. – TonyMykhaylovsky

+0

@TonyMykhaylovsky, я не уверен, что вы имеете в виду, когда вы говорите, что преобразователи значений не работают с типами с нулевым значением. Конечно, они делают, тип возврата и параметр 'value' имеют тип' object'. Если вы передадите тип значения, например 'int', он будет помещен в поле' object', что уменьшит необходимость преобразования вручную из int? и непустые типы. – ethane

0

Я знаю, что это больше года, но нашел решение этого вопроса, который может помочь другим. Если вы сделаете следующее изменение исходного кода представлено это работает:

BindableEntry (расширение входа) Код:

Изменение BindableEntry_TextChanged (объект отправителя, TextChangedEventArgs е)

private void BindableEntry_TextChanged(object sender, TextChangedEventArgs e) 
    { 
     if (!string.IsNullOrEmpty(e.NewTextValue)) 
     { 
      var t = typeof(T); 
      t = Nullable.GetUnderlyingType(t); 

      if (typeof(T) == typeof(decimal?)) 
      { 
       var results = decimal.TryParse(e.NewTextValue, out var tmpValue) ? 
        tmpValue : (decimal?) null; 

       if (results != null) 
        NumericText = (T)Convert.ChangeType(results, t); 
       else 
        NumericText = default(T); 
      } 
      else if (typeof(T) == typeof(double?)) 
      { 
       var results = double.TryParse(e.NewTextValue, out var tmpValue) ? 
        tmpValue : (double?)null; 

       if (results != null) 
        NumericText = (T)Convert.ChangeType(results, t); 
       else 
        NumericText = default(T); 
      } 
      else if (typeof(T) == typeof(int?)) 
      { 
       var results = int.TryParse(e.NewTextValue, out var tmpValue) ? 
        tmpValue : (int?)null; 

       if (results != null) 
        NumericText = (T)Convert.ChangeType(results, t); 
       else 
        NumericText = default(T); 
      } 
     } 
     else 
     { 
      NumericText = default(T); 
     } 
    } 

Я нашел недостающее чтобы получить эту работу here. Надеюсь, это поможет ...

Смежные вопросы