Изменение свойства не распространяется на цель привязки (UWP)

У меня есть ViewModel и Class. Они выглядят так:

//viewmodel
public class MyViewModel : ViewModelBase {

    private MyClass myClass;
    public MyClass MyClass{
        get{
            return myClass;
        }
        set{
            this.myClass = value;
            base.OnPropertyChanged();
        }
    }

    private string testString;
    public string TestString{
                get{
            return testString;
        }
        set{
            this.testString = value;
            base.OnPropertyChanged();
        }
    }

    public MyViewModel(){
        this.MyClass = new MyClass();
        this.TestString = "blah, blah, blah"
    }}



//class
    public class MyClass : ViewModelBase{
    private string  myString;
    public string MyString{
        get {
        return myString
        }
        set{
            this.myString = value;
            base.OnPropertyChanged();
        }
    }

    public MyClass (){
        this.MyString = "25";   }}

Базовый класс ViewModelBase реализует INotifyChange и содержит логику обработчика OnPropertyChanged. У меня есть UserControl, в котором я хочу привязать значения из MyClass следующим образом:

  <TextBlock Text="{x:Bind  Path=MyViewModel.MyClass.MyString, Mode=TwoWay}"></TextBlock>

Однако это не работает. Значение привязано при инициализации правильно, но любое изменение в MyViewModel.MyClass.MyString не отражается в texblock, текст остается прежним. OnPropertyChange возникает, точка останова в ViewModelBase попадает с измененным значением MyString, но каким-то образом не распространяется на texblock. Привязка к простому значению из MyClass работает как шарм, этот текстовый блок обновляется при изменении свойства:

  <TextBlock Text="{x:Bind  Path=MyViewModel.TestString, Mode=TwoWay}"></TextBlock>

Что мне не хватает? Почему не обновляется текстовый блок с привязкой к «MyViewModel.MyClass.MyString»?


person makak    schedule 13.09.2018    source источник


Ответы (1)


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

public MyViewModel MyViewModel => new MyViewModel();

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

public MyViewModel MyViewModel { get; } = new MyViewModel();

Вы получите правильное поведение при создании только одного экземпляра при создании страницы.

У меня такой код:

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();            
    }

    public MyViewModel MyViewModel { get; } = new MyViewModel();

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        MyViewModel.MyClass.MyString = "test";
    }
}

И мой простой ViewModelBase:

public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
person Martin Zikmund    schedule 13.09.2018
comment
Спасибо за ответ. Я не устанавливаю MyViewModel с помощью лямбда-оператора, однако я разрешаю его из контейнера DI, как это «» this. MyViewModel = DIManager.Instance.DIContainer.Resolve ‹MyViewModel› (); «. MyViewModel зарегистрирован в DI как singleton, поэтому я предполагаю, что с экземпляром все в порядке. Также MyViewModel.TestString работает хорошо. Проблема только в свойствах MyViewModel.MyClass. - person makak; 13.09.2018
comment
Кстати, моя ViewModelBase такая же, как у вас, я просто запускаю ее в диспетчере, так как возникла проблема с обновлением пользовательского интерфейса из фоновой угрозы. Я не думаю, что это проблема, поскольку, как я уже сказал, MyViewModel.TestString работает. Если у вас есть идеи, мы будем очень признательны. - person makak; 13.09.2018
comment
Это действительно очень странно. Что произойдет, если вы полностью замените MyClass новым экземпляром? Как 2_ - person Martin Zikmund; 13.09.2018
comment
Я пробовал, к сожалению, поведение точно такое же. Я уже нашел обходной путь, но меня это не особо устраивает. В UserControl я создаю новое строковое свойство newString. Я подписываюсь на событие MyViewModel.MyClass.PropertyChanged и в обработчике у меня this.newString = this.MyViewModel.MyClass.MyString, а в xaml я привязываюсь к newString. Это действительно работает, но это не очень аккуратное и чистое решение. Надеюсь, это будет временно. - person makak; 13.09.2018
comment
Это очень странно, у меня на компьютере точно такой же код, и он работает. Не могли бы вы создать новое решение, создать там тот же код и отправить его мне или поделиться на GitHub? - person Martin Zikmund; 13.09.2018
comment
К сожалению, это невозможно. Этот код - просто упрощение части довольно большого проекта, над которым я работаю, и мне не разрешено делиться им. Также задействованы интерфейсы, и я не использовал их в этом примере. Если я буду достаточно отчаянным (по крайней мере, сейчас не очень далеко), я постараюсь найти время, чтобы сделать более адекватную фиктивную копию этой проблемы и попытаться опубликовать ее где-нибудь. Спасибо, я очень ценю вашу помощь. - person makak; 13.09.2018
comment
Да, любой пример меньшего масштаба будет оценен по достоинству. Как я уже сказал, я думаю, что у меня есть точная копия того, чем вы поделились, и она работает, как ожидалось ... - person Martin Zikmund; 13.09.2018