简体   繁体   English

WPF + Caliburn Micro + MVVM:TabItem处理

[英]WPF + Caliburn Micro + MVVM: TabItem handling

I am trying to make a popup window which contains tabcontrol using WPF, Caliburn Micro and MVVM pattern, no need to use code behind in this case. 我正在尝试使用WPF,Caliburn Micro和MVVM模式制作一个包含tabcontrol的弹出窗口,在这种情况下无需使用任何代码。 The tabcontrol contains more than 1 tabitem. tabcontrol包含1个以上的tabitem。 After digging some threads in SO for a while I combine the found solutions and can create the popup window and fill it with tabcontrol and its tabitems ( I take it from this thread ). 在SO中挖掘了一些线程一段时间后,我结合了找到的解决方案,可以创建弹出窗口,并用tabcontrol及其Tabitems填充它( 我从此线程中获取它 )。

Problem: the tabitems show content (text) from view model but show no content from view. 问题:选项卡显示视图模型中的内容(文本),但不显示视图中的内容。 Please take a look the code attached here. 请查看此处附带的代码。

Expected I expect to see the text "Tab Item 1" as TabItem1 header and the text "Selection One" as content in TabItem1. 期望我希望看到文本“ Tab Item 1”作为TabItem1标头,而文本“ Selection One”作为TabItem1中的内容。 Right now both the header and the content of TabItems contains same text "Tab Item 1". 现在,TabItems的标题和内容都包含相同的文本“ Tab Item 1”。

Am I missing something? 我想念什么吗? I attach here the code. 我在这里附上代码。 Please feel free change the code. 请随时更改代码。 Any hints are highly appreciated. 任何提示都受到高度赞赏。

Sequence of code: 代码顺序:

  • TabItem1, TabItem2 view and viewmodel TabItem1,TabItem2视图和视图模型
  • ITabItem ITabItem
  • PopUp window view and viewmodel 弹出窗口视图和视图模型
  • AppBootstrapper, Shell view and viewmodel AppBootstrapper,Shell视图和视图模型

TabItem1ViewModel (TabItem2ViewModel has same content) TabItem1ViewModel (TabItem2ViewModel具有相同的内容)

public class TabItem1ViewModel : Screen, ITabItem
{
    public TabItem1ViewModel() => DisplayName = "Tab Item 1";
}

Attention: in following TabItem view I use Label to show the text "Selection One", but this text doesn't appear at all. 注意:在下面的TabItem视图中,我使用Label显示文本“选择一”,但此文本根本没有出现。 Only the display name "Tab Item 1" defined in view model appears as content of TabItem1 在视图模型中定义的仅显示名称“标签项1”显示为TabItem1的内容

TabItem1View (TabItem2View has same content) TabItem1View (TabItem2View具有相同的内容)

<UserControl
    x:Class="CmTabControl.Views.TabItem1View"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    d:DesignHeight="450"
    d:DesignWidth="800"
    mc:Ignorable="d">
    <Grid>
        <TabItem x:Name="TabItem1" Header="{Binding Path=DisplayName}">
            <Grid x:Name="TabItem1ContentGrid">
                <Label HorizontalAlignment="Left"                     
                    VerticalAlignment="Top"
                    Content="Selection One" />
            </Grid>
        </TabItem>
    </Grid>
</UserControl>

ITabItem (yes, it is only empty interface) ITabItem (是的,它只是一个空接口)

public interface ITabItem : IScreen
{
}

PopUpViewModel PopUpViewModel

public class PopUpViewModel : Screen
{
    public PopUpViewModel()
    {
        TabItems.Add(new TabItem1ViewModel());
        TabItems.Add(new TabItem2ViewModel());
    }

    private readonly BindableCollection<ITabItem> _tabItems = new BindableCollection<ITabItem>();
    public BindableCollection<ITabItem> TabItems
    {
        get => _tabItems;
        set
        {
            if (_tabItems == null)
            {
                return;
            }
            _tabItems.Clear();
            _tabItems.AddRange(value);
            NotifyOfPropertyChange(() => TabItems);
        }
    }
}

PopUpView PopUpView

<Window
    x:Class="CmTabControl.Views.PopUpView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:CmTabControl.Views"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="PopUpView"
    Width="800"
    Height="450"
    mc:Ignorable="d">
    <Grid Margin="3,8,3,3" HorizontalAlignment="Stretch"
        VerticalAlignment="Stretch">
        <TabControl x:Name="TabItems" />
    </Grid>
</Window>

AppBootstrapper AppBootstrapper

public class AppBootstrapper : BootstrapperBase
{
    private readonly SimpleContainer _container = new SimpleContainer();

    public AppBootstrapper() => Initialize();
    protected override object GetInstance(Type serviceType, string key) => _container.GetInstance(serviceType, key);
    protected override IEnumerable<object> GetAllInstances(Type serviceType) => _container.GetAllInstances(serviceType);
    protected override void BuildUp(object instance) => _container.BuildUp(instance);

    protected override void Configure()
    {
        base.Configure();

        _container.Singleton<IWindowManager, WindowManager>();
        _container.Singleton<IEventAggregator, EventAggregator>();
        _container.Singleton<ShellViewModel>();
        _container.PerRequest<PopUpViewModel>(); // Or Singleton if there'll only ever be one
    }

    protected override void OnStartup(object sender, StartupEventArgs e)
    {
        base.OnStartup(sender, e);
        DisplayRootViewFor<ShellViewModel>();
    }
}

ShellViewModel ShellViewModel

public class ShellViewModel : Conductor<object>.Collection.AllActive
{
    private IWindowManager _windowManager;

    public ShellViewModel(PopUpViewModel popUpVm)
    {
        DisplayName = "Shell Window";
        PopUpViewModel = popUpVm;
    }

    public PopUpViewModel PopUpViewModel { get; set; }

    public sealed override void ActivateItem(object item) => base.ActivateItem(item);

    public void OpenPopUp()
    {
        ActivateItem(PopUpViewModel);
        if (_windowManager == null) _windowManager = new WindowManager();
        _windowManager.ShowDialog(PopUpViewModel, null, null);
    }

    public sealed override string DisplayName { get; set; }
}

ShellView ShellView

<UserControl
    x:Class="CmTabControl.Views.ShellView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    d:DesignHeight="300"
    d:DesignWidth="300"
    mc:Ignorable="d">
    <Grid Width="300" Height="300"
        HorizontalAlignment="Center" VerticalAlignment="Center">
        <Button x:Name="OpenPopUp" Width="100" Height="35"
            Content="Open Popup" />
    </Grid>
</UserControl>

Added: Screenshot of Live Visual Tree. 补充:实时视觉树的屏幕截图。 在此处输入图片说明

I found a solution that uses Templates: 我找到了使用模板的解决方案:

PopUpViewModel add SelectedTab : PopUpViewModel添加SelectedTab

public sealed class PopUpViewModel : Screen
{
    private readonly BindableCollection<ITabItem> _tabItems = new BindableCollection<ITabItem>();
    private IScreen _selectedTab;


    public PopUpViewModel()
    {
        DisplayName = "Popup";
        TabItems.Add(new TabItem1ViewModel());
        TabItems.Add(new TabItem2ViewModel());
        SelectedTab = TabItems.FirstOrDefault();
    }


    public BindableCollection<ITabItem> TabItems
    {
        get => _tabItems;
        set
        {
            if(_tabItems == null)
                return;
            _tabItems.Clear();
            _tabItems.AddRange(value);
            NotifyOfPropertyChange(() => TabItems);
        }
    }

    public IScreen SelectedTab
    {
        get => _selectedTab;
        set
        {
            _selectedTab = value;
            NotifyOfPropertyChange();
        }
    }
}

PopUpView : PopUpView

<Grid Margin="3, 8, 3, 3"
      HorizontalAlignment="Stretch"
      VerticalAlignment="Stretch">
    <TabControl ItemsSource="{Binding TabItems}"
                SelectedItem="{Binding SelectedTab,
                               UpdateSourceTrigger=PropertyChanged}">
        <TabControl.ItemTemplate>
            <DataTemplate>
                <Label Content="{Binding DisplayName}" />
            </DataTemplate>
        </TabControl.ItemTemplate>
        <TabControl.ContentTemplate>
            <DataTemplate>
                <ContentControl cal:View.Model="{Binding}" />
            </DataTemplate>
        </TabControl.ContentTemplate>
    </TabControl>
</Grid>

Now you only add the TabItem content to your pages, TabItem1View : 现在,您仅将TabItem内容添加到页面TabItem1View

<UserControl x:Class="WpfTestApp.Views.Tabs.TabItem1View"
             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"
             xmlns:local="clr-namespace:WpfTestApp.Views.Tabs"
             mc:Ignorable="d"
             d:DesignHeight="450"
             d:DesignWidth="800">

    <Grid x:Name="TabItem1ContentGrid">
        <Label HorizontalAlignment="Left"
               VerticalAlignment="Top"
               Content="Selection One" />
    </Grid>
</UserControl>

Edit: 编辑:

SelectedTab is just there so the first tab is selected by default. SelectedTab就在其中,因此默认情况下选择第一个选项卡。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM