Как связать пользовательский тип с TextBox.Text? - PullRequest
7 голосов
/ 22 января 2009

У меня есть собственный тип c #, например (просто пример):

public class MyVector
{ 
   public double X {get; set;} 
   public double Y {get; set;} 
   public double Z {get; set;} 
   //...
}

И я хочу привязать его к TextBox.Text:

TextBox textBox;
public MyVector MyVectorProperty { get; set;}
//...
textBox.DataBindings.Add("Text", this, "MyVectorProperty");

По сути, мне нужно преобразование в и из строки для моего пользовательского типа значения. В текстовом поле я хочу что-то вроде «x, y, z», которое можно редактировать для обновления типа вектора. Я предполагал, что смогу сделать это, добавив TypeConverter производный класс:

public class MyVectorConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, 
                                        Type sourceType)
    {
        if (sourceType == typeof(string))
            return true;
        //...
        return base.CanConvertFrom(context, sourceType);
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, 
                                      Type destinationType)
    {
        if (destinationType == typeof(string))
            return true;
        //...
        return base.CanConvertTo(context, destinationType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, 
                                       System.Globalization.CultureInfo culture,
                                       object value)
    {
        if (value is string)
        {
            MyVector MyVector;
            //Parse MyVector from value
            return MyVector;
        }
        return base.ConvertFrom(context, culture, value);
    }

    public override object ConvertTo(ITypeDescriptorContext context,
                                     System.Globalization.CultureInfo culture, 
                                     object value, 
                                     Type destinationType)
    {
        if (destinationType == typeof(string))
        {
            string s;
            //serialize value to string s
            return s;
        }
        //...
        return base.ConvertTo(context, culture, value, destinationType);
    }
}

и связать его с моей структурой:

[TypeConverter(typeof(MyVectorConverter))]
public class MyVector { //... }

Похоже, это завершило половину битвы. Я вижу, как меня зовут MyVectorConverter, но что-то не так. Он вызывается, чтобы узнать, знает ли он, как преобразовать в строку, а затем вызывается для преобразования в строку. Однако его никогда не запрашивают, может ли он преобразовать строку FROM или выполнить преобразование. Кроме того, сразу после редактирования в текстовом поле старое значение немедленно заменяется (другая последовательность CanConvertTo и ConvertTo, восстанавливая старое значение). Конечный результат заключается в том, что вновь введенная запись в текстовом поле возвращается сразу после ее применения.

Я чувствую, что просто чего-то не хватает. Есть? Весь этот проект / подход обречен на провал? Кто-нибудь еще пытается такое безумие? Как двунаправленно связать пользовательский многоэлементный тип со строковым элементом управления?

Решение: Как ни странно, все, что нужно, это включить «форматирование» для объекта Binding. (спасибо, Джон Скит):

textBox.DataBindings.Add("Text", this, "MyVectorProperty"); //FAILS
textBox.DataBindings.Add("Text", this, "MyVectorProperty", true); //WORKS!

Как ни странно, все, что мой MSDN упоминает об этом параметре (formattingEnabled):

"true для форматирования отображаемых данных; в противном случае - false"

В нем ничего не говорится о том, что это требование возврата данных из системы управления (при этих условиях).

1 Ответ

10 голосов
/ 22 января 2009

Понятно!

Установите для свойства Binding.FormattingEnabled значение true. Это, кажется, заставляет все это работать. Вы можете сделать это с перегрузкой к методу ControlBindingsCollection.Add, который принимает логический параметр в конце. Странно, что раньше он работал в одном направлении, а не в другом, но теперь мое тестовое приложение работает ...

(Старый ответ ниже)

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

Попробуйте вместо этого использовать класс с автоматически реализуемыми свойствами:

public class MyClass
{ 
   public int IntPart { get; set; } 
   public string StringPart { get; set; }
   //...
}

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

РЕДАКТИРОВАТЬ: Как уже упоминалось в комментариях, теперь у меня есть пример и работает. Binding.Parse поднимается с правильным значением. Теперь выясним, почему TypeConverter не вызывается ...

РЕДАКТИРОВАТЬ: я нашел полезную статью , которая описывает связывание более подробно. Кажется, предполагается, что преобразователь типов используется только для преобразования «в» другой тип, поэтому вам потребуется преобразователь типа для string, чтобы знать, как преобразовать в пользовательский тип. Правда, мне это кажется довольно странным, но есть два других варианта:

  • Используйте события Format и Parse Binding для преобразования
  • Сделать агрегат типа IConvertible

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

...