【问题标题】:Changing viewmodel更改视图模型
【发布时间】:2016-11-10 08:44:43
【问题描述】:

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

当我单击按钮时,它会更改 ViewModel 的 Text 属性,并且 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 类有一个字符串属性(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";
    }

Complited binding is not working(文本未更改)我无法弄清楚为什么会这样...我需要将 viewModel 从 null 更改(vm 为 null,因为它正在等待实际应用中的一些数据)

【问题讨论】:

  • 当然可以,因为没有通知ViewModel发生了变化
  • 你的意思是 ViewModel 属性?如何通知更改 - 我不想在 MainPage 中实现 INotifyPropertychange
  • 没有魔法,您必须通知页面 ViewModel 的内容已更改。不管你怎么做,但你必须这样做
  • 也许你应该使用 DataContext 属性 - 已经有一个内置事件 DataContextChanged 并且绑定系统知道这一点
  • 如果您希望项目成功绑定,那么在没有实现 INotifyPropertyChange 的情况下,除了在 poco 中设置属性之外,您还需要做更多的事情。如果您是 xaml 和绑定的新手,我将研究 MVVM 的工作原理,您将解决该问题。 msdn.microsoft.com/en-gb/library/hh848246.aspx

标签: c# xaml mvvm uwp xbind


【解决方案1】:

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

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

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

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

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

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

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

private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
    ViewModel = new ViewModel();
    ViewModel.Text = "x:Bind does not work";
    this.Bindings.Update();
}

【讨论】:

  • 我知道这是旧的,但非常感谢。我不知道 this.Bindings.Update()。
【解决方案2】:

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

这对我有用。

【讨论】:

    猜你喜欢
    • 2017-05-04
    • 2018-06-30
    • 1970-01-01
    • 1970-01-01
    • 2014-02-13
    • 2013-12-02
    • 1970-01-01
    • 1970-01-01
    • 2014-02-01
    相关资源
    最近更新 更多