0

Ломаю голову уже целую ночь и так не пришел к ответу;( Надеюсь, что вы мне поможете Приступим, имеется разметка (Фото приложил) для TabControl

Проблема в том, что при переходе на ViewModel возникли проблемы с табами, самый первый при запуске приложения отрабатывает нормально, а кнопки добавить и удалить табы не работают. Ранее было событие, которое добавляла, удаляла табы и их содержимое, а при переходе на View такого события нет.

private void MyTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
   if (e.Source is TabControl)
    {
    var pos = MyTabControl.SelectedIndex;
    if (pos != 0 && pos == Tabs.Count - 1) //last tab
    {
        var tab = Tabs.Last();
        ConvertPlusToNewTab(tab);
        AddNewPlusButton();
    }
    }
}

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

public class TabControlViewModel : ObservableRecipient{
public TabControlViewModel()

{ var tab1 = new TabVM() { Header = $"Новая вкладка", Content = new ContentVM(new Frame(), new CompletionPage()), };

Tabs.Add(tab1);
AddNewPlusButton();

}

private ObservableCollection<TabVM> tabs = new();

public ObservableCollection<TabVM> Tabs { get => tabs; set => SetProperty(ref tabs, value); }

private string? _header; public string? Header { get => _header; set => SetProperty(ref _header, value); }

private int _tabSelectedItem;

public int TabSelectedItem { get => _tabSelectedItem; set => SetProperty(ref _tabSelectedItem, value); }

private int _tabSelectedIndex; public int TabSelectedIndex { get => _tabSelectedIndex; set => SetProperty(ref _tabSelectedIndex, value); }

public void SelectionChanged() { if (TabSelectedIndex != 0 && TabSelectedIndex == Tabs.Count - 1) //last tab { var tab = Tabs.Last(); ConvertPlusToNewTab(tab); AddNewPlusButton(); } } public ICommand MyTabControl_Button => new RelayCommand(OnTabCloseClick);

public void AddNewPlusButton() { var plusTab = new TabVM() { Header = "+", IsPlaceholder = true }; Tabs.Add(plusTab); }

public void ConvertPlusToNewTab(TabVM tab) { //Do things to make it a new tab. tab.Header = $"Новая вкладка"; tab.IsPlaceholder = false; tab.Content = new ContentVM(new Frame(), new CompletionPage()); }

private void OnTabCloseClick() {

if (Tabs.Count &gt; 1)
{
    if (TabSelectedItem == Tabs.Count - 2)//last tab before [+]
    {
        TabSelectedIndex--;
    }
    Tabs.RemoveAt(TabSelectedItem);
}

}

public class TabVM : ObservableRecipient { string _header; public string Header { get => _header; set => SetProperty(ref _header, value); }

bool _IsPlaceholder = false;
public bool IsPlaceholder
{
    get =&gt; _IsPlaceholder;
    set =&gt; SetProperty(ref _IsPlaceholder, value);
}

ContentVM _Content = null;
public ContentVM Content
{
    get =&gt; _Content;
    set =&gt; SetProperty(ref _Content, value);
}

}

public class ContentVM { public ContentVM(Frame frame, CompletionPage completionPage) { CompletionPage = completionPage; Frame = frame; } public CompletionPage CompletionPage { get; set; } public Frame Frame { get; set; } }}

Код XAML

  • Если вы пытаетесь писать все по MVVM, то у вас его нет. MVVM - это разделение всего на 3 мало связанных друг с другом слоя, View - весь UI (шрифты, стили, картинки, и прочее), Model - слой данных (база, работа с сайтом, ссылки на картинку, и прочее), ViewModel - связующий слой, который общается с Model, подготавливает нужные свойства для привязки View слоя. View не должен ничего знать про VM и M, аналогично и Model - не должен знать про другие слои, ViewModel - не должна знать про View. – EvgeniyZ Sep 23 '23 at 09:14
  • Теперь внимание на ваш код, у вас есть событие MyTabControl_SelectionChanged - это UI событие, которое вдруг вызывает VM методы ConvertPlusToNewTab и AddNewPlusButton, то есть у вас View знает про VM, а это не правильно, это нарушение. Ну а вообще, этот вопрос дубликат: https://ru.stackoverflow.com/a/1090412/220553 – EvgeniyZ Sep 23 '23 at 09:18
  • @EvgeniyZ если View ничего не знает про VM, то как тогда делать привязку к данным? Если View ничего не знает про VM, то зачем тогда существует x:TypeArguments, который часто используется в ReactiveUI? Если View ничего не знает про Model, то как тогда сделать EnumToVisibilityConverter или использовать любой конвертер, ведь мы ничего не знаем про VM? Выходит View все же что-то знает) – Lapish Sep 23 '23 at 14:03
  • @Lapish Привязки - V ничего не знает про VM, знает лишь название некого свойства, которое должно быть найдено, не более. C x:Type чуть сложнее, ибо повторю, V не должна ничего знать про другие слои, а значит нам нужна абстракция над VM, можно даже интерфейс, который уже и будет указан как тип. Ну а конвертор - вы не должны хотеть конвертировать в UI VM слой в что-то другое, ваша VM'ка должна дать конкретные данные, которые уже можно через конвертор перегонять, но не VM в конверторе. MVVM это про разделение всего на слои, чистый MVVM, это когда вы удалили все кроме V и проект запускается. – EvgeniyZ Sep 23 '23 at 14:28
  • Простой пример, закрыть окно по кнопке, как решите? С нарушением MVVM будет передан объект окна в класс, где есть команда, и уже в команде будет обращение к V и вызов его закрытия. Как видим, мы нарушаем MVVM, ибо VM знает про V, они тесно связаны и не могут существовать без друг друга. Но мы можем их разделить, например, создав интерфейс с простым Action Close(), реализуем этот интерфейс у VM и вызываем там Invoke(), а в V (окно), проверяем например DataContext на соответствие этому интерфейсу и задаем Action, готово, как видите, классы отвязаны, знают лишь про интерфейс, вот вам MVVM – EvgeniyZ Sep 23 '23 at 14:35
  • @EvgeniyZ у меня обычно однооконные приложения а-ля одностраничное в вебе, но даже если расмотреть вашу ситуацию, то я бы в xaml сделал кнопку с Click на xaml.cs Close(). Это если не надо ничего передавать или контролировать закрытие. Про интерфейс в такой ситуации не задумывался. >> А как бы вы поступили тогда с конвертером? Предположим есть VM и в ней свойство - enum(success, processing, error). V в зависимости от свойства должна то показывать, то не показывать какой-то контрол. Если я делаю EnumToVisConverter и передаю в него параметр - enum.Success, то я нарушаю mvvm, т.к v знает о model? – Lapish Sep 23 '23 at 15:45
  • 1
    @Lapish Я тоже делаю одностраничные приложения, пример закрытия, это лишь пример, где надо через VM закрыть окно, типичная задачка тесной связи VM и M слоев. Я вам дал решение очень базовое, обычно такую задачу делегируют отдельному "сервису", который знает о VM и о V, и по требованию создает их, и управляет ими. Enum - начнем с того, что это всеж не VM, да и не M наверно, это ближе к интерфейсам наверно, хотя в разных проектах к ним относятся по разному. А так, эта задача решается без конвертора, при помощи триггеров, чистый XAML. – EvgeniyZ Sep 23 '23 at 16:23
  • 1
    Вообще, я вам советую в такие тонкости не лезть, пишите проект так, как вам удобней, просто старайтесь делать так, чтобы V слой жил спокойно без каких либо классов от VM и M, чтобы он о них не знал, вот как я сказал выше, удалите все VM и M классы, запустите проект, если будут ошибки, значит ваш V знает о других слоях, если запустится без данных, то все супер. MVVM это набор рекомендаций, которым все по разному следуют. Я сейчас например использую в проектам WpfUI, в котором автор использует ... ViewModel { get; } в классе окна (V), это нарушение MVVM, но удобно, я оставил, использую. – EvgeniyZ Sep 23 '23 at 16:36

0 Answers0