Есть ли способ делать не такие громозкие события? — c# wpf mvvm

1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд
Загрузка...

Вопрос:


Реализация в модели (в паттерне MVVM), каждой публичной переменной так:

    private bool _isGraphSet;
    public bool isGraphSet {
        get { return _isGraphSet; }
        private set { _isGraphSet = value; OnPropertyChanged("isGraphSet"); }
    }

Совсем не радует. Есть ли способы сделать все более человечески?

Автор вопроса: Fangog

Я бы предложил воспользоваться такой заготовкой.

Для начала, вы определяете базовый класс для ваших VM:

class VM : INotifyPropertyChanged
{
    protected bool Set<T>(ref T field, T value,
                          [CallerMemberName] string propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals(field, value))
            return false;

        field = value;
        RaisePropertyChanged(propertyName);
        return true;
    }

    protected void RaisePropertyChanged([CallerMemberName] string propertyName = null) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

    public event PropertyChangedEventHandler PropertyChanged;
}

Затем, в ваших VM-классах вы пишете вот так:

class GraphVM : VM
{
    private bool _isGraphSet;
    public bool isGraphSet {
        get { return _isGraphSet; }
        private set { Set(ref _isGraphSet, value); }
    }
}

К IL-weaver’ам наподобие Fody у меня неоднозначное отношение.

С одной стороны, AOP — это как бы хорошо и правильно.

С другой стороны, с Fody возникают баги. Хуже того, поскольку кодогенерация происходит за сценой, вряд ли получится влёгкую отлаживать связанные с этим проблемы, если что-то пошло не так: у вас ведь нету исходного кода!

Поэтому я бы подождал, пока в C# не будет реализовано replace/original (вот и вот), чтобы иметь над кодогенерацией полный контроль с возможностью пошаговой отладки.

Когда-то написал вот такой велосипед с использованием Castle DynamicProxy, который перехватывает вызов сеттера свойств в моделях представления и автоматически дергает PropertyChanged:

// Базовый класс для всех VM
public abstract class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    internal virtual void RaisePropertyChanged(string propertyName)
    {
        var evt = PropertyChanged;
        if (evt != null)
            evt(this, new PropertyChangedEventArgs(propertyName));
    }

    // Создание прокси к VM с помощью Castle.DynamicProxy
    public static T Create<T>() where T : ViewModelBase
    {
        var generator = new ProxyGenerator();
        return generator.CreateClassProxy<T>(new PropertyChangedInterceptor());
    }
}

// Тестовая VM. Ручного кода, как видите, совсем нет
public class TestViewModel : ViewModelBase
{
    public virtual string TestProperty { get; set; }
}

// Перехватчик обращений к свойствам
public class PropertyChangedInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        var proxy = invocation.Proxy as ViewModelBase;
        if (proxy != null && IsSetter(invocation.Method)) {
            // если происходит вызов сеттера свойства в VM
            // то получаем текущее значение свойства
            // и, если оно изменилось, вызываем PropertyChanged
            PropertyInfo property = GetPropertyBySetter(invocation.Method);
            object currentValue = property.GetValue(proxy, null);
            object newValue = invocation.Arguments[0];
            if (IsModified(currentValue, newValue)) {
                invocation.Proceed();
                proxy.RaisePropertyChanged(property.Name);
            }
        } else {
            invocation.Proceed();
        }
    }

    public static bool IsModified(object currentValue, object newValue)
    {
        if (ReferenceEquals(currentValue, null))
            return !ReferenceEquals(newValue, null);

        return !currentValue.Equals(newValue);
    }

    public static PropertyInfo GetPropertyBySetter(MethodInfo mi)
    {
        return mi.DeclaringType.GetProperties().FirstOrDefault(p => p.GetSetMethod() == mi);
    }

    public static bool IsSetter(MethodInfo mi)
    {
        return mi.DeclaringType.GetProperties().Any(p => p.GetSetMethod() == mi);
    }
}

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

  • Нельзя создать модели представления через конструктор, только через фабричный метод. Создание VM придется явно прописывать в code behind (DataContext = ViewModelBase.Create<TestViewModel>()). При этом конструктор сделать приватным нельзя (иначе возникнет исключение в ProxyGenerator).
  • Вызов property.GetValue(proxy, null) инициирует рекурсивный вызов метода Intercept (вроде бы обратиться напрямую к backing field автосвойства через рефлексию без хаков нельзя).
  • RaisePropertyChanged торчит наружу.
  • Проблемы с производительностью.

Источник

Вам также может быть интересно:

Использование вложенных маршрутов в React Router — javascript reactjs react-router
Вопрос: Для организации маршрутов в приложении использую React Router. <Route path="/" component={...}> <IndexRoute component={...}/> <Route path="user/:userId" component={...}> ...
Как с помощью Retrofit 2.0 отправить данные в JSON на сервер и получить ответ? — java android retrofit
Вопрос: Только начал читать про Retrofit 2.0 до этого использовал HttpURLConnection. Как я работаю с HttpURLConnection, формирую Json перевожу его в byte, ставлю header в ...
Не приходят push уведомления. FCM — android firebase android-notification
Вопрос: Появилась необходимость реализовать push уведомления. Прописал в манифесте сервис: <service android:name=".MyFirebaseMessagingService"> <intent-filter> ...
Принцип браузерной игры в линукс терминале — java linux terminal
Вопрос: Наткнулся на Java библиотеку CHARVA. И хотел бы уточнить у знающих людей, возможно ли на основе данной библиотеки сделать программу по принципу браузерной игры, но ...
Мерцание заблокированного экрана при выключенной подсветке в Debian 8 Gnome 3 — linux debian экран
Вопрос: На ноутбуке с Debian 8 Jessie и Gnome 3 имеется следующая проблема. При выключенном заблокированном экране сквозь него можно наблюдать, как весь экран становится белым, ...
Создание WCF клиента на готовый SOAP web сервер — c# wcf
Вопрос: Доброго времени суток. Появилась задача опрашивать web сервер с клиента на котором планируется написать WCF клиентскую часть. Информации про сервер очень мало (не знаю платформу ...
Безопасно ли удалить файл логов general_log.txt? — mysql
Вопрос: При выполнении запроса со вставкой данных большого объёма SQLyog начал вылетать с ошибкой: not enough memory application terminated В связи с этим я решила ...
Callback функции создания таблицы mysql в nodejs — mysql node.js callback
Вопрос: Есть функция, которая при запуске создает базу даных, function showDb() { pool.query("show databases like 'bt' ",function (err, ...
Как создать Adapter с неограниченным количеством строк и с неограниченным разным количеством столбцов в каждой строке — java android
Вопрос: Как создать Adapter с неограниченным количеством строк и с неограниченным разным количеством столбцов в каждой строке Автор вопроса: Salut Amigo Источник
Не могу передать байтовый массив в контроллер — c# asp.net-mvc entity-framework
Вопрос: У меня изображения храняться в бд в формате байтового массива, через форич отлично все выводит, но когда я хочу открыть страницу для работы с ...
proguard release error — java android mvp
Вопрос: Включил в проекте proguard, apk собирается, все хорошо, но приложение не работает) Proguard-rules.pro -keepattributes InnerClasses -keepattributes EnclosingMethod -keepattributes *Annotation* -dontoptimize # Keep Butterknife -keep class butterknife.** { *; } -dontwarn butterknife.internal.** -keep ...
Не отрабатывает page:update — javascript ruby-on-rails
Вопрос: Есть мой учебный проект на ruby. Делаю редактирование объектов с помощью JS. Сейчас работает так: Редактирую первый раз - всё нормально. Не обновляя страницу, ...
Как найти определенный символ в строке и удалить значение после него (и вместе с ним) Jquery — javascript html jquery
Вопрос: Здравствуйте, есть определенный набор строк, типа "L / Красный / 12345", как можно на странице найти их, и вырезать из них все что находится ...
Почему не работает wildcard module declaration? — typescript
Вопрос: Почему не работает такой способ декларации: declare module "*!text" {} ? Цель - использовать контент файла в переменной: import layout = require("/js/views/layouts/wnd.html!text"); или так: import layout from "/js/views/layouts/wnd.html!text"; Если ...
Как прервать 3rd-party код? — c# многопоточность .net-core
Вопрос: Есть 3rd-party код из библиотеки который "зависает" в ожидании где-то в работе с сетью. CancellationToken поддержки нет, таймаутов нет. Запускаю я его через: Task.Run(() => ...

Оставьте ответ

Ваш e-mail не будет опубликован. Обязательные поля помечены *