2

我有简化的应用程序来显示我的问题

当我单击按钮时,它会更改Text属性ViewModelTextBlock.Text更新。

主页.xaml

<StackPanel>
  <Button  Click="ButtonBase_OnClick">Button to change text</Button>
  <TextBlock Text="{x:Bind ViewModel.Text, Mode=OneWay}"></TextBlock>
</StackPanel>

MainPage.xaml.cs

 public MainPage()
    {
        ViewModel = new ViewModel();
        this.InitializeComponent();
    }
 private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        ViewModel.Text = "x:Bind works";
    }

ViewModel类有一个字符串属性(Text)并实现了INotifyPropertyChange接口。


问题开始时ViewModel未在 ctor 中设置(即 viewModel 为 null 并在运行时更改):

   public MainPage()
    {
        //ViewModel = new ViewModel();//this line has been removed
        this.InitializeComponent();
    }
 private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        ViewModel = new ViewModel();//this line has been added
        ViewModel.Text = "x:Bind does not work";
    }

编译绑定不起作用(文本未更改),我无法弄清楚为什么会这样......我需要将 viewModel 从 null 更改(vm 为 null,因为它正在等待真实应用程序中的一些数据)

4

2 回答 2

2

{x:Bind}绑定(通常称为编译绑定)使用生成的代码来实现其优势。在 XAML 加载时,{x:Bind}被转换为您可以认为的绑定对象,并且此对象从数据源的属性中获取值。这些生成的代码可以在您的obj文件夹中找到,其名称类似于 (for C#) <view name>.g.cs

对于您的代码,生成的代码将如下所示:

// Update methods for each path node used in binding steps.
private void Update_(global::UWP.BlankPage3 obj, int phase)
{
    if (obj != null)
    {
        if ((phase & (NOT_PHASED | DATA_CHANGED | (1 << 0))) != 0)
        {
            this.Update_ViewModel(obj.ViewModel, phase);
        }
    }
}
private void Update_ViewModel(global::UWP.ViewModel obj, int phase)
{
    this.bindingsTracking.UpdateChildListeners_ViewModel(obj);
    if (obj != null)
    {
        if ((phase & (NOT_PHASED | DATA_CHANGED | (1 << 0))) != 0)
        {
            this.Update_ViewModel_Text(obj.Text, phase);
        }
    }
}

...

private global::UWP.ViewModel cache_ViewModel = null;
public void UpdateChildListeners_ViewModel(global::UWP.ViewModel obj)
{
    if (obj != cache_ViewModel)
    {
        if (cache_ViewModel != null)
        {
            ((global::System.ComponentModel.INotifyPropertyChanged)cache_ViewModel).PropertyChanged -= PropertyChanged_ViewModel;
            cache_ViewModel = null;
        }
        if (obj != null)
        {
            cache_ViewModel = obj;
            ((global::System.ComponentModel.INotifyPropertyChanged)obj).PropertyChanged += PropertyChanged_ViewModel;
        }
    }
}

在这里,我只是复制一些与您的问题相关的方法。从这些方法中,您可以发现在更新TextBlockPropertyChanged监听器之前,它会检查是否ViewModelnull. 如果是null,则什么都不会做。因此,要使{x:Bind}ViewModel工作,我们必须在页面加载之前进行初始化。这就是为什么在事件中初始化时{x:Bind}不起作用的原因。ViewModelButton.Click

要解决此问题,您可以像 Filip 所说的那样实现INotifyPropertyChanged接口,ViewModel以便生成的代码可以在ViewModel更改时得到通知(从nullnew ViewModel())并更新您的 UI。

但我认为你可以ViewModel在构造函数中初始化。当你初始化时ViewModel,你可以设置你正在等待的属性,null比如:

public MainPage()
{
    ViewModel = new ViewModel() { Text = null };
    this.InitializeComponent();
}

然后在您的日期准备好时更新这些属性。这样,您就可以不在INotifyPropertyChanged您的页面上实现接口。

除此之外,还有另一种更便宜的方法,您可以this.Bindings.Update();在初始化后调用方法来强制更新绑定,ViewModel如下所示:

private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
    ViewModel = new ViewModel();
    ViewModel.Text = "x:Bind does not work";
    this.Bindings.Update();
}
于 2016-07-08T08:00:44.533 回答
0

您是否INotifyPropertyChanged像这样在页面上实现

public sealed partial class MainPage : Page, INotifyPropertyChanged
{
    private ViewModel viewModel;

    public ViewModel ViewModel
    {
        get { return viewModel; }
        set 
        {
            viewModel = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ViewModel)));
        }
    }

    public MainPage()
    {
        ViewModel = new ViewModel { };
        this.InitializeComponent();
    }

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        ViewModel = new ViewModel {  };//this line has been added
        ViewModel.Text = "x:Bind does not work";
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

这对我有用。

于 2016-07-07T23:43:58.160 回答