좋아, 난 당신이 동적 데이터 바인딩과 MVVM (모델 - 뷰 - 뷰 모델) 방식을 사용하여 ContentControl을의 내용을 변경하는 방법을 보여주는 간단한 예제를 노크했습니다.
새 프로젝트를 만들고이 파일을로드하여 모든 작동 방식을 확인하는 것이 좋습니다.
먼저 INotifyPropertyChanged 인터페이스를 구현해야합니다. 속성에 대한 변경이 발생할 때 UI에 알릴 속성으로 사용자 고유의 클래스를 정의 할 수 있습니다. 이 기능을 제공하는 추상 클래스를 만듭니다.
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);
}
}
}
우리는 이제 데이터 모델이 필요합니다. 간단하게하기 위해 HomePage와 SettingsPage라는 두 가지 모델을 만들었습니다. 두 모델 모두 하나의 속성 만 가지고 있으므로 필요에 따라 더 많은 속성을 추가 할 수 있습니다.
HomePage.cs
public class HomePage
{
public string PageTitle { get; set; }
}
SettingsPage.cs
public class SettingsPage
{
public string PageTitle { get; set; }
}
나는 각 모델을 포장 ViewModels 해당 만듭니다. ViewModel은 내 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");
}
}
}
이제 우리는 각각의 뷰 모델에 대한 뷰를 제공해야합니다. 즉 HomePageView 및 SettingsPageView. 이를 위해 2 개의 UserControls를 만들었습니다.
HomePageView.
<UserControl x:Class="WpfApplication3.HomePageView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock FontSize="20" Text="{Binding Path=PageTitle}" />
</Grid>
XAML SettingsPageView.xaml
<UserControl x:Class="WpfApplication3.SettingsPageView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock FontSize="20" Text="{Binding Path=PageTitle}" />
</Grid>
우리는 이제 MainWindow를위한 XAML을 정의해야합니다. 2 개의 "페이지"사이를 탐색하는 데 도움이되는 2 개의 버튼이 포함되어 있습니다. MainWindow.xaml는
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication3"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type local:HomePageViewModel}">
<local:HomePageView />
</DataTemplate>
<DataTemplate DataType="{x:Type local:SettingsPageViewModel}">
<local:SettingsPageView />
</DataTemplate>
</Window.Resources>
<DockPanel>
<StackPanel DockPanel.Dock="Left">
<Button Content="Home Page" Command="{Binding Path=LoadHomePageCommand}" />
<Button Content="Settings Page" Command="{Binding Path=LoadSettingsPageCommand}"/>
</StackPanel>
<ContentControl Content="{Binding Path=CurrentViewModel}"></ContentControl>
</DockPanel>
우리는 또한 MainWindow를위한 뷰 모델이 필요합니다. 그러나 그 전에 우리는 버튼을 명령에 묶어 둘 수있는 다른 클래스를 만들어야합니다.
DelegateCommand.cs
public class DelegateCommand : ICommand
{
/// <summary>
/// Action to be performed when this command is executed
/// </summary>
private Action<object> executionAction;
/// <summary>
/// Predicate to determine if the command is valid for execution
/// </summary>
private Predicate<object> canExecutePredicate;
/// <summary>
/// Initializes a new instance of the DelegateCommand class.
/// The command will always be valid for execution.
/// </summary>
/// <param name="execute">The delegate to call on execution</param>
public DelegateCommand(Action<object> execute)
: this(execute, null)
{
}
/// <summary>
/// Initializes a new instance of the DelegateCommand class.
/// </summary>
/// <param name="execute">The delegate to call on execution</param>
/// <param name="canExecute">The predicate to determine if command is valid for execution</param>
public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
this.executionAction = execute;
this.canExecutePredicate = canExecute;
}
/// <summary>
/// Raised when CanExecute is changed
/// </summary>
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
/// <summary>
/// Executes the delegate backing this DelegateCommand
/// </summary>
/// <param name="parameter">parameter to pass to predicate</param>
/// <returns>True if command is valid for execution</returns>
public bool CanExecute(object parameter)
{
return this.canExecutePredicate == null ? true : this.canExecutePredicate(parameter);
}
/// <summary>
/// Executes the delegate backing this DelegateCommand
/// </summary>
/// <param name="parameter">parameter to pass to delegate</param>
/// <exception cref="InvalidOperationException">Thrown if CanExecute returns false</exception>
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을 defind 수 있습니다. CurrentViewModel은 MainWindow의 ContentControl에 바인딩되는 속성입니다. 버튼을 클릭하여이 속성을 변경하면 MainWindow에서 화면이 변경됩니다. MainWindow는 Window.Resources 섹션에서 정의한 DataTemplates 때문에로드 할 화면 (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."});
}
}
그리고 마지막으로, 우리는 우리가 MainWindow를의의 DataContext 속성에 우리의 MainWindowViewModel 클래스를로드 할 수 있도록 응용 프로그램 시작을 오버라이드 (override) 할 필요가있다.
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();
}
}
그것은 또한 우리가 시작까지 2 MainWindows을하지 않도록 App.xaml 응용 프로그램 태그에 StartupUri="MainWindow.xaml"
코드를 제거하는 것이 좋습니다 것입니다.
새 프로젝트에 복사하여 사용할 수있는 DelegateCommand 및 ViewModelBase 클래스에 유의하십시오. 이것은 아주 간단한 예일뿐입니다. 당신은 당신의 코멘트에 here 및 here
편집 에서 더 나은 아이디어를 얻을 수 있습니다, 당신은 각 뷰 및 관련 상용구 코드를위한 클래스가 필요하지 할 수 있는지 알고 싶었다. 내가 아는 한, 대답은 '아니오'입니다. 예, 하나의 거대한 클래스를 가질 수 있지만 각 Property setter에 대해 OnPropertyChanged를 호출해야합니다. 이것에는 몇 가지 단점이 있습니다. 첫째, 결과 클래스는 유지하기가 매우 어려울 것입니다. 많은 코드와 의존성이있을 것입니다. 둘째, 뷰를 "스왑"하기 위해 DataTemplates를 사용하는 것은 어려울 것입니다. DataTemplates에서 x : Key를 사용하고 사용자 정의 컨트롤에서 템플릿 바인딩을 하드 코딩하여 여전히 가능합니다. 본질적으로 코드를 훨씬 짧게 만들지는 못하지만 코드를 더 어렵게 만들 것입니다.
여러분의 주요 불만이 여러분의 모델에 많은 코드를 작성해야만 모델 속성을 감쌀 것 같아요.T4 templates을보십시오. 일부 개발자는 이것을 사용하여 상용구 코드 (예 : ViewModel 클래스)를 자동 생성합니다. 이 코드는 개인적으로 사용하지 않으며, 사용자 정의 코드 스 니펫을 사용하여 viewmodel 속성을 빠르게 생성합니다.
또 다른 옵션은 Prism 또는 MVVMLight와 같은 MVVM 프레임 워크를 사용하는 것입니다. 나는 하나도 사용하지 않았지만, 그들 중 일부는 상용구 코드를 쉽게 만들 수있는 기능을 내장하고 있다고 들었습니다. 데이터베이스에 설정을 저장하는 경우 엔티티 프레임 워크와 같은 ORM 프레임 워크를 사용하여 데이터베이스에서 모델을 생성 할 수 있습니다. 뷰 모델 및 뷰.
ListView는 어디에 있습니까? 수퍼 알로와 같은 코드를 더 많이 사용할 수 있습니까? 당신이 가진 모든 것을 우리에게주세요. –
탭을 사용하려면 대신 TabControl 컨트롤을 사용하지 않는 것이 좋습니다. 탭을 숨기거나 표시하려면 TabItem 컨트롤의 Visibility 속성을 조작합니다. 여기서 바인딩을 사용할 수 있습니다. 또한 Microsoft의 데이터 바인딩 개요 (http://msdn.microsoft.com/en-us/library/ms752347.aspx)를 읽으십시오. UI 요소를 바인딩하지 말 것을 권한다. 귀하의 예에서는 설정에 대한 여러 속성을 포함하는 SettingsPage 클래스를 만듭니다. xaml에서 컨트롤을 만들고 각 속성에 바인딩합니다. – failedprogramming
@ snowy gui hedgehog : ListView는 중요하지 않습니다. ContentControl의 컨텐트를 설정할 변경 이벤트를 트리거 할 수 있습니다. 기본적으로 내 질문에 동적으로 미리 정의 된 ContentControl 템플릿을 사용하여 코드에서 ContentControl 콘텐츠를 동적으로 변경하는 방법에 대한 모든 것입니다. @failedprogramming 내가 이것을하려고하는 이유는이 게시물입니다. [link] (http://stackoverflow.com/questions/7010688/wpf-tab-control-with-no-tabs) 여기. UI 요소를 바인딩하지 않는 이유는 무엇입니까? – Xaser