Статьи

Pivot: практический пример, часть 4 — привязка данных

Это четвертая из пяти статей, в которых рассказывается о реализации простого, но практичного приложения Pivot, называемого Stock Tracker . В предыдущей статье обсуждались «веб-запросы», родные средства Pivot для связи с удаленными службами данных. Этот раздел посвящен привязке данных. Последняя статья будет посвящена поддержке локализации в Pivot.

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

В Pivot привязка данных контролируется методами load () и store () класса Component:

public void load(Dictionary<String, Object> context) {...}
public void store(Dictionary<String, Object> context) {...}

Аргумент Dictionary, передаваемый этим методам, предоставляет «контекст связывания»: набор пар имя / значение, представляющих данные, с которыми связаны компоненты. Каждому связываемому компоненту может быть назначен «ключ связывания», который связывает компонент со значением в контексте. Реализации по умолчанию ничего не делают; Компоненты, поддерживающие привязку данных, переопределяют их, чтобы импортировать данные в компонент из контекста во время загрузки и экспортировать данные из компонента в контекст во время хранения.

Привязка данных чаще всего поддерживается компонентами, которые принимают и представляют пользовательский ввод (например, текстовое поле), но она также может быть реализована компонентами только для чтения, такими как метки и индикаторы выполнения. Большинство компонентов позволяют вызывающей стороне связываться только с одним значением (например, свойством «text» метки), хотя некоторые поддерживают дополнительные привязки (например, представление с проверенным списком, позволяющее вызывающей стороне связываться с обоими проверенными элементами). и выбранные штаты).

Важно отметить, что невозможно привязать к контейнеру напрямую. Однако контейнеры могут действовать как вложенные контексты связывания — когда ключу привязки назначается контейнер, предполагается, что он указывает на вложенный экземпляр Dictionary, представляющий подчиненный контекст связывания. Это позволяет, например, легко сопоставлять сложные графы объектов JSON, возвращаемые из веб-запроса, с набором связанных с данными компонентов, размещенных в нетривиальном макете.

Привязка данных в демонстрации Stock Tracker

Демонстрация Stock Tracker не настолько сложна. Он использует один плоский контекст привязки для заполнения полей в подробном представлении цитаты. Контекст связывания фактически представляет собой данные строки, извлеченные из веб-запроса для выбранного запаса. Вот почему мы запросили больше данных, чем нам казалось, в запросе GET: дополнительные поля используются для заполнения данных в подробной форме.

Связанные компоненты в данном случае являются метками только для чтения — Stock Tracker использует одностороннюю привязку для сопоставления полученных данных цитаты со свойством text каждого из них. Мы указали имя ключа для использования для каждой метки в файле stocktracker.detail.wtkx:

...
<Label label="%value" textKey="value"/>
...

Фактическая привязка происходит, когда выбор изменяется в табличном представлении; как мы видели в разделе обработки событий, обработчик изменения выбора вызывает метод refreshDetail () в ответ на событие изменения выбора. Код для этого метода выглядит следующим образом:

private void refreshDetail() {
int firstSelectedIndex = stocksTableView.getFirstSelectedIndex();
removeSymbolsButton.setEnabled(firstSelectedIndex != -1);

StockQuote stockQuote = null;
detailChangeLabel.getAttributes().remove(Form.FLAG_ATTRIBUTE);

if (firstSelectedIndex != -1) {
int lastSelectedIndex = stocksTableView.getLastSelectedIndex();

if (firstSelectedIndex == lastSelectedIndex) {
List tableData = (List )stocksTableView.getTableData();
stockQuote = tableData.get(firstSelectedIndex);

if (stockQuote.getChange() < 0) {
detailChangeLabel.getAttributes().put(Form.FLAG_ATTRIBUTE,
new Form.Flag(Alert.Type.ERROR));
}
}
}

StockQuoteView stockQuoteView = new StockQuoteView(stockQuote);
detailRootPane.load(stockQuoteView);
}

Метод делает следующее:

  • Получает первый выбранный индекс в таблице (если выбрано более одного элемента, мы не хотим ничего показывать в деталях)

  • Удаляет атрибут «flag» из метки подробного изменения (контейнер Form позволяет вызывающей стороне помечать поля значением флага для использования при проверке данных или просто в простом уведомлении — здесь флаг используется для указания отрицательного изменения в запасе стоимость)

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

  • Если процент изменения отрицательный, рядом с меткой сведений отображается красный флаг «ошибка».

  • Оборачивает данные строки в экземпляр StockQuoteView и вызывает load () , заполняя форму данными из выбранной цитаты.

StockQuoteView — это «декоратор» — он реализует тот же интерфейс словаря, что и фактические данные строки, но, как и средства визуализации ячеек, используемые в табличном представлении, он обеспечивает форматирование и представление данных в удобочитаемом виде в подробном представлении.

Обратите внимание, что метод load () фактически вызывается в родительском контейнере формы, а не в самой форме. Это потому, что мы также хотим привязать к ярлыку, который содержит название компании, которая не является дочерней по форме. Вложенный контейнер автоматически не подразумевает существование подконтекста — подконтексты создаются только тогда, когда вложенному контейнеру назначается собственный ключ связывания. Поскольку ключ связывания для него не определен, форма просто наследует контекст связывания, который был передан его родителю.