一个主-细节TabControl绑定(1:n),使用parent SelectedItem和iccommand



我有XAML相关的问题,我试图研究一个徒劳的答案。我已经对XAML的相关问题进行了评论。在我看来,由于我试图安排事情的方式,这个问题看起来更复杂。

基本上,我在TabControl标头中使用了一个主视图模型,然后在内容区域中,我将显示来自主视图模型的项目列表。我只是不知道该怎么做。这是主要问题。然而,我怀疑我的下一个和最终目标可能会考虑到如何考虑这一点,所以我也添加了它们。剩下的代码是为了完整起见。

<StackPanel>
    <TabControl x:Name="mainsTabControl"
             IsSynchronizedWithCurrentItem="True"
             IsEnabled="True"
             Visibility="Visible"
             ItemsSource="{Binding Path=Mains}">
        <!-- How to select a different background for the selected header? Note that the background color is "selected tab" if MainContentViewModel.IsActive is not TRUE.
             If it is, a different color is chosen. Here this fact is just emulated with IsEnabled property due to well, multi-binding to the rescue (and a converter)? -->
        <!-- Is there a clean way to use ICommand binding (RelayCommand) to check if it is OK to change the tab and if necessary, present a dialogue asking for the change? -->
        <TabControl.ItemContainerStyle>
            <Style TargetType="{x:Type TabItem}">
                <Setter Property="IsEnabled" Value="{Binding IsActive}"/>
            </Style>
        </TabControl.ItemContainerStyle>
        <!-- This binds to every item in the MainViewModel.Mains collection. Note the question about background color. -->
        <TabControl.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBlock Text="{Binding Name}" HorizontalAlignment="Center"/>
                </StackPanel>
            </DataTemplate>
        </TabControl.ItemTemplate>
        <TabControl.ContentTemplate>
            <DataTemplate>
                <!-- This binding gives reference to the selected MainContentViewModel. -->
                <StackPanel Orientation="Horizontal" VerticalAlignment="Top" Margin="10" DataContext="{Binding ElementName=mainsTabControl, Path=SelectedItem, Mode=OneWay}">
                    <ItemsControl  ItemsSource="{Binding Path=CustomItems}">
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <StackPanel Orientation="Vertical"/>
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Name}"/>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                </StackPanel>
            </DataTemplate>
        </TabControl.ContentTemplate>
    </TabControl>
</StackPanel>

using GalaSoft.MvvmLight;
using System.Collections.ObjectModel;
using System.Linq;
namespace WpfDependencyInjection.ViewModel
{
public class MainContentViewModel: ViewModelBase
{
    private ObservableCollection<CustomItemViewModel> customItems;
    private mainContentDto MainContent { get; set; }
    public string Name { get; }

    public bool isActive;

    public MainContentViewModel(Engine engine, mainContentDto mainContent)
    {
        MainContent = mainContent;
        Name = MainContent.Name;
        IsActive = true;
        //The custom items belonging to this main content.
        var customItems = engine.CustomItemContents.Where(i => i.MainContentId == MainContent.Id).Select(i => new CustomItemViewModel(engine, i));
        CustomItems = new ObservableCollection<CustomItemViewModel>(customItems);
    }

    public ObservableCollection<CustomItemViewModel> CustomItems
    {
        get
        {
            return customItems;
        }
        set
        {
            customItems = value;
            RaisePropertyChanged(nameof(CustomItems));
        }
    }

    public bool IsActive
    {
        get
        {
            return isActive;
        }
        private set
        {
            isActive = value;
            RaisePropertyChanged(nameof(IsActive));
        }
    }
}
}
public class CustomItemViewModel: ViewModelBase
{
    private Engine Engine { get; }
    private ItemTypeDto  CustomItem { get; set; }
    public string Name { get; }        

    public CustomItemViewModel(Engine engine, ItemTypeDto customItem)
    {
        Engine = engine;
        CustomItem = customItem;
        Name = customItem.Name;
    }
}
namespace WpfDependencyInjection
{    
public class Engine
{
    public string Name { get; } = "EngineMan";
    public List<mainContentDto> MainContents { get; set; } = new List<mainContentDto>(new[]
    {
        new mainContentDto { Name = "Main One", Id = Guid.Parse("C51AC758-504B-4914-92DC-5EBE9A1F39E1"), Version = 1 },
        new mainContentDto { Name = "Main Two", Id = Guid.Parse("C51AC758-504B-4914-92DC-5EBE9A1F39E2"), Version = 1 }
    });

    public List<ItemTypeDto> CustomItemContents { get; set; } = new List<ItemTypeDto>(new ItemTypeDto[]
    {            
        new ItemType1Dto { MainContentId = Guid.Parse("C51AC758-504B-4914-92DC-5EBE9A1F39E1"), Name = "ItemType1Dto I", Id = Guid.NewGuid(), Version = 1 },
        new ItemType2Dto { MainContentId = Guid.Parse("C51AC758-504B-4914-92DC-5EBE9A1F39E1"), Name = "ItemType2Dto I", Id = Guid.NewGuid(), Version = 1 },
        new ItemType2Dto { MainContentId = Guid.Parse("C51AC758-504B-4914-92DC-5EBE9A1F39E2"), Name = "ItemType2Dto 2", Id = Guid.NewGuid(), Version = 1 }
    });

    public Engine()
    {
    }
}
}

<edit:绑定部分解决了,但没有解决iccommand部分。>

试试这个:

<TabControl x:Name="mainsTabControl"
            IsEnabled="True"
            IsSynchronizedWithCurrentItem="True"
            ItemsSource="{Binding Path=Mains}"
            SelectedItem="0"
            Visibility="Visible">
    <TabControl.ItemContainerStyle>
        <Style TargetType="{x:Type TabItem}">
            <Setter Property="IsSelected" Value="{Binding IsSelected}" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type TabItem}">
                        <Border Background="{TemplateBinding Background}">
                            <ContentPresenter ContentSource="Header" />
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="IsSelected" Value="True">
                    <Setter Property="Background" Value="HotPink" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </TabControl.ItemContainerStyle>
    <TabControl.ItemTemplate>
        <DataTemplate DataType="{x:Type local:MainContentViewModel}">
            <Button Background="{x:Null}"
                    Command="{Binding SomeCommand}"
                    Content="{Binding Name}"
                    FocusVisualStyle="{x:Null}" />
        </DataTemplate>
    </TabControl.ItemTemplate>
    <TabControl.ContentTemplate>
        <DataTemplate DataType="{x:Type local:MainContentViewModel}">
            <ItemsControl Margin="10"
                          VerticalAlignment="Top"
                          ItemsSource="{Binding Path=CustomItems}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel Orientation="Vertical" />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate DataType="{x:Type local:CustomItem}">
                        <TextBlock Text="{Binding Name}" />
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </DataTemplate>
    </TabControl.ContentTemplate>
</TabControl>

命令:

可能有更简洁的方法,但这里我们将TabItemIsSelected绑定到视图模型的IsSelected属性。这允许有一个命令询问是否可以导航,如果可以,则将IsSelected设置为true。

背景:

我们也重新模板的表项,使背景工作,我们想要的。如果您使用Snoop检查,WPF将在选中项目时插入额外的边框。

旁注1:

不要那样把TabControl放进StackPanel里。一个StackPanel大小的内容,将杀死滚动和绘制控件外。同时这也是有代价的,一个深度的视觉树并不便宜。ItemTemplate和其他地方也一样。事实上,StackPanel很少对任何事情都是正确的:)

旁注2:

如果你在DataTemplate中指定DataType,你会得到智能感知和一些编译时检查。

最新更新