Являются ли преобразователи стоимости большим количеством проблем, чем они того стоят?

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» отображается в меню intellisense. Это заметно снижает вероятность ошибок и делает использование преобразователей значений менее утомительным (вам не нужно запоминать или искать нужное имя преобразователя значений).

Если вам интересно, я вставлю код ниже. Одной из важных особенностей данной реализации 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

С тех пор как я разместил этот вопрос, я натолкнулся на проект с открытым исходным кодом, который использует «IL-ткачество» для внедрения кода 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», но владелец (Саймон Поттер) с тех пор создал платформу «Fody» для размещения целой серии ткачей IL. Эквивалент NotifyPropertyWeaver под этой новой платформой называется PropertyChanged.Fody.

Если вы предпочитаете использовать NotifyPropertyWeaver (который немного проще в установке, но не обязательно будет обновляться в будущем после исправления ошибок), вот сайт проекта: http://code.google.com/p/ notifypropertyweaver /

В любом случае, эти решения для IL-ткача полностью меняют исчисление в споре между моделью представления о стероидах и преобразователях стоимости.

devuxer
источник
Просто примечание: BooleanToVisibilityпринимает одно значение, которое связано с видимостью (true / false), и переводит его в другое. Это похоже на идеальное использование ValueConverter. С другой стороны, MatchToVisibilityкодирует бизнес-логику в View(какие типы элементов должны быть видны). По моему мнению, эта логика должна быть направлена ​​вниз ViewModelили даже дальше в то, что я называю EditModel. То, что может видеть пользователь, должно быть проверено.
Скотт Уитлок
@ Скотт, это хороший момент. Приложение, над которым я сейчас работаю, на самом деле не является «деловым» приложением, где существуют разные уровни разрешений для пользователей, поэтому я не думал об этом. MatchToVisibilityказалось удобным способом включить некоторые простые переключатели режимов (у меня есть одно представление, в частности, с кучей деталей, которые можно включать и выключать. В большинстве случаев участки вида даже помечаются (с x:Name), чтобы соответствовать режиму они соответствуют.) Мне действительно не приходило в голову, что это «бизнес-логика», но я подумаю над вашим комментарием.
devuxer
Пример: скажем, у вас была стереосистема, которая могла работать в режиме радио, CD или MP3. Предположим, есть визуальные элементы, соответствующие каждому режиму в разных частях пользовательского интерфейса. Вы можете (1) позволить представлению решить, какие графики соответствуют какому режиму, и включить / выключить их соответствующим образом, (2) предоставить свойства модели представления для каждого значения режима (например, IsModeRadio, IsModeCD) или (3) открыть свойства в модели представления для каждого графического элемента / группы (например, IsRadioLightOn, IsCDButtonGroupOn). (1) казалось естественным подходом для моего взгляда, потому что он уже имеет режим осведомленности. Что вы думаете в этом случае?
devuxer
Это самый длинный вопрос, который я когда-либо видел во всей SE! :]
Трейдер

Ответы:

10

Я использовал ValueConvertersв некоторых случаях и поместил логику ViewModelв других. Я чувствую, что a ValueConverterстановится частью Viewслоя, поэтому, если логика действительно является частью, Viewтогда поместите его туда, иначе поместите его в ViewModel.

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

Я предпочитаю другое разделение:

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

Так, как я это делаю, у меня мало пользы ValueConverterс

Способ, которым я избавился от некоторых ваших «Конов», состоит в том, чтобы сделать мои ViewModelочень универсальными. Например, один из ViewModelних, названный, ChangeValueViewModelреализует свойство Label и свойство Value. На Viewесть Labelпривязка к свойству Label и TextBoxпривязка к свойству Value.

Я тогда , ChangeValueViewкоторый является DataTemplateключом офф ChangeValueViewModelтипа. Всякий раз, когда WPF видит, что ViewModelон применяет это View. Конструктор my ChangeValueViewModelберет логику взаимодействия, необходимую ему для обновления своего состояния EditModel(обычно просто передавая a Func<string>), и действие, которое необходимо выполнить, когда пользователь редактирует значение (просто a 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 First, и у меня есть сервисный слой, который переводит между моделями представления и моделями сущностей, и наоборот.) Итак, учитывая это, я полагаю, вы говорите, что не так уж много затрат поместить всю / большую часть логики преобразования в слой модели представления.
devuxer
@DanM - Да, я согласен. Я бы предпочел преобразование в ViewModelслое. Не все согласны со мной, но это зависит от того, как работает ваша архитектура.
Скотт Уитлок
2
Я собирался сказать +1 после прочтения первого параграфа, но потом я прочитал ваш второй и категорически не согласен с тем, чтобы помещать специфичный для вида код во ViewModels. Единственное исключение - если ViewModel создан специально для перехода за универсальный вид (например, CalendarViewModelдля CalendarViewUserControl или a DialogViewModelдля a DialogView). Это только мое мнение, хотя :)
Рэйчел
@ Рейчел - ну, если бы ты продолжил читать мой второй абзац, ты бы увидел, что это именно то, что я делал. :) У меня нет бизнес-логики ViewModel.
Скотт Уитлок
8

Если преобразование связано с просмотром, например, для определения видимости объекта, определения отображаемого изображения или определения цвета кисти, я всегда помещаю конвертеры в представление.

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

Из ваших примеров, я думаю , что вам не хватает большой кусок WPF: DataTriggers. Кажется, вы используете конвертеры для определения условных значений, но конвертеры действительно должны использоваться для преобразования одного типа данных в другой.

В вашем примере выше

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

Я бы использовал, DataTriggerчтобы определить, какое изображение отображать, а не 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/…
Рэйчел
Ранее я использовал шаблоны данных и элементы управления контентом, но мне никогда не требовались триггеры, потому что у меня всегда была уникальная модель представления для каждого представления. В любом случае, ваша техника имеет смысл и довольно элегантна, но она также очень многословна. С 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

Это зависит от того, что вы тестируете , если что.

Никаких тестов: перемешать Просмотреть код с ViewModel по желанию (вы всегда можете выполнить рефакторинг позже).
Тесты на ViewModel и / или ниже: используйте конвертеры.
Тесты на модельных слоях и / или ниже: смешанный просмотр кода с помощью ViewModel по желанию

ViewModel абстрагирует модель для представления . Лично я бы использовал ViewModel для кистей и т. Д. И пропускал конвертеры. Проверьте на слое (ях), где данные находятся в « чистом » виде (т. Е. Слои модели ).

Джейк Бергер
источник
2
Интересные моменты о тестировании, но я думаю, я не вижу, как использование логики преобразователя в модели представления вредит тестируемости модели представления? Я, конечно, не предлагаю помещать фактические элементы управления пользовательского интерфейса в модель представления. Просто просмотреть специфические свойства , такие как Visibility, SolidColorBrush, и Thickness.
devuxer
@DanM: Если вы используете подход View-first , то проблем нет . Тем не менее, некоторые используют 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%.

kleineg
источник