0

Разбираюсь с WPF, .NET-7. Пытаюсь сделать что-то типа меню с табами, стараюсь реализовать через TabControl, в котором возможно будет динамически добавлять и удалять определённые TabItem-ы. Шаблон итемов сделал, чтобы при добавлении добавлялись кнопки, сейчас пытаюсь через команды их добавлять и удалять, но так работает только с нижними кнопками добавления/удаления, но только не работает удаление, хотя команда срабатывает. В чём проблема и как лучше реализовать данную задачу?

TabModel.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace TestProject { public class TabModel: INotifyPropertyChanged { private string title;

public string Title
{
    get { return title; }
    set
    {
        title = value;
        OnPropertyChanged("Title");
    }
}

public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string prop = "")
{
    if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(prop));
}

} }

ApplicationViewModel.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace TestProject { public class ApplicationViewModel : INotifyPropertyChanged { private TabModel selectedTab;

public ObservableCollection<TabModel> Tabs { get; set; }

private RelayCommand addCommand;
public RelayCommand AddCommand
{
    get
    {
        return addCommand ??
          (addCommand = new RelayCommand(obj =>
          {
              MessageBox.Show("add tab");
              TabModel tab = new TabModel{ Title = "New Tab" };
              Tabs.Add(tab);
              SelectedTab = tab;
          }));
    }
}

private RelayCommand removeCommand;
public RelayCommand RemoveCommand
{
    get
    {
        return removeCommand ??
          (removeCommand = new RelayCommand(obj =>
          {
              MessageBox.Show("remove tab");
              TabModel tab = obj as TabModel;
              if (tab != null)
              {
                  Tabs.Remove(tab);
              }
          },
         (obj) => Tabs.Count > 0));
    }
}

public TabModel SelectedTab
{
    get { return selectedTab; }
    set
    {
        selectedTab = value;
        OnPropertyChanged("SelectedTab");
    }
}

public ApplicationViewModel()
{
    Tabs = new ObservableCollection<TabModel>
    {
        new TabModel { Title="Tab 1" },
        new TabModel { Title="Tab 2" },
        new TabModel { Title="Tab 3" },
        new TabModel { Title="Tab 4" }
    };
}

public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string prop = "")
{
    if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(prop));
}

} }

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace TestProject { public partial class MainWindow : Window { public MainWindow() { InitializeComponent();

        DataContext = new ApplicationViewModel();
    }
}

}

RelayCommand.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace TestProject { public class RelayCommand : ICommand { private Action<object> execute; private Func<object, bool> canExecute;

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public RelayCommand(Action&lt;object&gt; execute, Func&lt;object, bool&gt; canExecute = null)
    {
        this.execute = execute;
        this.canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return this.canExecute == null || this.canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        this.execute(parameter);
    }
}

}

MainWindow.xaml

<Window x:Class="TestProject.MainWindow"
        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"
        xmlns:local="clr-namespace:TestProject"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
    </Window.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
    &lt;TabControl Grid.Column=&quot;0&quot; SelectedItem=&quot;{Binding SelectedTab}&quot; ItemsSource=&quot;{Binding Tabs}&quot;&gt;
        &lt;TabControl.ItemTemplate&gt;
            &lt;DataTemplate&gt;
                &lt;StackPanel Orientation=&quot;Horizontal&quot;&gt;
                    &lt;TextBlock Text=&quot;{Binding Path=Title}&quot;/&gt;
                    &lt;Button Command=&quot;{Binding AddCommand}&quot; Content=&quot;+&quot;/&gt;
                    &lt;Button Command=&quot;{Binding RemoveCommand}&quot;
                            CommandParameter=&quot;{Binding SelectedTab}&quot;
                            Content=&quot;-&quot;/&gt;
                &lt;/StackPanel&gt;
            &lt;/DataTemplate&gt;
        &lt;/TabControl.ItemTemplate&gt;

    &lt;/TabControl&gt;

    &lt;StackPanel Grid.Row=&quot;1&quot; Orientation=&quot;Horizontal&quot;&gt;
        &lt;Button Command=&quot;{Binding AddCommand}&quot;
                Width=&quot;50&quot;
                Height=&quot;50&quot;&gt;+&lt;/Button&gt;
        &lt;Button Command=&quot;{Binding RemoveCommand}&quot;
                CommandParameter=&quot;{Binding SelectedPhone}&quot;
                Width=&quot;50&quot;
                Height=&quot;50&quot;&gt;-&lt;/Button&gt;
    &lt;/StackPanel&gt;
&lt;/Grid&gt;

</Window>

PURPLE
  • 1
  • Не передаете ссылку на TabModel в команду, хотя там её запрашиваете и ожидаете (CommandParameter у кнопки). – OwDafuq Feb 27 '23 at 13:00
  • Проверяйте параметры, которые передаете, смотрите, то-ли вы туда отправляете, или нет. Займитесь вообще отладкой, поставьте точку остановки и все посмотрите. Также смотрите лог привязок, какие там ошибки кидаются. Вот это вот MessageBox.Show("remove tab"); - это не отладка, а мусор, который еще и нарушает MVVM) Вот давайте по порядку, у вас 2 кнопки удаления, 1 внутри <TabControl.ItemTemplate>, что сразу означает о том, что DataContext у нее будет браться от объекта TabModel. 2 у вас снаружи, но там вдруг появляется {Binding SelectedPhone}... – EvgeniyZ Feb 27 '23 at 13:03
  • Посмотрите этот ответ, думаю частично под себя без проблем возьмете. – EvgeniyZ Feb 27 '23 at 13:04
  • @EvgeniyZ Спасибо, просто тяжело пока воспринимаю, в каком контексте какой-либо элемент находится. Т.е. вроде у основного окна прописываю контекст DataContext = new ApplicationViewModel();, а дальше как контекст видит не понимал, т.е., как в моём случае, внутри <TabControl.ItemTemplate> какой контекст находится внутри тегов, относительно AppViewModel или TabModel (благодаря вашему комменту уже понял, спасибо). Спасибо ещё раз за ответы, буду разбираться дальше :) – PURPLE Feb 27 '23 at 18:50
  • Любой ItemsControl (контрол, который выводит коллекцию), будет для своего шаблона задавать DataContext конкретному объекту коллекции, а не общей VM. В любом случае, научитесь отлаживать свой проект, смотрите логи, там куча всего полезного. В вашем случае, например писало бы "нет публичного свойства RemoveCommand в классе TabModel", сразу вот видим что не так. – EvgeniyZ Feb 27 '23 at 19:01
  • Да, согласен, сразу тогда понятно. Я просто видел ворнинги, что где-то пишет, что Tabs или SelectedTab не найден в xaml-файле и всё :), хотя по итогу после инициализации всё выводит, так я сильно в отладке уже не разбирался, но на заметку поставил, спасибо ещё раз) – PURPLE Feb 27 '23 at 19:09

0 Answers0