Как избежать привязки данных/событий ад на сложном экране?

Это скорее вопрос архитектуры/дизайна.

В прошлом я работал с несколькими проектами в WPF/Windows Forms и т.д., которые имеют сложные экраны с большим количеством полей, и эти поля связаны друг с другом (их значения зависят друг от друга с некоторой логикой).

Эти проекты я реализовал после того, как они были реализованы, и я обнаружил, что много событий/данных связывают ад. Я имею в виду, что потому, что все эти поля зависят от других, они внедрили INotifyPropertyChanged, а другие поля измененный в результате. Это приводит к тому, что те же поля обновляются 5-6 раз, когда загружается экран, и порядок заполнения полей вызывает ужасные ошибки. (Например, дата была задана перед типом задания, а не после типа задания, поэтому я получаю другую плату за работу.)

Чтобы усугубить ситуацию, некоторые хаки реализованы в событиях пользовательского интерфейса (например, DropDown изменен на поле обновления X), а другие - в модели домена, к которой привязан интерфейс.

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

Ответ 1

Я постараюсь максимально упростить логику бизнеса из средств настройки свойств.

Прежде всего, если для одного вычисления требуется несколько свойств, я бы написал один метод, который выполняет расчет, и вызовет этот метод, когда это необходимо. Например. если все различные комбинации значений свойств имеют смысл, можно просто вызвать метод в сеттерах каждого свойства, убедившись, что один и тот же код запускается в любое время, когда изменяется одно из свойств. Если вы можете оценивать специальные комбинации значений свойств, вы можете либо реализовать команду, либо позволить пользователю решить, когда вычислять полученные изменения, или вы могли бы предоставить обратную связь через проверку и только оценить изменения свойств, если комбинация действительна. Если есть несколько взаимозависимых свойств, я часто использую переменную "ChangeInitiator", чтобы указать, какое свойство изменилось, так что в методе расчета ясно, какое свойство отвечает за изменение и какие другие должны измениться в результате. В принципе, это то же самое, что делать одну часть вычисления в каждом устройстве настройки свойств, но я нахожу, что это помогает мне вести обзор вещей, если разные части отношений все в одном методе.

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

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

Ответ 2

У нас довольно сложные пользовательские интерфейсы (в том числе несколько связанных полей разных типов, например, строка Row в DataGrid), и шаблон MVVM работал очень хорошо для нас. Все свойства, идущие от модели и подвергается воздействию View, которые имеют сложную логику связанную являются "завернуты" эквивалентной собственностью в ViewModel, который не имеет никакой поддержки поля, а скорее указует прямо на модель:

public class SomeComplexViewModel
{

    public SomeModel Model {get;set;}

    public string SomeCrazyProperty
    {
       get
       {
          return Model.SomeCrazyProperty;
       }
       {
          Model.SomeCrazyProperty = value;
          //... Some crazy logic here, potentially modifying some other properties as well.
       }
    }
}

<TextBox Text="{Binding SomeCrazyProperty}"/>

Это устраняет проблему "начального значения", поскольку начальное значение, считанное привязкой, фактически является реальным значением, исходящим от модели, и поэтому логика, размещенная в Setter, выполняется только при необходимости.

Затем для фиктивных свойств (которые не имеют логики) мы привязываемся непосредственно от представления к модели:

<TextBox Text="{Binding Model.SomeRegularProperty}"/>

Это уменьшает раздувание в ViewModel.

Что касается событий в коде позади, я полностью избегаю этого. Мой код за файлами почти всегда один InitializeComponent() и ничего больше.

В коде, находящемся сзади (например, в анимации и т.д.), находится только логика просмотра. Специфическая логика не может быть выполнена непосредственно в XAML или проще сделать в коде (что больше не так).

Изменить:

Важно отметить, что возможности привязки winforms - это шутка по сравнению с XAML-based. может ли это быть причиной того, что вы видите эти ужасные беспорядки в этих проектах?