Raisepropertychanged имеет значение null и не уведомляет свойство после изменения контекста данных

У меня есть mainPage, на котором я вызываю свой пользовательский элемент управления. Идея в том, что у меня есть тексблок... и при нажатии кнопки я меняю текст, который находится выше, на имя Hello World.

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock Text="Hello, world!" Margin="20" FontSize="30" />
        <local1:GenericControl></local1:GenericControl>

        <Button x:Name="create" Click="clickBtn"/>
    </Grid>

MainPage.xaml.cs

 public sealed partial class MainPage : Page
    {
        public TestViewModel viewModel { get; set; } = new TestViewModel();

        public MainPage()
        {
            this.InitializeComponent();
            this.DataContext = viewModel;
        }

        public void clickBtn(object sender, RoutedEventArgs e) {

            var m = ((Button)sender).DataContext as TestViewModel;
            m.CreateModel.HeaderText = "tripathi";
            viewModel = m;
            var s = new GenericControl {

                DataContext = viewModel
            };
        }

    }

и ниже пользователь контролирует то, что я создаю из кода, стоящего за

public GenericControl()
        {
            this.InitializeComponent();
            //this.DataContext = this;
            DataContextChanged += (s, e) =>
            {
                var createModel = this.DataContext as TestViewModel;
                CreateControl(createModel.CreateModel);
            };
        }

        private void CreateControl(Test t) {
            var grid = new Grid();
            grid.SetBinding(Grid.DataContextProperty, new Binding { Source = DataContext, Mode = BindingMode.TwoWay });
            //grid.DataContext = this.DataContext;
            var tb = new TextBlock();
            tb.SetBinding(TextBlock.TextProperty, new Binding { Source = t.HeaderText, Mode = BindingMode.TwoWay
                 , UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged});

            grid.Children.Add(tb);
            gridlayout.Children.Add(grid);
        }

GenericControl.xaml -

<Grid Name="gridlayout">

    </Grid>

TestViewModel -

private Test _createModel = new Test();

        private string _textHeader;

        public Test CreateModel
        {
            get { return _createModel; }
            set { _createModel = value;
                Task.Factory.StartNew(() =>
                {
                    Thread.Sleep(5000);
                RaisePropertyChanged("CreateModel");

                });
            }
        }

        public TestViewModel() {
            CreateModel.HeaderText = "Ankit";
            CreateModel = CreateModel;
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public void RaisePropertyChanged([CallerMemberName]string prop = "")
        {
            if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }

Модель -

private string _headerText;

        public string HeaderText
        {
            get { return _headerText; }
            set { _headerText = value;
                Task.Factory.StartNew(() =>
                {
                    Thread.Sleep(10000);
                    RaisePropertyChanged("HeaderText");

                });

            }
        }



        public event PropertyChangedEventHandler PropertyChanged;
        public void RaisePropertyChanged([CallerMemberName]string prop = "") {
            if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }

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

1 ответ

Решение

Основная проблема здесь в том, что привязка не работает, заключается в том, что вы не должны устанавливать Source свойство привязки здесь:

tb.SetBinding(
  TextBlock.TextProperty, 
  new Binding { 
    Source = t.HeaderText, 
    Mode = BindingMode.TwoWay, 
    UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
  });

По сути, это означает, что механизм привязки должен наблюдать за экземпляром строки, который находится в t.HeaderText при создании привязки, которая, скорее всего, имеет значение null и не может измениться, поскольку string не наблюдается.

Вместо этого вам нужно сделать следующее:

tb.SetBinding(
  TextBlock.TextProperty,
  new Binding { 
    Path = new PropertyPath("CreateModel.HeaderText"), 
    Mode = BindingMode.TwoWay, 
    UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
  });

Это укажет механизму привязки установить текущий DataContext когда TextBlock добавляется в gridLayout, который имеет тип TestViewModel.

Вам также не нужна эта строка:

grid.SetBinding(
  Grid.DataContextProperty, 
  new Binding { 
    Source = DataContext, 
    Mode = BindingMode.TwoWay 
  });

Поскольку это делается автоматически фреймворком.

Вы также должны быть осторожны с созданием пользовательского интерфейса в измененном DataContext, а не с его очисткой, где:

gridlayout.Children.Add(grid);

будет постоянно добавлять TextBlock экземпляров в gridLayout, не удаляя предыдущие.

И, наконец, не следует вызывать изменения событий таким образом:

set { 
    _createModel = value;
    Task.Factory.StartNew(() =>
    {
        Thread.Sleep(5000);
        RaisePropertyChanged("CreateModel");

    });
}

поскольку вы поднимете его из потока, отличного от пользовательского интерфейса, и вызовет исключение. Обязательно используйте Dispatcher.RunAsync вместо.

Другие вопросы по тегам