具有删除按钮绑定问题的 WPF 自定义 TabItem

WPF-Custom TabItem with Delete Button Binding Issue

您好,我正在尝试创建一个带有删除按钮的自定义 TabItem,我想将我的视图模型命令绑定到我的自定义依赖项 属性 'DeleteCommandProperty'。有人可以告诉我我做错了什么吗?

我的自定义选项卡控件:

    /// <summary>
    /// TabControl withCustom TabItem
    /// </summary>
    public class MyTabControl:TabControl
    {
        /// <summary>
        /// TabItem override
        /// </summary>
        /// <returns></returns>
        protected override DependencyObject GetContainerForItemOverride()
        {
            return new MyTabItem();
        }
    }

我的自定义 TabItem class :

    /// <summary>
    /// Custom TabItem
    /// </summary>
    public class MyTabItem:TabItem
    {
        /// <summary>
        /// Delete Command 
        /// </summary>
        public static DependencyProperty DeleteCommandProperty = DependencyProperty.Register(
           "DeleteCommand",typeof(ICommand),typeof(MyTabItem));

        /// <summary>
        /// Delete
        /// </summary>
        public ICommand DeleteCommand
        {
            get { return (ICommand)GetValue(DeleteCommandProperty); }
            set { SetValue(DeleteCommandProperty, value); }
        }
    }

当我像这样直接绑定 DeleteCommand 时,我的 ViewModel 中的命令被执行

 <customControls:MyTabControl>
            <customControls:MyTabItem Header="Test" DeleteCommand="{Binding DeleteStudiengangCommand}" Template="{DynamicResource MyTabItemControlTemplate}"/>
        </customControls:MyTabControl>

bu 当尝试通过这样的样式绑定 deleteCommand 但它不起作用时:

 <Style TargetType="customControls:MyTabItem">
                        <Setter Property="Template" Value="{DynamicResource MyTabItemControlTemplate}"/>
                        <Setter Property="DeleteCommand" Value="{Binding MyDeleteCommand}"/>
                    </Style>



<customControls:MyTabControl ItemsSource="{Binding MyList}" SelectedItem="{Binding SelectedItem}" SelectedIndex="0">
                    <customControls:MyTabControl.ContentTemplate>
                        <DataTemplate>
                            <ItemsControl ItemsSource="{Binding Value}" >
                                <ItemsControl.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <WrapPanel/>
                                    </ItemsPanelTemplate>
                                </ItemsControl.ItemsPanel>
                            </ItemsControl>
                        </DataTemplate>
                    </customControls:MyTabControl.ContentTemplate>
                </customControls:MyTabControl>

TL;DR

我怀疑您的 DataContext 包含您当前来自 MyList 的项目(无论它是什么),因此样式 setter 无法访问 MyDeleteCommand 属性正如你计划的那样。查看 visual studio 调试器日志,是否记录了绑定异常。


从工作代码演变而来的示例,直到它碰巧揭示了可能的问题解释。

看来您在减少示例方面遇到了一些困难,因此根据您提供的信息,我唯一能为您提供的是一个使用 StyleTemplateBinding 方法的小型工作示例您可以以此为基础来确定您的真正问题。 编辑:最后的解释可能就是你问题的答案。

请注意,您可能需要更改名称空间以匹配您的项目设置。

MainWindow.xaml

<Window x:Class="WpfApplication2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication2"
        Title="MainWindow" Height="350" Width="525"
        Loaded="Window_Loaded">

    <Window.Resources>
        <!-- Header template -->
        <ControlTemplate x:Key="MyTabItemControlTemplate" TargetType="{x:Type local:MyTabItem}">
            <!-- Some text and the command button with template binding -->
            <StackPanel Orientation="Horizontal" Background="Aquamarine" Margin="3">
                <ContentPresenter Content="{TemplateBinding Header}" VerticalAlignment="Center" Margin="2"/>
                <Button Content="Delete" Command="{TemplateBinding DeleteCommand}" Margin="2"/>
            </StackPanel>
        </ControlTemplate>

        <!-- Setting the control template and assigning the command implementation -->
        <Style TargetType="{x:Type local:MyTabItem}">
            <Setter Property="Template" Value="{DynamicResource MyTabItemControlTemplate}"/>
            <Setter Property="DeleteCommand" Value="{Binding MyDeleteCommand}"/>
            <Setter Property="Header" Value="Default Header Text"/>
        </Style>
    </Window.Resources>

    <Grid>
        <local:MyTabControl ItemsSource="{Binding MyTabItemList}"/>
    </Grid>
</Window>

MainWindow.xaml.cs

/// <summary>
/// TabControl withCustom TabItem
/// </summary>
public class MyTabControl : TabControl
{
    /// <summary>
    /// TabItem override
    /// </summary>
    protected override DependencyObject GetContainerForItemOverride()
    {
        return new MyTabItem();
    }
}

public class MyTabItem : TabItem
{
    /// <summary>
    /// Delete Command 
    /// </summary>
    public static DependencyProperty DeleteCommandProperty = DependencyProperty.Register(
       "DeleteCommand", typeof(ICommand), typeof(MyTabItem));

    /// <summary>
    /// Delete
    /// </summary>
    public ICommand DeleteCommand
    {
        get { return (ICommand)GetValue(DeleteCommandProperty); }
        set { SetValue(DeleteCommandProperty, value); }
    }
}

public class MyCommand : ICommand
{
    public void Execute(object parameter)
    {
        MessageBox.Show("Hello WPF", "Message");
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public event EventHandler CanExecuteChanged { add { } remove { } }
}

public class MyContext
{
    public ICommand MyDeleteCommand { get; set; }

    public List<object> MyTabItemList { get; set; }
}

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        var list = new List<object>();
        list.Add(new TextBlock() { Text = "Test 1" });
        list.Add(new MyTabItem() { Content = "Test Content 2", Header = "Test Header 2" });
        list.Add(new TabItem() { Content = "Test Content 3", Header = "Test Header 3" });
        this.DataContext = new MyContext()
        {
            MyTabItemList = list,
            MyDeleteCommand = new MyCommand()
        };
    }
}

示例总结: 您会看到三个不同的选项卡,每个选项卡都有其独特的构造和外观:

  1. 选项卡项是通过 GetContainerForItemOverride 方法创建的,Header 属性 的样式 setter 指定 header 中出现的文本. MyDeleteCommand 绑定无效!
  2. 选项卡项以 MyTabItem 形式提供,具有 header 和内容值。 Header 属性 的样式 setter 不适用,因为 属性 是明确设置的。 DeleteCommand 属性 的样式 setter 绑定到 MyContext.
  3. MyDeleteCommand 属性
  4. 选项卡项作为 TabItem 提供,并且由于没有覆盖 MyTabControl.IsItemItsOwnContainerOverride,这被接受为 MyTabControl 的控制项。整个 MyTabItem 模板不适用。

visual studio 中的调试输出给出了有关第一个选项卡项的潜在问题的提示:

System.Windows.Data Error: 40 : BindingExpression path error: 'MyDeleteCommand' property not found on 'object' ''TextBlock' (Name='')'. BindingExpression:Path=MyDeleteCommand; DataItem='TextBlock' (Name=''); target element is 'MyTabItem' (Name=''); target property is 'DeleteCommand' (type 'ICommand')

原因是,在这种情况下,当前选项卡项内容成为新的本地 DataContext,这与第二个选项卡项不同,在第二个选项卡项中,项目本身被接受为容器。

一个解决方案可能是确保在命令绑定上使用正确的上下文:

假设你给一些合适的parent元素起一个名字x:Name="_this",那么你就可以访问parentDataContext.

<Setter Property="DeleteCommand" Value="{Binding DataContext.MyDeleteCommand,ElementName=_this}"/>