programing

TabControl을 ViewModel 모음에 바인딩하려면 어떻게 해야 합니까?

megabox 2023. 4. 9. 21:18
반응형

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

반응형