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

#c# #xaml #uwp #binding

#c# #xaml #uwp #обязательный

Вопрос:

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

У меня есть страница xaml со следующим кодом :

 <TextBlock Text="{x:Bind ViewModel.SelectedEvidence.EvidenceName, Mode=OneWay}" />
 

Свойство ViewModel в коде позади :

 public EvidenceViewModel ViewModel { get; } = new EvidenceViewModel();
 

Выбранное свойство доказательства в EvidenceViewModel :

     public Evidence SelectedEvidence
    {
        get => _selectedEvidence;
        set => Set(ref _selectedEvidence, value); //this calls for RaisePropertyChanged
    }
 

EvidenceViewModel происходит от наблюдаемого класса для повышения изменений свойств.

 public class Observable : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void Set<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
    {
        if (Equals(storage, value))
        {
            return;
        }

        storage = value;
        OnPropertyChanged(propertyName);
    }

    protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
 

Свойство EvidenceName в классе Evidence

     public string EvidenceName
    {
        get { return _evidenceName; }
        set 
        {
            if (_evidenceName != value)
            {
                _evidenceName = value; 
                RaisePropertyChanged();
            }
        }
    }
 

Обновление 1

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

Обновление 2

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

Обновление 3

Код для класса доказательств в сгенерированном Nswagger файле для клиента

изображение 1

Код для класса EvidenceBase

изображение 2

Свойство EvidenceName, которое фактически существует в классе EvidenceBase

изображение 3

RasiePropertyChanged код в базе доказательств

изображение 5

Комментарии:

1. Не могли бы вы предоставить исходный код RaisePropertyChanged , пожалуйста? Немного странно, что код содержит Set , OnPropertyChanged и RaisePropertyChanged все ли они взяты из одного и того же инструментария MVVM?

2. @MartinZikmund механизм изменения свойств raise происходит из сгенерированного файла nswag, поскольку эти классы моделей на самом деле являются таблицами db в dotnetcore, и nswagger используется для связывания этого с клиентским приложением.

3. @MartinZikmund Пожалуйста, ознакомьтесь с Обновлением 3 моего вопроса, которое может помочь прояснить ваш вопрос о PropertyChanged, и это событие PropertyChanged отлично работает для всех свойств, если они не наследуются от абстрактного класса, как в этом случае.

Ответ №1:

Вы могли бы позволить Evidence классу наследовать от Observable класса и вызвать OnPropertyChanged метод в EvidenceName .

Например:

 public class Evidence:Observable
{
    private string _evidenceName;
    public string EvidenceName
    {
        get { return _evidenceName; }
        set
        {
            if (_evidenceName != value)
            {
                _evidenceName = value;
                OnPropertyChanged("EvidenceName");
            }
        }
    }
}
 

Обновление:
Я протестировал код из вашего обновления 3 и обнаружил, что проблема заключается в переопределениях в Evidence классе.
Пожалуйста, проверьте следующий код:

 private void Button_Click(object sender, RoutedEventArgs e)
{
    ViewModel.SelectedEvidence.EvidenceName = "testName";
}


public abstract partial class EvidenceBase : System.ComponentModel.INotifyPropertyChanged
{        
    private string _evidenceName;
    [Newtonsoft.Json.JsonProperty("evidenceName",Required =Newtonsoft.Json.Required.Default,NullValueHandling =Newtonsoft.Json.NullValueHandling.Ignore)]
    public string EvidenceName
    {
        get { return _evidenceName; }
        set
        {
            if(_evidenceName!=value)
            {
                _evidenceName = value;
                RaisePropertyChanged("EvidenceName");
            }
        }
    }
    protected virtual void RaisePropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));
    }
    public event PropertyChangedEventHandler PropertyChanged;
}

public partial class Evidence : EvidenceBase, System.ComponentModel.INotifyPropertyChanged
{

//Remove the override of PropertyChanged property and RaisePropertyChanged method to avoid hide the ones inherited from base class.
}

public class Observable : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void Set<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        if (Equals(storage, value))
        {
            return;
        }

        storage = value;
        OnPropertyChanged(propertyName);
    }
    protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

public class EvidenceViewModel:Observable
{
    private Evidence _selectedEvidence;
    public Evidence SelectedEvidence
    {
        get { return _selectedEvidence; }
        set
        {
            Set(ref _selectedEvidence, value);
        }
    }
    public EvidenceViewModel()
    {
        _selectedEvidence = new Evidence();
    }
}
 

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

Комментарии:

1. Не могу этого сделать, потому что механизм изменения свойств raise происходит из сгенерированного файла nswag, поскольку эти классы моделей на самом деле являются таблицами БД в dotnetcore, и nswagger используется для связи этого с клиентским приложением. Пожалуйста, ознакомьтесь с Обновлением 3 моего вопроса, которое может помочь уточнить PropertyChanged , и это событие PropertyChanged отлично работает для всех свойств, если они не наследуются от абстрактного класса, как в этом случае.

2. Я обновил свой ответ, вы можете проверить обновление, чтобы узнать, может ли оно соответствовать вашим требованиям.

3. да, ваше обновленное решение, похоже, работает, я только что прокомментировал событие Propertychanged и поднял значение PropertyChanged в классе «Доказательства». Но проблема в том, что этот файл автоматически генерируется файлом nswag, поэтому я не уверен, как настроить файл nswag или классы моделей, поэтому каждый раз, когда генерируется файл nswag_Generated, он не включает событие PropertyChanged в дочерние классы, и что, если дочерний класс также имеет некоторые собственные свойства, не уверен, как обрабатыватьэто.

4. Но в любом случае ваш ответ затрагивает основную проблему, поэтому я отмечаю его как выполненный 🙂

5. Я думаю, я должен просто создавать интерфейсы и добавлять все дочерние свойства вручную в классы и полностью удалять родительские абстрактные классы?