gpt4 book ai didi

c# - 如何从自定义列表框中将按钮、TexBlocks 等添加到 Dynamic ObservableCollection Tabitem

转载 作者:行者123 更新时间:2023-12-03 10:59:35 24 4
gpt4 key购买 nike

我有两个网格,

  • 使用绑定(bind)在 ObservableCollection 上的 TabControl 和将关闭动态 TabItem 的 ContextMenuItem。
    <Grid>
    <TabControl Name="mainTabControl" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding ObservableCollectionTabItems}" Background="White" Margin="10,0,0,0">
    <TabControl.ItemTemplate>
    <DataTemplate>
    <TextBlock Text="{Binding Header}" >
    <TextBlock.ContextMenu>
    <ContextMenu>
    <MenuItem Header="Close" Click="MenuItemCloseTab_Click">
    </MenuItem>
    </ContextMenu>
    </TextBlock.ContextMenu>
    </TextBlock>
    </DataTemplate>
    </TabControl.ItemTemplate>
    </TabControl>
    </Grid>
  • 显示我的控件名称的列表框。
    <Grid Background="#FFD61B1B">
    <ListBox x:Name="RightListBox" SelectionMode="Single" SelectionChanged="RightListBox_OnSelectionChanged" IsSynchronizedWithCurrentItem="true" Margin="10,0,0,0">
    <ListBox.ItemTemplate>
    <DataTemplate>
    <Label Margin="10" Content="{Binding}"/>
    </DataTemplate>
    </ListBox.ItemTemplate>
    </ListBox>
    </Grid>

  • 在 MainWindow.xaml 的代码隐藏中添加一个新选项卡:
    this._vmMainWindowTabControl.AddTab("ViewOne", "Test");

    VMMainWindowTabControl.cs 是一个 ViewModel,它具有:VMBase -> INotifyPropertyChanged 类,VMParentForViews 是一个空的 ViewModel 类,VMViewTypeOne 是另一个 ViewModel,我们在构造函数中的 TabItem 上设置了标题。
    public class VMMainWindowTabControl :VMBase
    {
    private VMParentForViews vmParentForViews;

    public VMMainWindowTabControl()
    {
    ObservableCollectionTabItems = new ObservableCollection<VMParentForViews>();
    }

    public ObservableCollection<VMParentForViews> ObservableCollectionTabItems { get; set; }


    ///<summary>
    /// I'm trying to get controls to Tabitem with SelectedIndex but I have not success.
    /// </summary>
    //public int SelectedIndex
    //{
    // get
    // {
    // ICollectionView collectionView = CollectionViewSource.GetDefaultView(this.ObservableCollectionTabItems);
    // return collectionView.CurrentPosition;
    // }
    // set
    // {
    // ICollectionView collectionView = CollectionViewSource.GetDefaultView(this.ObservableCollectionTabItems);
    // collectionView.MoveCurrentToPosition(value);
    // OnPropertyChanged("SelectedIndex");
    // }
    //}

    /// <summary>
    /// Adds the tab to the TabControl
    /// </summary>
    /// <param name="viewType">Type of the view.</param>
    /// <param name="header">Header of the TabItem.</param>
    public void AddTab(string viewType, string header)
    {
    if(viewType.Equals("ViewOne"))
    {
    vmParentForViews = new VMViewTypeOne(header);
    this.ObservableCollectionTabItems.Add(vmParentForViews);
    }

    // Set the new tab to be the current tab
    ICollectionView collectionView1 = CollectionViewSource.GetDefaultView(this.ObservableCollectionTabItems);

    if (collectionView1 != null)
    {
    collectionView1.MoveCurrentTo(vmParentForViews);
    }
    }

    /// <summary>
    /// Closes the tab item.
    /// </summary>
    public void CloseTabItem(Object sender)
    {
    VMParentForViews vmParentForViews = (sender as MenuItem).DataContext as VMParentForViews;
    this.ObservableCollectionTabItems.Remove(vmParentForViews);
    }

    public void AddElement(Object sender)
    {
    // How I can do this.
    }

    }

    我的问题是,当我单击 ListBoxItem 时,我得到了这个 ListBox 的 SelectedItem。但是现在 我不知道如何引用我将添加此控件的相应 TabItem。 此 TabItem 保存在 ObservableCollection 上,但我需要单击选项卡的发件人。也许是我没有很好地搜索的其他方式。

    这是一张图片,用于解释我项目中 View 和 ViewModels 上的 TreeView 。
    enter image description here

    我正在尝试使用 VMMainWindowTabControl 中的 SelectedIndex 属性将元素添加到 TabItem 中没有成功。

    VTabItem.xaml 只是每个 TabItem 上显示的 Canvas 项。

    创建新标签并关闭此标签正常工作,非常感谢 Nishant Rana 的这两个教程: Creating dynamic TabItem in WPFAdding a Close Context Menu to TabItem in WPF

    非常感谢您提供的所有帮助。
    问候和新年快乐! :D

    最佳答案

    我相信你的 MVVM 越来越模糊,你可以通过绑定(bind)做更多的事情。

    我已经整理了一个我认为你所追求的例子,使用绑定(bind)、模板和 RoutedCommands 来实现你所说的你所追求的功能。

    它像这样工作......

    我的示例中有 3 个模型,MyModel1 到 MyModel3,它们基本上都是

    public class MyModel1
    {
    public string Header { get { return "One"; }}
    }

    header 为每个模型返回不同的值。

    ViewModel 也很简单

    公共(public)类 MyViewModel:INotifyPropertyChanged
    {
    私有(private)对象 selectedItem;
        public MyViewModel()
    {
    this.AvailableItems = new Collection<Type>() { typeof(MyModel1), typeof(MyModel2), typeof(MyModel3) };
    this.Items = new ObservableCollection<object>();
    }

    public Collection<Type> AvailableItems { get; set; }

    public ObservableCollection<object> Items { get; set; }

    public void AddItem(Type type)
    {
    var item = Items.FirstOrDefault(i => i.GetType() == type);
    if (item == null)
    {
    item = Activator.CreateInstance(type);
    Items.Add(item);
    }

    SelectedItem = item;
    }

    internal void RemoveItem(object item)
    {
    var itemIndex = this.Items.IndexOf(item);
    if (itemIndex > 0)
    {
    SelectedItem = Items[itemIndex - 1];
    }
    else if (Items.Count > 1)
    {
    SelectedItem = Items[itemIndex + 1];
    }

    Items.Remove(item);
    }

    public object SelectedItem
    {
    get { return selectedItem; }
    set
    {
    if (value != selectedItem)
    {
    selectedItem = value;
    OnPropertyChanged();
    }
    }
    }

    private void OnPropertyChanged([CallerMemberName]string propertyName = null)
    {
    var handler = PropertyChanged;
    if (handler != null)
    {
    handler.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    }

    ListBox 将绑定(bind)到 AvailableItems,TabControl 将绑定(bind)到 Items。

    有 3 个用户控件,每个模型一个,它们看起来都像这样
    <UserControl x:Class="StackOverflow._20933056.UserControl1" 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">
    <TextBlock Text="User Control 1" />
    </UserControl>

    后面的 View 代码实例化 ViewModel,注册一个 RoutedCommand 并处理 RoutedCommand 的事件。 .
    public partial class MainWindow : Window
    {
    public static RoutedCommand CloseItemCommand = new RoutedCommand("CloseItem", typeof(MainWindow));

    public MainWindow()
    {
    this.ViewModel = new MyViewModel();
    InitializeComponent();
    }

    public MyViewModel ViewModel { get; set; }

    private void MyListBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
    this.ViewModel.AddItem(e.AddedItems.OfType<Type>().FirstOrDefault());
    }

    private void CommandBinding_OnCanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
    e.CanExecute = true;
    e.Handled = true;
    }

    private void CommandBinding_OnExecuted(object sender, ExecutedRoutedEventArgs e)
    {
    this.ViewModel.RemoveItem(e.Parameter);
    }

    稍后将详细介绍 RoutedCommand。

    乐趣在于 Xaml,这很简单
    <Window x:Class="StackOverflow._20933056.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:this="clr-namespace:StackOverflow._20933056"
    DataContext="{Binding RelativeSource={RelativeSource Self}, Path=ViewModel}"
    Title="MainWindow" Height="600" Width="800">
    <Window.Resources>
    <ContextMenu x:Key="TabContextMenu">
    <MenuItem Header="Close" Command="{x:Static this:MainWindow.CloseItemCommand}" CommandParameter="{Binding}" />
    </ContextMenu>
    <DataTemplate DataType="{x:Type this:MyModel1}">
    <this:UserControl1 DataContext="{Binding}" ContextMenu="{StaticResource TabContextMenu}" />
    </DataTemplate>
    <DataTemplate DataType="{x:Type this:MyModel2}">
    <this:UserControl2 DataContext="{Binding}" ContextMenu="{StaticResource TabContextMenu}" />
    </DataTemplate>
    <DataTemplate DataType="{x:Type this:MyModel3}">
    <this:UserControl2 DataContext="{Binding}" ContextMenu="{StaticResource TabContextMenu}" />
    </DataTemplate>
    <Style TargetType="{x:Type TabItem}">
    <Setter Property="Header" Value="{Binding Path=Header}" />
    <Setter Property="ContextMenu" Value="{StaticResource TabContextMenu}" />
    </Style>
    </Window.Resources>

    <Window.CommandBindings>
    <CommandBinding Command="{x:Static this:MainWindow.CloseItemCommand}" CanExecute="CommandBinding_OnCanExecute" Executed="CommandBinding_OnExecuted" />
    </Window.CommandBindings>
    <Grid>
    <Grid>
    <Grid.ColumnDefinitions>
    <ColumnDefinition Width="200" />
    <ColumnDefinition />
    </Grid.ColumnDefinitions>

    <ListBox x:Name="MyListBox" ItemsSource="{Binding Path=AvailableItems}" SelectionChanged="MyListBox_OnSelectionChanged">
    <ListBox.ItemTemplate>
    <DataTemplate DataType="{x:Type system:Type}"><TextBlock Text="{Binding Path=Name}" /></DataTemplate>
    </ListBox.ItemTemplate>
    </ListBox>
    <TabControl Grid.Column="1" ItemsSource="{Binding Path=Items}" SelectedItem="{Binding Path=SelectedItem, Mode=TwoWay}" />
    </Grid>
    </Grid>
    </Window>

    正如我之前所说,ListBox 绑定(bind)到 ViewModel 的 AvailableItems,而 TabControl 绑定(bind)到 Items。 TabControl 还绑定(bind)到 SelectedItem,它允许从 View 模型中控制选定的选项卡。

    ListBox.SelectionChanged 事件在后面的代码中处理,以调用 ViewModel.AddItem 方法,该方法添加或选择选项卡项。

    注意:TabControl 中的每个选项卡实际上是一个模型对象,而不是 TabItem 控件。有 DataTemplates定义为允许 TabControl 在 TabItem 的内容中正确插入每个 Model 所需的 UserControl。

    TabItem 管理是通过 ViewModel 中的 AddItem 和 RemoveItem 方法进行的。

    现在,回到 RoutedCommand .

    RoutedCommand 允许定义一个命令,该命令可以从 VisualTree 中的某个地方触发,然后在其他地方获取它,而接收处理程序不关心它的来源。

    因此,在 Xaml 中有一个名为 TabContextMenu 的 ContextMenu 资源。该资源通过样式绑定(bind)到所有 TabItem 的 ContextMenu。它还绑定(bind)了 DataTemplates 中每个 UserControl 的 ContextMenu。

    在 ContextMenu 中有一个 MenuItem,它将触发 RoutedCommand,并与它一起传递当前的 DataContext(模型)。

    MainWindow 有一个 CommandBinding接收并处理 RoutedCommand。在 CommandBindings Executed 事件处理程序中,调用 ViewModel.RemoveItem 方法。

    这里的代码几乎是我示例的完整代码库。此答案中仅缺少 MyModel2、MyModel3、UserControl2 和 UserControl3 的实现,它们可以从 MyModel1 和 UserControl1 中推断出来。您应该能够在新的 C#/WPF 项目中重现该示例。

    我希望这有帮助。

    关于c# - 如何从自定义列表框中将按钮、TexBlocks 等添加到 Dynamic ObservableCollection Tabitem,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20933056/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com