WPF-Custom TabItem с проблемой привязки кнопки «Удалить»

Привет, я пытаюсь создать собственный TabItem с кнопкой удаления, я хочу связать свою команду модели представления с моим пользовательским свойством зависимости 'DeleteCommandProperty'. Может кто-нибудь сказать мне, что я делаю неправильно?

мой пользовательский TabControl:

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

мой пользовательский класс TabItem:

    /// <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>

person Community    schedule 27.04.2016    source источник
comment
Попробуйте сократить это до MVCE. Например, не пытайтесь привязать DeleteCommand в своем стиле, если только это не необходимо для демонстрации вашей проблемы... Вместо этого назначьте статическую команду в своем конструкторе   -  person grek40    schedule 27.04.2016
comment
Я вижу, вы каким-то образом отредактировали свой вопрос, но он далеко не минимальный (вы используете очень много шаблонов, не связанных с вашей проблемой), далеко не поддающийся проверке ( Я создал небольшой пример с вашим установщиком команд на основе стилей, и он работал просто отлично), далеко не полный (где код для customControls:MyTabControl? Кроме того, пожалуйста, не заставляйте меня использовать Blend) и вы на самом деле не объясняете наблюдаемое поведение в отличие от ожидаемого поведения, поэтому пример также не приводится.   -  person grek40    schedule 27.04.2016
comment
Я добавил свой класс TabControl и попытался объяснить, что не сработало, надеюсь, кто-то может помочь   -  person    schedule 27.04.2016


Ответы (1)


TL;DR

Я подозреваю, что ваш DataContext содержит ваш текущий элемент из MyList (что бы это ни было), поэтому установщик стиля не может получить доступ к свойству MyDeleteCommand, как вы планировали. Посмотрите в лог отладчика Visual Studio, логируется ли там исключение привязки.


Пример, который развивался из рабочего кода, пока не обнаружил возможное объяснение проблемы.

Кажется, у вас есть некоторые трудности с сокращением вашего примера, поэтому единственное, что я могу предложить вам на основе предоставленной вами информации, - это небольшой рабочий пример с подходом Style и TemplateBinding, который вы можете использовать в качестве основы для определения вашей реальной проблемы. Изменить. Объяснение в конце может быть ответом на ваш вопрос.

Обратите внимание, что вам, возможно, придется изменить пространства имен, чтобы они соответствовали настройке вашего проекта.

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 указывает текст, который отображается в заголовке. Привязка MyDeleteCommand не работает!
  2. Элемент вкладки предоставляется как MyTabItem с заголовком и значением содержимого. Установщик стиля для свойства Header не применяется, так как свойство задано явно. Установщик стиля для свойства DeleteCommand привязывается к свойству MyDeleteCommand из MyContext.
  3. Элемент вкладки предоставляется как TabItem, и, поскольку нет переопределения MyTabControl.IsItemItsOwnContainerOverride, он принимается в качестве элемента управления MyTabControl. Весь шаблон MyTabItem не применяется.

Выходные данные отладки в Visual Studio дают подсказку об основной проблеме для первого элемента вкладки:

Ошибка System.Windows.Data: 40: ошибка пути BindingExpression: свойство «MyDeleteCommand» не найдено в «объекте» «TextBlock» (Name = '')'. BindingExpression:Path=MyDeleteCommand; DataItem='TextBlock' (Имя=''); целевой элемент — «MyTabItem» (Name=''); целевое свойство — «DeleteCommand» (тип «ICommand»)

Причина в том, что текущее содержимое элемента вкладки становится новым локальным DataContext в этом сценарии, в отличие от второго элемента вкладки, где сам элемент принимается в качестве контейнера.

Решением может быть обеспечение использования правильного контекста привязки команды:

Предположим, вы дали некоторому подходящему родительскому элементу имя x:Name="_this", тогда вы можете получить доступ к родительскому элементу DataContext.

<Setter Property="DeleteCommand" Value="{Binding DataContext.MyDeleteCommand,ElementName=_this}"/>
person grek40    schedule 27.04.2016
comment
Спасибо, что указали DataContext, как вы сказали, решили проблему. - person ; 27.04.2016