以编程方式绑定到不断变化的ViewModel

时间:2012-07-03 10:58:41

标签: c# wpf mvvm

我正在创建一个类似于某些微软应用程序的“向导”组件的应用程序。为了表示这一点,我有两个ViewModel:

class WizardVm {
    public string Name { get; set; }
    public ICommand QuitCommand { get { /* ommitted */ } }
    public WizardStepVm CurrentStep { get; set; }
}

class WizardStepVm {
    public string StepName { get; set; }
    public string StepText {get; set; }
}

在视图中,WizardVm绑定到窗口,WizardStepVm绑定到窗口内的内容面板。我以编程方式创建内容控件,并将其添加到WizardView,如下所示:

// in the "DataContextChanged" handler for the WizardView
var bn = new Binding("CurrentStep");
bn.Mode = BindingMode.OneWay;

var contentControl = new ContentControl();
contentControl.SetBinding(ContentControl.ContentProperty, bn);
WizardViewStackPanel.Children.Add(contentControl);

最初创建WizardView时,这会正确呈现。但是,如果CurrentStep发生更改,则视图不会更新以反映此情况。 CurrentStep更改,但ContentControl继续显示原始WizardStepVm。此外,旧的WizardStepVm继续存在于内存中,其字符串属性仍然可以更改(从视图中)。

这是为什么?我该怎么做才能让内容控件发生变化以反映它绑定的ViewModel中的变化?

(实际上有一个很好的理由以编程方式执行此操作。但是,xaml解决方案也很受欢迎。)

2 个答案:

答案 0 :(得分:2)

您的课程需要实现INotifyPropertyChanged界面,以便每次都通知用户界面 其中一个属性发生了变化:

class WizardStepVm : INotifyPropertyChanged {
    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
    private string m_stepName;
    public string StepName {
      get {
        return m_stepName;
      }
      set {
        m_stepName = value; 
        NotifyPropertyChanged("StepName");
      }
    }
    /* etc... */
}

答案 1 :(得分:2)

首先你的viewmodel应该实现INotifyPropertyChanged或者只使用BindingMode OneTime。(见this post)

尽管如此,您可以使用viewmodel first approach和datatemplates

轻松完成您想要的任务
public class WizardVm {
   public string Name { get; set; }
   public ICommand QuitCommand { get { /* ommitted */ } }
   public object CurrentStep { get; set; }//raise OnPropertyChanged("CurrentStep"); in your setter!!
}

public class WizardStep1Vm {
   public string StepName { get; set; }
   public string StepText {get; set; }
}

public class WizardStep2Vm {
   public string StepName { get; set; }
   public string StepText {get; set; }
}

window.xaml

<Window>
<Window.Resources>
    <DataTemplate DataType="{x:Type local:WizardStep1Vm}">
        <!--your view for step1 goes here-->
        <local:MyStep1View/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type local:WizardStep2Vm}">
        <!--your view for step2 goes here-->
        <local:MyStep2View/>
    </DataTemplate>
</Window.Resources>
  <Grid>
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
    </Grid.RowDefinitions>

    <TextBlock Grid.Row="0" Text="{Binding Name}" />

    <ContentPresenter Content="{Binding CurrentStep}"/>

  </Grid>
</Window>

何时在viewmodel中设置CurrentStep属性。您将在contentcontrol中看到正确的视图。如果没有,则缺少DataTemplate;)