【问题标题】:Raisepropertychanged is null and does not notify the property after datacontext changedRaisepropertychanged 为 null,datacontext 更改后不通知属性
【发布时间】:2020-08-25 12:22:44
【问题描述】:

我有一个 mainPage,我在其中调用我的用户控件。我的想法是我有一个TextBlock,单击按钮后,我将更改 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));
        }

我在调试时得到了正确的值,但它没有显示在 UI 中。

【问题讨论】:

    标签: uwp uno-platform winui


    【解决方案1】:

    这里绑定不起作用的主要问题是您不应该在此处设置绑定的Source 属性:

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

    这样做基本上是告诉绑定引擎在创建绑定时观察t.HeaderText 中的字符串实例,这很可能是空的,并且不能更改,因为它string 是不可观察的。

    您需要执行以下操作:

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

    这将指示绑定引擎在将TextBlock 添加到gridLayout(类型为TestViewModel)时设置当前的DataContext。

    你也不需要这一行:

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

    因为这是由框架自动完成的。

    您还应该小心在已更改的 DataContext 上创建 UI 而不要清除它,其中:

    gridlayout.Children.Add(grid);
    

    将不断地将TextBlock 实例添加到gridLayout,而不删除以前的实例。

    最后,您不应该以这种方式引发事件更改:

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

    因为您将从非 UI 线程引发,并会导致异常。请务必改用Dispatcher.RunAsync。

    【讨论】:

    • 感谢杰罗姆·拉班。我正在从后面的代码创建动态页面,但我的问题是我们在上面给出的控件中的绑定表达式在 wasm 中不起作用。如果我在文本块或任何控件上使用绑定表达式,它会显示一个空白页面。
    • 如果它不能仅在 Wasm 上运行,则可能是链接器问题,您的视图模型或模型位于另一个程序集中。您必须将其添加到 LinkerConfig.xml 文件中。
    猜你喜欢
    • 2015-09-18
    • 2017-07-25
    • 1970-01-01
    • 2013-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-21
    相关资源
    最近更新 更多