TabControl을 ViewModel 모음에 바인딩하려면 어떻게 해야 합니까?
기본적으로 MainViewModel.cs에는 다음과 같은 내용이 있습니다.
ObservableCollection<TabItem> MyTabs { get; private set; }
다만, 어떻게든 탭을 작성할 뿐만 아니라, MVVM을 유지하면서 탭의 내용을 로드해 적절한 뷰 모델에 링크할 수 있어야 합니다.
기본적으로 사용자 컨트롤을 탭 항목의 콘텐츠로 로드하고 해당 사용자 컨트롤을 적절한 뷰 모델에 연결하려면 어떻게 해야 합니까?이를 어렵게 하는 부분은 View Model이 실제 뷰 항목을 구성하지 않도록 되어 있다는 것입니다.아니면 할 수 있을까?
기본적으로 MVVM이 적절한가요?
UserControl address = new AddressControl();
NotificationObject vm = new AddressViewModel();
address.DataContext = vm;
MyTabs[0] = new TabItem()
{
Content = address;
}
ViewModel 내에서 View(AddressControl)를 구축하고 있기 때문에 물어보는 것일 뿐인데, 이는 MVVM No-No처럼 들립니다.
이건 MVVM이 아니야뷰 모델에서 UI 요소를 생성해서는 안 됩니다.
아이템을 바인딩해야 합니다.ObservableCollection에 대한 Tab의 소스이며, 이 소스에는 생성해야 하는 탭에 대한 정보가 포함된 모델이 있어야 합니다.
탭 페이지를 나타내는 VM 및 모델은 다음과 같습니다.
public sealed class ViewModel
{
public ObservableCollection<TabItem> Tabs {get;set;}
public ViewModel()
{
Tabs = new ObservableCollection<TabItem>();
Tabs.Add(new TabItem { Header = "One", Content = "One's content" });
Tabs.Add(new TabItem { Header = "Two", Content = "Two's content" });
}
}
public sealed class TabItem
{
public string Header { get; set; }
public string Content { get; set; }
}
윈도의 바인딩은 다음과 같습니다.
<Window x:Class="WpfApplication12.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<ViewModel
xmlns="clr-namespace:WpfApplication12" />
</Window.DataContext>
<TabControl
ItemsSource="{Binding Tabs}">
<TabControl.ItemTemplate>
<!-- this is the header template-->
<DataTemplate>
<TextBlock
Text="{Binding Header}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<!-- this is the body of the TabItem template-->
<DataTemplate>
<TextBlock
Text="{Binding Content}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Window>
(다른 탭에 다른 내용을 표시하려면DataTemplates
. 각 탭의 뷰 모델은 자체 클래스로 하거나 커스텀을 만듭니다.DataTemplateSelector
올바른 템플릿을 선택합니다.)
데이터 템플릿 내의 사용자 제어:
<TabControl
ItemsSource="{Binding Tabs}">
<TabControl.ItemTemplate>
<!-- this is the header template-->
<DataTemplate>
<TextBlock
Text="{Binding Header}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<!-- this is the body of the TabItem template-->
<DataTemplate>
<MyUserControl xmlns="clr-namespace:WpfApplication12" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
프리즘에서는 보통 탭 컨트롤을 영역 컨트롤로 하여 바인딩된 탭 페이지 집합을 제어할 필요가 없습니다.
<TabControl
x:Name="MainRegionHost"
Regions:RegionManager.RegionName="MainRegion"
/>
이제 MainRegion 영역에 자체 등록하여 보기를 추가할 수 있습니다.
RegionManager.RegisterViewWithRegion( "MainRegion",
( ) => Container.Resolve<IMyViewModel>( ).View );
그리고 여기 여러분은 프리즘의 특별한 점을 볼 수 있습니다.View는 ViewModel에 의해 설치됩니다.이 경우 제어 컨테이너의 반전(예: Unity 또는 MEF)을 통해 ViewModel을 해결합니다.ViewModel은 생성자 주입을 통해 View를 주입받아 View의 데이터 컨텍스트로 설정합니다.
또는 뷰의 유형을 영역 컨트롤러에 등록하는 방법도 있습니다.
RegionManager.RegisterViewWithRegion( "MainRegion", typeof( MyView ) );
이 방법을 사용하면 런타임 중에 나중에 보기를 만들 수 있습니다. 예를 들어 다음과 같습니다.
IRegion region = this._regionManager.Regions["MainRegion"];
object mainView = region.GetView( MainViewName );
if ( mainView == null )
{
var view = _container.ResolveSessionRelatedView<MainView>( );
region.Add( view, MainViewName );
}
보기 유형을 등록했으므로 보기가 올바른 영역에 배치됩니다.
UI와 ViewModel을 분리하는 컨버터를 사용하고 있습니다.이거는 다음과 같습니다.
<TabControl.ContentTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Tab,Converter={StaticResource TabItemConverter}"/>
</DataTemplate>
</TabControl.ContentTemplate>
Tab은 내 TabItemViewModel의 열거형이며 TabItemConverter는 이를 실제 UI로 변환합니다.
TabItemConverter에서 값을 가져오고 필요한 사용자 컨트롤을 반환합니다.
내 솔루션은 View Models를 직접 사용하기 때문에 누군가에게 도움이 될 수 있을 것 같습니다.
먼저 App.xaml 파일의 ViewModels에 Views를 바인드합니다.
<Application.Resources>
<DataTemplate DataType="{x:Type local:ViewModel1}">
<local:View1/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModel2}">
<local:View2/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModel3}">
<local:View3/>
</DataTemplate>
</Application.Resources>
MainViewModel은 다음과 같습니다.
public class MainViewModel : ObservableObject
{
private ObservableCollection<ViewModelBase> _viewModels = new ObservableCollection<ViewModelBase>();
public ObservableCollection<ViewModelBase> ViewModels
{
get { return _viewModels; }
set
{
_viewModels = value;
OnPropertyChanged();
}
}
private ViewModelBase _currentViewModel;
public ViewModelBase CurrentViewModel
{
get { return _currentViewModel; }
set
{
_currentViewModel = value;
OnPropertyChanged();
}
}
private ICommand _closeTabCommand;
public ICommand CloseTabCommand => _closeTabCommand ?? (_closeTabCommand = new RelayCommand(p => closeTab()));
private void closeTab()
{
ViewModels.Remove(CurrentViewModel);
CurrentViewModel = ViewModels.LastOrDefault();
}
private ICommand _openTabCommand;
public ICommand OpenTabCommand => _openTabCommand ?? (_openTabCommand = new RelayCommand(p => openTab(p)));
private void openTab(object selectedItem)
{
Type viewModelType;
switch (selectedItem)
{
case "1":
{
viewModelType = typeof(ViewModel1);
break;
}
case "2":
{
viewModelType = typeof(ViewModel2);
break;
}
default:
throw new Exception("Item " + selectedItem + " not set.");
}
displayVM(viewModelType);
}
private void displayVM(Type viewModelType)
{
if (!_viewModels.Where(vm => vm.GetType() == viewModelType).Any())
{
ViewModels.Add((ViewModelBase)Activator.CreateInstance(viewModelType));
}
CurrentViewModel = ViewModels.Single(vm => vm.GetType() == viewModelType);
}
}
}
메인 윈도XAML:
<Window.DataContext>
<local:MainWindowViewModel x:Name="vm"/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Menu Grid.Row="0">
<MenuItem Header="1" Command="{Binding OpenTabCommand}" CommandParameter="1"/>
<MenuItem Header="2" Command="{Binding OpenTabCommand}" CommandParameter="2"/>
<MenuItem Header="3" Command="{Binding OpenTabCommand}" CommandParameter="3"/>
</Menu>
<TabControl Grid.Row="1" ItemsSource="{Binding ViewModels}" SelectedItem="{Binding CurrentViewModel}">
<TabControl.ItemTemplate>
<DataTemplate DataType="{x:Type MVVMLib:ViewModelBase}">
<TextBlock Text="{Binding Title}">
<Hyperlink Command="{Binding RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}, Path=DataContext.CloseWindowCommand}">X</Hyperlink>
</TextBlock>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
</Grid>
알기 쉽게 번역했는데 오타가 있을 수 있어요.
언급URL : https://stackoverflow.com/questions/5650812/how-do-i-bind-a-tabcontrol-to-a-collection-of-viewmodels
'programing' 카테고리의 다른 글
사용자가 [Cancel InputBox VBA Excel]를 선택했는지 여부를 검출하는 방법 (0) | 2023.04.09 |
---|---|
어떤 git repo에서 (복제가 아닌) 다른 git repo로 일부 파일을 이동하는 방법 (0) | 2023.04.09 |
64비트 Windows에서의 cURL 실행 (0) | 2023.04.09 |
GUID의 SCOPE_IDENTITY()? (0) | 2023.04.09 |
VBA에서 열의 모든 고유한 값을 빠르게 가져올 수 있는 방법? (0) | 2023.04.09 |