为动态内容绑定ContentControl内容

我目前正在尝试通过使用ListView(作为选项卡)和带有绑定内容属性的ContentControl来实现带有隐藏选项卡的tabcontrol的function。

我读了一下这个主题,如果我做对了,它应该这样工作:

          . .         

并在代码背后:

 public partial class MainWindow : MetroWindow { private ContentControl SettingsPage; private ResourceDictionary SettingsPagesDict = new ResourceDictionary(); public MainWindow() { InitializeComponent(); SettingsPagesDict.Source = new Uri("SettingsPages.xaml", UriKind.RelativeOrAbsolute); SettingsPage = SettingsPagesDict["AppearancePage"] as ContentControl; 

尽管它没有抛出任何错误,但它不会显示“Test”TextBlock。

我可能有错误的绑定概念,请给我一个正确方向的提示。

问候

好的我已经敲了一个简单的例子来向您展示如何使用带有数据绑定的MVVM(Model-View-ViewModel)方法动态更改ContentControl的内容。

我建议您创建一个新项目并加载这些文件以查看它是如何工作的。

我们首先需要实现INotifyPropertyChanged接口。 这将允许您定义自己的类,其属性将在对属性进行更改时通知UI。 我们创建一个提供此function的抽象类。

ViewModelBase.cs

 public abstract class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); } protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) { var handler = this.PropertyChanged; if (handler != null) { handler(this, e); } } } 

我们现在需要拥有数据模型。 为简单起见,我创建了2个模型 – HomePage和SettingsPage。 两种模型只有一个属性,您可以根据需要添加更多属性。

HomePage.cs

 public class HomePage { public string PageTitle { get; set; } } 

SettingsPage.cs

 public class SettingsPage { public string PageTitle { get; set; } } 

然后我创建相应的ViewModel来包装每个模型。 请注意,viewmodelsinheritance自我的ViewModelBase抽象类。

HomePageViewModel.cs

 public class HomePageViewModel : ViewModelBase { public HomePageViewModel(HomePage model) { this.Model = model; } public HomePage Model { get; private set; } public string PageTitle { get { return this.Model.PageTitle; } set { this.Model.PageTitle = value; this.OnPropertyChanged("PageTitle"); } } } 

SettingsPageViewModel.cs

 public class SettingsPageViewModel : ViewModelBase { public SettingsPageViewModel(SettingsPage model) { this.Model = model; } public SettingsPage Model { get; private set; } public string PageTitle { get { return this.Model.PageTitle; } set { this.Model.PageTitle = value; this.OnPropertyChanged("PageTitle"); } } } 

现在我们需要为每个ViewModel提供视图。 即HomePageView和SettingsPageView。 我为此创建了2个UserControls。

HomePageView.xaml

     

SettingsPageView.xaml

     

我们现在需要为MainWindow定义xaml。 我已经包含了2个按钮,可帮助在2个“页面”之间导航。 MainWindow.xaml

                 

我们还需要一个用于MainWindow的ViewModel。 但在此之前,我们需要创建另一个类,以便我们可以将我们的按钮绑定到命令。

DelegateCommand.cs

 public class DelegateCommand : ICommand { ///  /// Action to be performed when this command is executed ///  private Action executionAction; ///  /// Predicate to determine if the command is valid for execution ///  private Predicate canExecutePredicate; ///  /// Initializes a new instance of the DelegateCommand class. /// The command will always be valid for execution. ///  /// The delegate to call on execution public DelegateCommand(Action execute) : this(execute, null) { } ///  /// Initializes a new instance of the DelegateCommand class. ///  /// The delegate to call on execution /// The predicate to determine if command is valid for execution public DelegateCommand(Action execute, Predicate canExecute) { if (execute == null) { throw new ArgumentNullException("execute"); } this.executionAction = execute; this.canExecutePredicate = canExecute; } ///  /// Raised when CanExecute is changed ///  public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } ///  /// Executes the delegate backing this DelegateCommand ///  /// parameter to pass to predicate /// True if command is valid for execution public bool CanExecute(object parameter) { return this.canExecutePredicate == null ? true : this.canExecutePredicate(parameter); } ///  /// Executes the delegate backing this DelegateCommand ///  /// parameter to pass to delegate /// Thrown if CanExecute returns false public void Execute(object parameter) { if (!this.CanExecute(parameter)) { throw new InvalidOperationException("The command is not valid for execution, check the CanExecute method before attempting to execute."); } this.executionAction(parameter); } } 

现在我们可以定义MainWindowViewModel。 CurrentViewModel是绑定到MainWindow上的ContentControl的属性。 当我们通过单击按钮更改此属性时,屏幕将在MainWindow上更改。 由于我在Window.Resources部分中定义的DataTemplates,MainWindow知道要加载哪个屏幕(usercontrol)。

MainWindowViewModel.cs

 public class MainWindowViewModel : ViewModelBase { public MainWindowViewModel() { this.LoadHomePage(); // Hook up Commands to associated methods this.LoadHomePageCommand = new DelegateCommand(o => this.LoadHomePage()); this.LoadSettingsPageCommand = new DelegateCommand(o => this.LoadSettingsPage()); } public ICommand LoadHomePageCommand { get; private set; } public ICommand LoadSettingsPageCommand { get; private set; } // ViewModel that is currently bound to the ContentControl private ViewModelBase _currentViewModel; public ViewModelBase CurrentViewModel { get { return _currentViewModel; } set { _currentViewModel = value; this.OnPropertyChanged("CurrentViewModel"); } } private void LoadHomePage() { CurrentViewModel = new HomePageViewModel( new HomePage() { PageTitle = "This is the Home Page."}); } private void LoadSettingsPage() { CurrentViewModel = new SettingsPageViewModel( new SettingsPage(){PageTitle = "This is the Settings Page."}); } } 

最后,我们需要覆盖应用程序启动,以便我们可以将MainWindowViewModel类加载到MainWindow的DataContext属性中。

App.xaml.cs

 public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); var window = new MainWindow() { DataContext = new MainWindowViewModel() }; window.Show(); } } 

删除App.xaml Application标记中的StartupUri="MainWindow.xaml"代码也是一个好主意,这样我们就无法在启动时获得2个MainWindows。

请注意,DelegateCommand和ViewModelBase类只能复制到新项目中并使用。 这只是一个非常简单的例子。 你可以从这里和这里得到一个更好的主意

编辑在您的评论中,您想知道是否可以不必为每个视图和相关的样板代码创建一个类。 据我所知,答案是否定的。 是的,您可以拥有一个巨大的类,但您仍然需要为每个Property setter调用OnPropertyChanged。 这也有很多弊端。 首先,由此产生的类很难维护。 会有很多代码和依赖项。 其次,使用DataTemplates来“交换”视图会很困难。 仍然可以使用ax:键入您的DataTemplates并在您的usercontrol中对模板绑定进行硬编码。 从本质上讲,你并没有真正使你的代码更短,但是你会让自己变得更难。

我猜你的主要抱怨是你必须在viewmodel中编写这么多代码来包装你的模型属性。 看看T4模板 。 一些开发人员使用它来自动生成他们的样板代码(即ViewModel类)。 我个人不使用这个,我使用自定义代码片段来快速生成viewmodel属性。

另一种选择是使用MVVM框架,例如Prism或MVVMLight。 我自己没有使用过,但我听说其中一些内置function可以轻松实现样板代码。

另一点需要注意的是:如果要将设置存储在数据库中,则可以使用ORM框架(如Entity Framework)从数据库生成模型,这意味着您剩下的就是创建视图模型和视图。