Как стать автором
Обновить
30
Карма
0
Рейтинг

Пользователь

  • Подписчики 41
  • Подписки 3

Инжекторы контекста xaml

Начнём с того, что код библиотеки полностью открыт и ничто не мешает вместо Service Locator использовать любой другой шаблон проектирования на ваш выбор.

Большинство современных unity-контейнеров реализуют Service Locator, но значимость состоит не в том, что Aero Framework предлагает его использовать по умолчанию, а в том, что в библиотеке введён принцип прямых инжекций, когда инжектировать вью-модель можно точечно в любое место визуального дерева.

Что касается вашего примера, то в статье предпологается, что в SettingsViewModel хранятся глобальные параметры для пользователя, как обычно и бывает в приложениях, но запросто можно использовать и динамически создаваемые вью-модели. Взгляните на пример к библиотеке: AppViweModel статическая, а TextViewModel нет, — и всё прекрасно работает.

Довелось написать немало завершённых приложений и никаких ощутимых проблем со всем этим не было ни разу. Решайте сами, использовать библиотеку или нет в вашей работе. На мой взгляд, основная её ценность в простоте и лаконичности получаемого кода, чего не хватало многим проектам, которые попадались автору на практике и использовали другие MVVM-фреймворки.

Инжекторы контекста xaml

Думаю, если вы хорошо подумаете над этим или другими каверзными вопросами, то и сами сможете на них ответить :)

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

Расширения привязки и xaml-разметки на примере локализации

У меня, похоже, находит инжекции, или вы что-то другое имеете в виду?

image

d:DesignInstance тоже работает и подсказки появляются.

    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    d:DataContext="{d:DesignInstance viewModels:AppViewModel, IsDesignTimeCreatable=True}"

Расширения привязки и xaml-разметки на примере локализации

Попробуйте починить примерно так <Grid d:DataContext="{d:DesignInstance viewModels:AppViewModel}">, возможно, подсказки появятся (сам не пробовал, поскольку обхожусь без них).

А решарперовский Find Usages прекрасно работает и по умолчанию.

Расширения привязки и xaml-разметки на примере локализации

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

Конечно, стоит признать многословность, но обычно интерфейс и разметка в мобильных Windows Store и Windows Phone 8.1 приложениях проще, чем на десктоп-платформах, поэтому на этот недостаток ещё можно закрыть глаза в виду тех преимуществ, которые можно реализовать.

Инжекторы контекста xaml

Стоит также упомянуть о том, что зачастую после загрузки представления возникает необходимость в обновлении каких-либо данных. Чтобы обойтись без бехаинд-кода в библиотеке Aero Framework предусмотрен механизм контекстных триггеров команд.

<View DataContext="{Store Key=viewModels:ProductsViewModel}">
    <Attacher.ContextTriggers>
        <ContextTrigger 
            EventName="Loaded" 
            UseEventArgsAsCommandParameter="False"
            Command="{Context Key=Refresh, StoreKey=viewModels:ProductsViewModel}"/>
    </Attacher.ContextTriggers>
...
</View>

public class ProductsViewModel : ContextObject, IExposable
{
    public Product CurrentProduct
    {
        get { return Get(() => CurrentProduct); }
        set { Set(() => CurrentProduct, value); }
    }

    public ContextSet<Product> Products { get; set; }

    public virtual void Expose()
    {
        Products = new ContextSet<Product>();
        
        this[() => CurrentProduct].PropertyChanged += (sender, args) => Context.Get("GoToProduct").RaiseCanExecuteChanged();
        
        this[Context.Get("GoToProduct")].CanExecute += (sender, args) => args.CanExecute = CurrentProduct != null;
        this[Context.Get("GoToProduct")].Executed += (sender, args) => Navigator.GoTo(args.Parameter);
        this[Context.Refresh].Executed += async (sender, args) =>
        {
            try
            {
                var products = await Bank.Current.GetProducts();
                CurrentProduct = null;
                Products.Clean();
                products.ForEach(p => Products.Add);
            }
            catch (Exception exception)
            {
                Error = Unity.App.Localize(exception.Message);
            }
        };
    }
}


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

Расширения привязки и xaml-разметки на примере локализации

Ещё на Windows Store и Windows Phone 8.1, к сожалению, допустим только такой вариант декларации из-за ограничений парсера разметки

<TextBlock>
    <TextBlock.Text>
        <local:Localizing Key=AppDescription>
    </TextBlock.Text>
</TextBlock>

Расширения привязки и xaml-разметки на примере локализации

Спасибо, немного позже посмотрю. По крайней мере, для WP8.1 расширения привязки у меня работали с префиксом local, то есть когда находились в основной сборке. Для WP7 и WP8 всё точно работает, это хорошо проверял.

Инжекторы контекста xaml

Ничто не запрещает контролировать время жизни вью-моделей, если это нужно. Более того, применияя подход с сериализацией, который описан в этой статье, можно выгружать и загружать вью-модели, сохраняя их состояние в произвольный момент (смотрите пример проекта к библиотеке).

Увеличения связности кода на практике не происходит никакого, скорее даже наоборот — она уменьшается, ведь не нужно использовать никаких дополнительных регистрации и код-бехаин логики. Насчёт тестирования то же самое.

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

На мой взгляд, самые обощённые и простые решения с минимальным количеством строк кода зачатую лучшие. Довелось завершить немало проектов, в том числе объёмных, а механизм прямых инжекций по-прежнему удивляет своим удобством и надёжностью.

Расширения привязки и xaml-разметки на примере локализации

Мне не доводилось заниматься локализацией на WinForms, поэтому благоразумнее об этом не спорить. Но с разработкой на этой платформе раньше сталкивался более-менее плотно, и, когда начал изучать WPF, вторая вызвала во мне ощутимое сопротивление обилием нетривиальных идей и подходов к разработке. Просто не понимал, зачем всё так сложно, ведь раньше хватало WinForms. Со временем же, когда проникся всеми этими мыслями и пришло их осознание, понял, что по мощности своей WPF очень далеко впереди и если что-то кажется трудноосуществимым, то это, скорее всего, от незнания или плохого понимания какого-либо механизма.

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

По-моему, если не ошибаюсь, существует и другой способ: задать каждому визуальному элементу уникальный ключ и по нему точечно менять его параметры. По крайней мере, с помощью attached property такое достаточно просто самому реализовать. Но к такому методу сам не прибегал, поскольку стилей всегда хватало.

Расширения привязки и xaml-разметки на примере локализации

Не нужно делать отдельных стилей на каждый контрол да и ещё для каждого языка. Вы забываете о наследовании свойств в стилях и их переопределении. В вашем случае достаточно всего лишь трёх: первый самый общий — для большинства кнопок, второй — для кнопок с шириной 10, третий — с шириной 20. В зависимости от языка вы применяете нужную тему, например, через MergedDictionaries. Это всё делается достаточно просто.

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

Расширения привязки и xaml-разметки на примере локализации

<Button Style={StaticResource CustomButtonStyle}/>

Эту запись достаточно сделать один раз. Для каждого же языка можно создать отдельный файл ресурсов и редактировать тему в нём, не затрагивая само представление. В простейшем случае потребуется перезагрузка представления для смены темы, но с помощью тех же Binding Extensions очень просто реализовать «горячую» смену во время работы программы.

<Button Style={Theme Key=CustomButtonStyle}/>

WPF отличается от WinPhone и тяжелее для изучения, но он на порядок мощнее и гораздо более гибкий, чем кажется сразу.

Расширения привязки и xaml-разметки на примере локализации

Чем плох такой способ для точечных изменений?

<Button Style={StaticResource CustomButtonStyle}/>

Расширения привязки и xaml-разметки на примере локализации

Для таких вещей прекрасно подходят стили (Styles). С их помощью запросто можно создавать различные темы приложения, в том числе и для определённых языковых локализаций.

Расширения привязки и xaml-разметки на примере локализации

=) Эта совсем старая, в ней ещё только зарождались некоторые идеи. Окончательную форму они приобрели уже в Aero Framework…

Расширения привязки и xaml-разметки на примере локализации

Да, сравнительно недавно была обзорная и довольно насыщенная статья об Aero Framework, где этот вопрос поднимался. Но из-за насыщенности не всем хватило терпения её изучить, поэтому было решено разобрать некоторые аспекты детальнее и в более доступном виде.

Расширения привязки и xaml-разметки на примере локализации

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

Кроме того, применение расширений привязки не ограничивается одной лишь локализацией, а позволяет делать гораздо более интересные вещи… Чтобы много не говорить, рекомендую просто ознакомиться с примером.

?.: когда свойства в C# могут быть равны null

Кстати, если так, то, насколько понимаю, конструкция

PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name")); 

разворачивается в

PropertyChanged == null ? null : PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Name")); 

?
То есть такой код не является потокобозопасным. Или события разворачиваются по-другому?

Ведь a?.b должно быть аналогично
var tmp = a;
tmp == null ? null : tmp .b

чтобы код остался потокобезопасным относительно переменной a…

P.S. Вспомнился забавный кусочек в одном из проектов (скорее всего, описка)
var handler = PropertyChanged;
if (handler != null) PropertyChanged(o, e);

?.: когда свойства в C# могут быть равны null

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

public event PropertyChangedEventHandler PropertyChanged = (sender, args) => { };

Просто, лаконично, потокобезопасно и удобно.

Context Model Pattern via Aero Framework

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

Информация

В рейтинге
5,532-й
Зарегистрирован
Активность