anna3276 Ответов: 2

Получение inotifypropertychanged для уведомления WPF между различными классами


У меня есть код WPF, в котором я не могу получать уведомления об обновлениях свойств.
Мой код должен иметь такую структуру:
A1… An (view .xaml & .xaml.cs) → B1… Bn (viewmodel .cs)
C (model .cs, my business logic)
D (utility .cs like constants etc)
E (custom object .cs, that will be x-times instantiated in my model C)

Диаграмма: диаграмма.bmp - Google Диск[^]

Что мне нужно, так это то, что только мой пользовательский объект реализует InotifyPropertyChanged, а не мои viewmodels, но я не мог получить уведомление Gui, когда устанавливал свойства моих пользовательских объектов (E) внутри моих моделей (C).
Вот пример того, что у меня есть до сих пор:

Что я уже пробовал:

В моем представлении .xaml:
<Button 
    Content="{Binding content, Mode=OneWay, NotifyOnTargetUpdated=True}"
    Command="{Binding action}" />

In my views .xaml.cs (код позади):
public A1()
{
    InitializeComponent();
    DataContext = new B1();
}

На мой взгляд, Models .cs:
public class B1 : C
{
    public string content => MyObject.Text;
    public ICommand action => MyObject.Pressed;
}

В моих моделях .cs:
public class C : D
{
    // here i instantiate my object(s) 
    public E MyObject = new E();

    // here comes my stuff
    ... 
}

public class D
{
    //my constants and wrappers
    ... 
}

И для моих пользовательских объектов (они будут x-раз созданы в моей модели):
public class E : INotifyPropertyChanged
{
    private string _text = String.Empty;
    public string Text
    {
        get => _text
        set => SetProperty(ref _text, value);
    }

    private readonly _DelegateCommand _pressed;
    public ICommand Pressed => _pressed;

    private void OnPressed(object commandParameter, string value)
    {
        Text = "newtext";
        _pressed.InvokeCanExecuteChanged();
    }

    private bool CanExecute(object commandParameter)
    {
        ... // some logic here
    }

    public E()
    {
        _pressed = new _DelegateCommand(OnPressed, CanExecute);
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public bool SetProperty<T>(ref T field, T newValue, [CallerMemberName]string propertyName = null)
    {
        if (!EqualityComparer<T>.Default.Equals(field, newValue))
        {
            field = newValue;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            return true;
        }
        return false;
    }
}

Может ли кто-нибудь помочь мне в этом, пожалуйста?
Заранее большое спасибо за любые подсказки!

2 Ответов

Рейтинг:
1

Gerry Schmitz

Вы перепроектировали OnPropertyChanged, и теперь все запуталось; особенно рефери.

Вы создаете OnPropertyChanged() в классе, который привязан к элементам управления в XAML public или internal, и вызываете его из всего, что обновляет этот класс, и вы хотите обновить пользовательский интерфейс. С точки зрения предоставления имени свойства, это имеет смысл только в том случае, если вы можете использовать nameof() вместо литералов, и с точки зрения производительности это имеет значение только при "подкачке" или тому подобном, в противном случае никто не может сказать, когда вы предоставляете только"", чтобы обновить все в этом пользовательском элементе управления / модели представления / контроллере / презентаторе / коде.


anna3276

Привет, Джерри, спасибо за ответ. Все классы уже являются "общедоступными", поэтому, к сожалению, я не понимаю вашей точки зрения... не могли бы вы сообщить мне, как я должен изменить свой бизнес-логический класс (C), чтобы изменения в свойствах моего объекта (экземпляры (E) везде) могли обновить/обновить мои представления (A1)? Большое спасибо за ваше время!

Gerry Schmitz

Наследование обычно плохо, особенно в вашем случае; и единственный объект, который "логически" имеет смысл иметь IPropertyChangedInterface, - это "B1", который не имеет надлежащих свойств. В противном случае слишком много "косвенности".

(Бессмысленно делать отдельные "свойства изменены", если вы обновляете все свойства, так как OnPropertyChanged("") выполняет то же самое ... вот почему я сказал, чтобы сделать его публичным или внутренним, чтобы он мог быть вызван "извне" этого объекта, когда это необходимо.)

общественный разделяемого класса ХХХ : аааа, INotifyPropertyChanged в {

публичное мероприятие PropertyChangedEventHandler PropertyChanged;
общественного недействительными on свойство changed( строка имя = "" ) {
Собственность поменялась?.Invoke( this, new PropertyChangedEventArgs( name ) );
}

// прием.
}

anna3276

мммм... какие классы

xxx
и
aaaa
предполагалось, что он будет в вашем образце? Я пытался записать эти изменения в свой класс (С), но безуспешно... конечно, я упускаю здесь что-то важное...

Рейтинг:
0

Ashutosh Gpta

если я хорошо понимаю, то не думаю, что вам нужен класс b1 только для наследования c, что здесь не имеет смысла. поскольку datacontext, который вы пытаетесь установить здесь, является классом c, поэтому просто убедитесь, что все привязки выполняются для элементов управления, являющихся частью класса контекста данных. я не уверен, что здесь используется класс E, вы даже можете переместить все вещи из класса E в класс C, чтобы сделать ваш дизайн простым.

public A1()
{
    InitializeComponent();
    DataContext = new C();
}
public class C : D
{
    public E MyObject;
    public C()
    {
        MyObject = new E();
    }
    public string content => MyObject.Text;
    public ICommand action => MyObject.Pressed;
}


anna3276

Привет, Ашутош, спасибо за ответ. Мне все еще нужна структура программы, представленная в моей диаграмме и вопросе, но я все равно изменил свою структуру.

DataContext = new B1();
(что перед тем, как спросить, я тоже пробовал), но все равно не получаю уведомлений на своем графическом интерфейсе...

Ashutosh Gpta

хорошо, тогда ваше решение может быть таким: держите ссылку на c в своем классе E, А класс E является своего рода дочерним элементом C, который управляет E.
реализуйте INotifyPropertyChanged для класса c, а также этот класс реквизитов привязан в элементе управления.
public E MyObject = new E(this); // реализация класса C.
электронные государственные(с cobj) // конструктор электронных
{
этот.Cobj = cobj;
}
public bool SetProperty<t>(ref T field, T newValue, [CallerMemberName]string propertyName = null)
{
если (!EqualityComparer<t>.По умолчанию.Равно(поле, значение))
{
поле = значение;
Собственность поменялась?.Invoke(this, new
PropertyChangedEventArgs(имя свойства));
это.Cobj.Собственность поменялась?.Вызвать(это.Cobj, новый
PropertyChangedEventArgs(nameof(this.Cobj.Содержание)));
вернуть true;
}
возвращать false;
}

не просто ссылайтесь на мой код и вставляйте его, я надеюсь, что мой код даст вам понимание того, как работает INotifyPropChnge.

anna3276

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

this.Cobj.PropertyChanged?.Invoke(this.Cobj, new PropertyChangedEventArgs(nameof(this.Cobj.Content)));
потому что в классе С нет свойств, которые нужно изменить, так как все они определены в классе Е...