Чи перетворювачі цін мають більше клопоту, ніж коштують?


20

Я працюю над програмою WPF із переглядами, які потребують численних перетворень цінності. Спочатку моя філософія (частково натхненна цією бурхливою дискусією щодо XAML Disciples ) полягала в тому, що я повинен скласти модель погляду суворо щодо підтримки вимог до даних щодо поглядів. Це означало, що будь-які перетворення значень, необхідні для перетворення даних у такі види, як видимість, пензлі, розміри тощо, будуть оброблятися перетворювачами значень та багатозначними перетворювачами. Концептуально це здавалося досить елегантним. Модель перегляду і погляд мають однозначну мету і добре підключаться. Буде проведено чітку межу між "даними" та "виглядом".

Ну, після надання цій стратегії "старої спроби коледжу", у мене виникають сумніви, чи хочу я продовжувати розвивати цей шлях. Я насправді настійно розглядаю можливість скидання перетворювачів значень і покладання відповідальності за (майже) всю конверсію значень прямо в руки моделі перегляду.

Реальність використання перетворювачів значень просто не здається вимірювальною до очевидного значення чітко розділених проблем. Моя найбільша проблема з перетворювачами цінності полягає в тому, що вони нудно використовувати. Вам потрібно створити новий клас, реалізувати IValueConverterабо передати IMultiValueConverterзначення або значення з objectправильного типу, протестувати DependencyProperty.Unset(принаймні для багатозначних перетворювачів), написати логіку перетворення, зареєструвати перетворювач у словнику ресурсів [див. Оновлення нижче ], і нарешті, підключіть перетворювач, використовуючи досить багатослівний XAML (що вимагає використання магічних рядків як для прив'язки (и), так і для назви перетворювача[див. оновлення нижче]). Процес налагодження теж не є пікніком, оскільки повідомлення про помилки часто є виразними, особливо в режимі дизайну Visual Studio / Expression Blend.

Це не означає, що альтернатива - зробити модель перегляду відповідальною за все перетворення цінностей - є вдосконаленням. Це може бути справою того, що трава з іншого боку буде зеленішою. Окрім втрати елегантного роз'єднання проблем, вам доведеться написати купу похідних властивостей та переконатися, що ви сумлінно дзвоните RaisePropertyChanged(() => DerivedProperty)під час встановлення базових властивостей, що може виявитися неприємним питанням технічного обслуговування.

Нижче наведено початковий перелік, який я зібрав за плюси і мінуси, що дозволяють переглядаючим моделям обробляти логіку перетворення та виконувати перетворення значень:

  • Плюси:
    • Менше загальних прив’язок, оскільки усуваються багатоперетворювачі
    • Менше магічних рядків (шляхи прив'язки + назви ресурсів конвертора )
    • Немає більше реєстрації кожного перетворювача (плюс підтримка цього списку)
    • Менше роботи для запису кожного перетворювача (не потрібно застосовувати інтерфейси чи кастинг)
    • Легко вводити залежності, щоб допомогти з перетвореннями (наприклад, кольорові таблиці)
    • Розмітка XAML менш докладна і легша для читання
    • Повторне використання конвертера все ще можливе (хоча планується певне планування)
    • Жодних загадкових проблем із DependencyProperty.Unset (проблема, яку я помітив із багатозначними перетворювачами)

* Прорички вказують на переваги, які зникають, якщо ви використовуєте розширення розмітки (див. Оновлення нижче)

  • Мінуси:
    • Більш міцне з'єднання між моделлю перегляду та видом (наприклад, властивості повинні відповідати таким поняттям, як видимість та щітки)
    • Більше загальних властивостей, щоб дозволити пряме відображення для кожного перегляду прив'язки
    • RaisePropertyChangedпотрібно викликати кожне похідне властивість (див. Оновлення 2 нижче)
    • Потрібно все ж покластися на перетворювачі, якщо перетворення базується на властивості елемента інтерфейсу

Отже, як ви, напевно, можете сказати, у мене є пекота в цьому питанні. Я дуже вагаюся, щоб піти по шляху рефакторингу лише, щоб зрозуміти, що процес кодування настільки ж неефективний і стомлюючи, чи використовую я перетворювачі значень чи піддаю численні властивості перетворення значення у моїй моделі перегляду.

Я пропускаю якісь плюси / мінуси? Для тих, хто спробував обидва засоби перетворення цінності, які ви вважали, що працюють краще для вас і чому? Чи є інші альтернативи? (Учні щось згадували про постачальників дескрипторів типів, але я не міг зрозуміти, про що вони говорять. Будь-яке розуміння щодо цього було б вдячно.)


Оновлення

Сьогодні я з'ясував, що можна використовувати щось, що називається "розширення розмітки", щоб усунути необхідність реєструвати перетворювачі значень. Насправді це не тільки позбавляє від необхідності їх реєстрації, але фактично забезпечує інтелісенс для вибору перетворювача під час введення Converter=. Ось стаття, яка мене розпочала: http://www.wpftutorial.net/ValueConverters.html .

Можливість використовувати розширення розмітки дещо змінює баланс у моїх переліках та переліках та обговоренні вище (див. Підкреслення).

В результаті цього відкриття я експериментую з гібридною системою, де я використовую перетворювачі BoolToVisibilityі те, що я називаю, MatchToVisibilityі модель перегляду для всіх інших перетворень. MatchToVisibility - це в основному перетворювач, який дозволяє мені перевірити, чи пов'язане значення (як правило, перерахунок) відповідає одному або більше значень, зазначених у XAML.

Приклад:

Visibility="{Binding Status, Converter={vc:MatchToVisibility
            IfTrue=Visible, IfFalse=Hidden, Value1=Finished, Value2=Canceled}}"

В основному, це робиться, щоб перевірити, чи статус закінчено чи скасовано. Якщо це так, то видимість отримує значення "Видиме". В іншому випадку він отримує набори на "Прихований". Це виявилося дуже поширеним сценарієм, і цей конвертер врятував мені близько 15 властивостей на моїй моделі перегляду (плюс пов'язані з ним заяви RaisePropertyChanged). Зауважте, що під час введення Converter={vc:"MatchToVisibility" відображається в неперервному меню. Це помітно знижує ймовірність помилок і робить перетворювачі значень менш стомлюючими (вам не потрібно пам’ятати і не шукати ім'я потрібного конвертора значень).

Якщо вам цікаво, я вставлю код нижче. Однією з важливих особливостей даної реалізації MatchToVisibilityє те , що він перевіряє, якщо оцінка вартості є enum, і , якщо він є, він перевіряє , щоб переконатися Value1, Value2і т.д., також перерахувань одного і того ж типу. Це забезпечує перевірку під час проектування та час виконання того, чи будь-яке значення значення перерахувань введено неправильно. Щоб покращити це до перевірки часу компіляції, ви можете скористатися наступним (я ввів це вручну, тому, будь ласка, пробачте мене, якщо я допустив помилки):

Visibility="{Binding Status, Converter={vc:MatchToVisibility
            IfTrue={x:Type {win:Visibility.Visible}},
            IfFalse={x:Type {win:Visibility.Hidden}},
            Value1={x:Type {enum:Status.Finished}},
            Value2={x:Type {enum:Status.Canceled}}"

Хоча це безпечніше, це просто занадто багатослівно, щоб воно того вартувало. Я б також міг просто використовувати властивість моделі перегляду, якщо я збираюся це зробити. У будь-якому разі я вважаю, що перевірка часу проектування є цілком адекватною сценаріям, які я намагався до цього часу.

Ось код для MatchToVisibility

[ValueConversion(typeof(object), typeof(Visibility))]
public class MatchToVisibility : BaseValueConverter
{
    [ConstructorArgument("ifTrue")]
    public object IfTrue { get; set; }

    [ConstructorArgument("ifFalse")]
    public object IfFalse { get; set; }

    [ConstructorArgument("value1")]
    public object Value1 { get; set; }

    [ConstructorArgument("value2")]
    public object Value2 { get; set; }

    [ConstructorArgument("value3")]
    public object Value3 { get; set; }

    [ConstructorArgument("value4")]
    public object Value4 { get; set; }

    [ConstructorArgument("value5")]
    public object Value5 { get; set; }

    public MatchToVisibility() { }

    public MatchToVisibility(
        object ifTrue, object ifFalse,
        object value1, object value2 = null, object value3 = null,
        object value4 = null, object value5 = null)
    {
        IfTrue = ifTrue;
        IfFalse = ifFalse;
        Value1 = value1;
        Value2 = value2;
        Value3 = value3;
        Value4 = value4;
        Value5 = value5;
    }

    public override object Convert(
        object value, Type targetType, object parameter, CultureInfo culture)
    {
        var ifTrue = IfTrue.ToString().ToEnum<Visibility>();
        var ifFalse = IfFalse.ToString().ToEnum<Visibility>();
        var values = new[] { Value1, Value2, Value3, Value4, Value5 };
        var valueStrings = values.Cast<string>();
        bool isMatch;
        if (Enum.IsDefined(value.GetType(), value))
        {
            var valueEnums = valueStrings.Select(vs => vs == null ? null : Enum.Parse(value.GetType(), vs));
            isMatch = valueEnums.ToList().Contains(value);
        }
        else
            isMatch = valueStrings.Contains(value.ToString());
        return isMatch ? ifTrue : ifFalse;
    }
}

Ось код для BaseValueConverter

// this is how the markup extension capability gets wired up
public abstract class BaseValueConverter : MarkupExtension, IValueConverter
{
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return this;
    }

    public abstract object Convert(
        object value, Type targetType, object parameter, CultureInfo culture);

    public virtual object ConvertBack(
        object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Ось метод розширення ToEnum

public static TEnum ToEnum<TEnum>(this string text)
{
    return (TEnum)Enum.Parse(typeof(TEnum), text);
}

Оновлення 2

Оскільки я опублікував це запитання, я натрапив на проект із відкритим кодом, який використовує "Іллінг" для введення коду NotifyPropertyChanged для властивостей та залежних властивостей. Це робить реалізацію бачення Джоша Сміта про модель зору як "перетворювача цін на стероїди" абсолютним вітерцем. Ви можете просто скористатися "Властивості, що реалізуються автоматично", а ткацьке зробить все інше.

Приклад:

Якщо я введіть цей код:

public string GivenName { get; set; }
public string FamilyName { get; set; }

public string FullName
{
    get
    {
        return string.Format("{0} {1}", GivenName, FamilyName);
    }
}

... ось що складається:

string givenNames;
public string GivenNames
{
    get { return givenName; }
    set
    {
        if (value != givenName)
        {
            givenNames = value;
            OnPropertyChanged("GivenName");
            OnPropertyChanged("FullName");
        }
    }
}

string familyName;
public string FamilyName
{
    get { return familyName; }
    set 
    {
        if (value != familyName)
        {
            familyName = value;
            OnPropertyChanged("FamilyName");
            OnPropertyChanged("FullName");
        }
    }
}

public string FullName
{
    get
    {
        return string.Format("{0} {1}", GivenName, FamilyName);
    }
}

Це величезна економія кількості коду, який потрібно вводити, читати, прокручувати минуле тощо. Що ще важливіше, це позбавляє вас від необхідності з'ясувати, які ваші залежності. Ви можете додати нові "властивості отримує" на кшталт, FullNameне вимагаючи кропіткого просування по ланцюгу залежностей, щоб додавати RaisePropertyChanged()дзвінки.

Як називається цей проект з відкритим кодом? Оригінальна версія називається "NotifyPropertyWeaver", але власник (Simon Potter) з тих пір створив платформу під назвою "Fody" для розміщення цілого ряду ткачів IL. Еквівалент NotifyPropertyWeaver на цій новій платформі називається PropertyChanged.Fody.

Якщо ви хочете скористатись NotifyPropertyWeaver (який трохи простіше встановити, але не обов’язково оновлюватиметься в майбутньому за виправленнями помилок), ось веб-сайт проекту: http://code.google.com/p/ notifypropertyweaver /

Так чи інакше, ці рішення ІЛ-ткача повністю змінюють обчислення в дискусії між моделлю перегляду стероїдів проти перетворювачів цінності.


Лише зауваження: BooleanToVisibilityбере одне значення, пов’язане з видимістю (true / false) і переводить його в інше. Це виглядає як ідеальне використання ValueConverter. З іншого боку, MatchToVisibilityце кодування ділової логіки в View(які типи елементів повинні бути видимими). На мій погляд, цю логіку слід відсунути вниз ViewModel, а то й далі, до того, що я називаю EditModel. Те, що може бачити користувач, має бути тестуваним.
Скотт Вітлок

@Scott, це хороший момент. Додаток, над яким я зараз працюю, насправді не є «діловим» додатком, де користувачі мають різні рівні дозволів, тому я не замислювався. MatchToVisibilityЗдавалося, зручним способом включити деякі прості перемикачі режимів (у мене є один перегляд, зокрема, з тоною деталей, які можна включати та вимикати. У більшості випадків розділи представлення навіть позначені (з x:Name), щоб відповідати режиму вони відповідають.) Мені насправді не прийшло в голову, що це "ділова логіка", але я дам вам твій коментар.
devuxer

Приклад: скажімо, у вас була стереосистема, яка могла знаходитись у режимі радіо, компакт-дисків або MP3. Припустимо, що в різних частинах інтерфейсу користувача є візуальні зображення, що відповідають кожному режиму. Ви можете (1) дозволити перегляду вирішити, яка графіка відповідає якому режиму, і відповідно включити / вимкнути їх, (2) виставити властивості моделі перегляду для кожного значення режиму (наприклад, IsModeRadio, IsModeCD) або (3) експонувати властивості моделі перегляду для кожного графічного елемента / групи (наприклад, IsRadioLightOn, IsCDButtonGroupOn). (1) здавалося, що це природно підходить моєму погляду, оскільки він уже має поінформованість про режим. Що ви думаєте в цьому випадку?
devuxer

Це найдовше запитання, яке я коли-небудь бачив у цілому SE! :]
trejder

Відповіді:


10

Я використовував ValueConvertersв одних випадках і вкладав логіку ViewModelв інші. Моє відчуття таке, що a ValueConverterстає частиною Viewшару, тому якщо логіка дійсно є частиною того, що Viewвиклали там, інакше покладіть його на ViewModel.

Особисто я не бачу проблеми з ViewModelвирішенням таких Viewспецифічних понять, як Brushes, тому що в моїх програмах ViewModelіснує лише тестова і біндіруемая поверхня для View. Однак деякі люди вкладають багато ділової логіки в ViewModel(я цього не роблю), і в цьому випадку ViewModelце більше нагадує частину їхнього бізнес-шару, тому в такому випадку я не хотів би, щоб там були специфічні для WPF речі.

Я віддаю перевагу іншому поділу:

  • View- WPF речі, іноді нестабільні (наприклад, XAML та код-позаду), але також ValueConverters
  • ViewModel - тестовий і сполучний клас, який також є специфічним для WPF
  • EditModel - частина бізнес-шару, яка представляє мою модель під час маніпуляцій
  • EntityModel - частина бізнес-шару, яка представляє мою модель як збережену
  • Repository- відповідальність за збереження EntityModelбази даних

Отже, як я це роблю, я мало що використовую для ValueConverters

Те, як я відійшов від деяких ваших "Кон", - це зробити своє ViewModelдуже загальним. Наприклад, один, який ViewModelя маю, називається ChangeValueViewModelреалізує властивість Label і властивість Value. На що Viewє Labelщо пов'язується властивості ярлика і TextBoxщо пов'язується властивості Value.

Потім у мене є ChangeValueViewякий є DataTemplateвідключенням ChangeValueViewModelтипу. Щоразу, коли WPF бачить, що ViewModelце застосовується View. Моїй конструктор ChangeValueViewModelприймає логіку взаємодії, необхідну для оновлення свого стану від EditModel(як правило, просто проходження в a Func<string>), і дії, яку він повинен здійснити, коли користувач редагує значення (просто Actionте, що виконує певну логіку в EditModel).

Батько ViewModel(для екрана) бере EditModelсвій конструктор і просто створює відповідні елементарні елементи ViewModel, такі як ChangeValueViewModel. Оскільки батько ViewModelвводить дію, яку слід здійснити, коли користувач робить будь-які зміни, він може перехопити всі ці дії та вчинити інші дії. Тому введена дія редагування ChangeValueViewModelможе виглядати так:

(string newValue) =>
{
    editModel.SomeField = newValue;
    foreach(var childViewModel in this.childViewModels)
    {
        childViewModel.RefreshStateFromEditModel();
    }
}

Очевидно, що foreachцикл можна відновити в іншому місці, але що це робиться - це вжити заходів, застосувати його до моделі, тоді (якщо припустити, що модель оновив стан якимось невідомим способом), повідомляє всім дітям ViewModelпіти і отримати свій стан знову модель. Якщо держава змінилася, вони відповідають за виконання їх PropertyChangedподій, у міру необхідності.

Це досить добре обробляє взаємодію між, скажімо, вікном списку та панеллю деталей. Коли користувач вибирає новий вибір, він оновлює EditModelвибір, а також EditModelзмінює значення властивостей, викритих на панелі деталей. У ViewModelдітей, які відповідають за відображення інформації про деталі панелі автоматично отримати повідомлення , що вони повинні перевірити нові значення, і якщо вони змінилися, вони стріляють свої PropertyChangedподії.


/ кивок. Це дуже схоже на те, як виглядають мої.
Ян

+1. Дякую за вашу відповідь, Скотт, у мене майже такі ж шари, як і ви, і я також не вкладаю бізнес-логіку у модель перегляду. (Для запису я використовую EntityFramework Code спочатку, і у мене є сервісний рівень, який перекладається між моделями перегляду та моделями сутності, і навпаки.) Отже, враховуючи це, я думаю, ви говорите, що це не велика вартість щоб розмістити всю / більшість логіки перетворення в шарі моделі перегляду.
devuxer

@DanM - Так, я згоден. Я вважаю за краще перетворення в ViewModelшарі. Не всі згодні зі мною, але це залежить від того, як працює ваша архітектура.
Скотт Вітлок

2
Я збирався сказати +1 після прочитання першого абзацу, але потім я прочитав ваш другий і категорично не згоден із тим, що в ViewModels вводити специфічний для перегляду код. Єдиним винятком є ​​те, якщо ViewModel створений спеціально для того, щоб за загальним переглядом (наприклад, CalendarViewModelдля CalendarViewUserControl або a DialogViewModelдля a DialogView). Це тільки моя думка, хоча :)
Рейчел

@Rachel - ну, якби ти продовжував читати минулий мій абзац, ти побачив би, що саме це я і робив. :) У мене немає ділової логіки ViewModel.
Скотт Вітлок

8

Якщо конверсія є чимось пов’язаним із переглядом, наприклад, вирішенням видимості об'єкта, визначенням зображення, яке потрібно відображати, або з'ясуванням, який колір пензля використовувати, я завжди розміщую свої перетворювачі у поданні.

Якщо його пов’язано з бізнесом, наприклад, визначити, чи слід поле маскувати, або якщо користувач має дозвіл на виконання дії, перетворення відбувається в моєму ViewModel.

З ваших прикладів, я думаю , що вам не вистачає великий шматок WPF: DataTriggers. Здається, ви використовуєте перетворювачі для визначення умовних значень, але перетворювачі дійсно повинні бути для перетворення одного типу даних в інший.

У вашому прикладі вище

Приклад: скажімо, у вас була стереосистема, яка могла знаходитись у режимі радіо, компакт-дисків або MP3. Припустимо, що в різних частинах інтерфейсу є відеозображення, що відповідають кожному режиму. Ви можете (1) дозволити перегляду вирішити, яка графіка відповідає якому режиму, і відповідно увімкнути / вимкнути їх, (2) виставити властивості моделі перегляду для кожного значення режиму (наприклад, IsModeRadio, IsModeCD) або (3) експонувати властивості моделі перегляду для кожного графічного елемента / групи (наприклад, IsRadioLightOn, IsCDButtonGroupOn). (1) здавалося, що це природно підходить моєму погляду, оскільки він уже має поінформованість про режим. Що ви думаєте в цьому випадку?

Я б використовував a, DataTriggerщоб визначити, яке зображення потрібно відображати, а не a Converter. Конвертер призначений для перетворення одного типу даних в інший, тоді як тригер використовується для визначення деяких властивостей на основі значення.

<Style x:Key="RadioImageStyle">
    <Setter Property="Source" Value="{StaticResource RadioImage}" />
    <Style.Triggers>
        <DataTrigger Binding="{Binding Mode}" Value="CD">
            <Setter Property="Source" Value="{StaticResource CDImage}" />
        </DataTrigger>
        <DataTrigger Binding="{Binding Mode}" Value="MP3">
            <Setter Property="Source" Value="{StaticResource MP3Image}" />
        </DataTrigger>
    </Style.Triggers>
</Style>

Єдиний раз, коли я б розглядав можливість перетворювача для цього, це якщо зв'язане значення насправді містило дані зображення, і мені потрібно було перетворити його у тип даних, який інтерфейс користувача міг би зрозуміти. Наприклад, якщо джерело даних містило властивість, що викликається ImageFilePath, тоді я б розглядав можливість перетворювача для перетворення рядка, що містить розташування файлу зображення, до BitmapImageякого можна використовувати як джерело для мого зображення

<Style x:Key="RadioImageStyle">
    <Setter Property="Source" Value="{Binding ImageFilePath, 
            Converter={StaticResource StringPathToBitmapConverter}}" />
</Style>

Кінцевий результат - у мене є одна область імен бібліотеки, повна загальних перетворювачів, які перетворюють один тип даних в інший, і мені рідко доводиться кодувати новий перетворювач. Бувають випадки, коли я хочу отримати конвертери для конкретних перетворень, але вони досить рідкі, що я не заперечую їх писати.


+1. Ви піднімаєте кілька хороших балів. Раніше я використовував тригери, але в моєму випадку я не вимикаю джерела зображень (які є властивістю), я вимикаю цілі Gridелементи. Я також намагаюся робити такі речі, як встановити пензлі для переднього плану / фону / обведення на основі даних моєї моделі перегляду та конкретної палітри кольорів, визначеної у конфігураційному файлі. Я не впевнений, що це чудово підходить ні для тригера, ні для перетворювача. Єдиною проблемою, з якою у мене зараз є розміщення більшості логік перегляду у моделі перегляду, є з'єднання всіх RaisePropertyChanged()дзвінків.
devuxer

@DanM Я б насправді робив усі ці речі в DataTrigger, навіть вимикаючи елементи Grid. Зазвичай я розміщую місце, ContentControlде повинен бути мій динамічний контент, і замінюю його на ContentTemplateтригері. У мене є приклад за наступним посиланням, якщо вас цікавить (прокрутіть униз до розділу з заголовком Using a DataTrigger) rachel53461.wordpress.com/2011/05/28/…
Rachel

Раніше я використовував шаблони даних і контроль вмісту, але мені ніколи не потрібні тригери, тому що я завжди мав унікальну модель перегляду для кожного перегляду. У будь-якому випадку, ваша техніка має ідеальний сенс і є досить елегантною, але теж дуже багатослівною. З MatchToVisibility його можна було б скоротити до цього: <TextBlock Text="I'm a Person" Visibility={Binding ConsumerType, Converter={vc:MatchToVisibility IfTrue=Visible, IfFalse=Hidden, Value1=Person}}"та<TextBlock Text="I'm a Business" Visibility={Binding ConsumerType, Converter={vc:MatchToVisibility IfTrue=Visible, IfFalse=Hidden, Value1=Business}}"
devuxer

1

Це залежить від того, що ви тестуєте , якщо що.

Немає тестів: intermix Перегляд коду з / за ViewModel за бажанням (ви завжди можете рефактор пізніше)
Тести на ViewModel та / або нижчі: використовуйте перетворювачі.
Тести на шари моделі та / або нижче: intermix Перегляд коду з / за ViewModel за бажанням

ViewModel абстрагує модель для перегляду . Особисто я б використав ViewModel для щіток тощо і пропустив перетворювачі. Перевірте на шарах (іх), де дані перебувають у " найчистішому " вигляді (тобто модельні шари ).


2
Цікаві моменти щодо тестування, але, мабуть, я не бачу, як наявність логіки перетворювача у моделі перегляду шкодить передбачуваності моделі перегляду? Я, звичайно, не пропоную вводити фактичні елементи управління інтерфейсом у модель перегляду. Просто переглянути специфічні властивості , такі як Visibility, SolidColorBrush, і Thickness.
devuxer

@DanM: Якщо ви використовуєте підхід Першого перегляду , то жодних проблем . Тим НЕ менше, деякі використовують ViewModel-перший підхід , де ViewModel посилається на вигляд, це може бути проблематично .
Джейк Бергер

Привіт Джей, безумовно, перший підхід. Перегляд нічого не знає про модель перегляду, за винятком назв властивостей, з якими він повинен пов'язуватися. Дякуємо за продовження роботи. +1.
devuxer

0

Це, ймовірно, не вирішить усі згадані вами проблеми, але слід врахувати два моменти:

По-перше, вам потрібно розмістити код перетворювача десь у вашій першій стратегії. Чи вважаєте ви цю частину перегляду чи модель перегляду? Якщо це частина перегляду, чому б не розмістити властивості, характерні для перегляду, у представленні замість моделі перегляду?

По-друге, це звучить як ваше неконвертерне проектування намагається змінити фактичні властивості об'єктів, які вже існують. Здається, вони вже реалізують INotifyPropertyChanged, то чому б не використовувати створений для перегляду об’єкт обгортки для прив’язки? Ось простий приклад:

public class RealData
{
    private bool mIsInteresting;
    public bool IsInteresting
    {
        get { return mIsInteresting; }
        set 
        {
            if (mIsInteresting != null) 
            {
                mIsInteresting = value;
                RaisePropertyChanged("IsInteresting");
            }
        }
    }
}

public class RealDataView
{
    private RealData mRealData;

    public RealDataView(RealData data)
    {
        mRealData = data;
        mRealData.PropertyChanged += OnRealDataPropertyChanged;
    }

    public Visibility IsVisiblyInteresting
    {
       get { return mRealData.IsInteresting ? Visibility.Visible : Visibility.Hidden; }
       set { mRealData.IsInteresting = (value == Visibility.Visible); }
    }

    private void OnRealDataPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "IsInteresting") 
        {
            RaisePropertyChanged(this, "IsVisiblyInteresting");
        }
    }
}

Я не хотів би мати на увазі, що я змінюю властивості моєї сутності моделі безпосередньо в моделі перегляду чи перегляду. Модель перегляду, безумовно, відрізняється від шару моєї сутності. Насправді робота, яку я робив дотепер, стосувалася переглядів лише для читання. Це не означає, що моя програма не передбачає жодного редагування, але я не бачу перетворень на елементах управління, що використовуються для редагування (тому припускаю, що всі прив'язки є односторонніми, за винятком виділень у списках). Хоча хороша думка щодо "переглядів даних". Це була концепція, піднята в публікації учнів XAML, про яку я згадував у верхній частині свого питання.
devuxer

0

Іноді добре використовувати перетворювач значень, щоб скористатися віртуалізацією.

Приклад цього - в проекті, де нам довелося відображати дані в масках для сотень тисяч комірок в сітці. Коли ми розшифровували бітові маски в моделі перегляду для кожної осередку, програма завантажувалася занадто довго, щоб завантажуватись.

Але коли ми створили конвертер значень, який розшифрував одну клітинку, програма завантажилася за частину часу і була так само чутлива, тому що перетворювач викликається лише тоді, коли користувач дивиться на певну клітинку (і її потрібно було б лише викликати максимум тридцять разів у будь-який час, коли користувач змінює свій погляд на сітку).

Я не знаю, наскільки скарга MVVM на це рішення, але це скоротило час завантаження на 95%.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.