更改视图模型

本文关键字:模型 视图 | 更新日期: 2023-09-27 17:55:42

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

当我单击按钮时,它会更改Text ViewModel的属性并更新TextBlock.Text

MainPage.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 类有一个字符串属性(文本)并实现了 INotifyPropertyChange 接口。


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

   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";
    }

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

更改视图模型

{x:Bind} 绑定(通常称为编译绑定)使用生成的代码来实现其优势。在 XAML 加载时,{x:Bind} 将转换为可视为绑定的对象,并且此对象从数据源上的属性获取值。这些生成的代码可以在obj文件夹中找到,其名称类似于(对于 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侦听器之前,它会检查ViewModel是否null。如果null,什么都不会做。因此,为了使 {x:Bind} 工作,我们必须在页面加载之前初始化ViewModel。这就是为什么 {x:Bind} 在事件中初始化ViewModel时不起作用的原因Button.Click

要解决此问题,您可以像 Filip 所说的那样为ViewModel实现INotifyPropertyChanged接口,以便在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();
}

您是否像这样在页面上实现了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;
}

这对我有用。