0

Учусь MVVM. Я пытаюсь из страницы HomeView открыть страницу ProductsView

За основу взял Rachel Lim's Blog

ApplicationViewModel

public class ApplicationViewModel : ObservableObject, IPageDisplay
{
...
        public IPageViewModel GetCurrentPage()
        {
            return CurrentPageViewModel;
        }
    void IPageDisplay.ChangeViewModel(IPageViewModel newPage)
    {
        ChangeViewModel(newPage);
    }

... }

HomeViewModel

public class HomeViewModel : ObservableObject, IPageViewModel
{
        private IPageDisplay _pageDisplay;
        public HomeViewModel(IPageDisplay pageDisplay)
        {
            _pageDisplay = pageDisplay;
        }
    private ICommand _loadDashboardCommand;
    public ICommand LoadDashboardCommand
    {
        get
        {
            if (_loadDashboardCommand == null)
            {
                _loadDashboardCommand = new RelayCommand(
                    p => LoadOtherView());
            }
            return _loadDashboardCommand;
        }
    }

    private void LoadOtherView()
    {
        _pageDisplay.ChangeViewModel(new ProductsViewModel());
    }

IPageDisplay

public interface IPageDisplay
{
IPageViewModel GetCurrentPage();
void ChangeViewModel(IPageViewModel newPage);

}

По итогу получаю ошибку:

Ссылка на объект не указывает на экземпляр объекта

В строке _pageDisplay.ChangeViewModel(new ProductsViewModel()); метода LoadOtherView

В чем проблема? Я долго изучал, но не разобрался. Я только учусь, надеюсь на подробный ответ и примером. Спасибо.

Можете скачать мой проект.

Mr. Dandomi
  • 117
  • 1
  • 10
  • Всё намного проще https://ru.stackoverflow.com/a/1266479/373567 – aepot Feb 17 '22 at 18:42
  • @aepot у меня другая ситуация. Допустим, как в том примере из первой страницы открыть другую? Как в том примере из Page1ViewModel открыть Page2ViewModel? – Mr. Dandomi Feb 17 '22 at 18:44
  • С помощью команды главной вьюмодели? – aepot Feb 17 '22 at 18:45
  • Я создал интерфейс IPageDisplay и пытаюсь через него получить список моделей главной вьюмодели List<IPageViewModel> GetPageViewModels(); а потом в Page1ViewModel _pageDisplay.ChangeViewModel(_pageDisplay.GetPageViewModels()[1]);. Та жа ошибка – Mr. Dandomi Feb 17 '22 at 18:47
  • К примеру у вас есть вьюмодель просмотра данных и вьюмодель авторизации. Просматривая данные вы вдруг обнаруживаете, что юзеру надо перелогиниться. Вьюмодель, просматривающая данные вызывает событие, например через INPC, меняет свое свойство, главная ВМ подписана на это событие, видит что свойство поменялось и меняет ВМ. Другими словами дочерняя ВМ не должна никаких методов вызывать, она должна только менять свое состояние или спавнить событие, здесь уж вам решать. – aepot Feb 17 '22 at 18:52
  • @aepot хорошо. Как тогда странице авторизации поменять в основном вьюмодел currentPage? То есть как после нажатия кнопки авторизации открыть страницу профиля? – Mr. Dandomi Feb 17 '22 at 18:56
  • @aepot можно пример этого? – Mr. Dandomi Feb 17 '22 at 18:57

1 Answers1

1

Абстрагируясь от вашего примера, я бы сделал как-то так. Способ называется "циклическая зависимость". Используется редко, вследствие своих недостатков, но зато выглядит просто.

Завел бы дочерние вьюмодели. Например какая-то логин-ВМ и ВМ элемента.

public interface IViewModel
{

}

public class LoginViewModel : IViewModel { private readonly INavigationViewModel _navigator;

public LoginViewModel(INavigationViewModel navigator)
{
    _navigator = navigator;
}

public ICommand LoginCommand { get; } = new RelayCommand(_ =&gt; {
    _navigator.Navigate&lt;ItemViewModel&gt;();
});

}

public class ItemViewModel : IViewModel {

}

И главную ВМ, где живут дочерние ВМ

public interface INavigationViewModel
{
    void Navigate<T>();
}

public class NavigationViewModel : INavigationViewModel { private IViewModel[] viewModels;

public IViewModel CurrentViewModel { get; private set; } // INPC

public NavigationViewModel()
{
    viewModels = new IViewModel[]
    {
        new LoginViewModel(this),
        new ItemViewModel()
    };
    CurrentViewModel = viewModels[0];
}

public void Navigate&lt;T&gt;()
{
    CurrentViewModel = viewModels.First(vm =&gt; vm is T);
}

}

Вот и получается, что при вызове команды LoginCommand ВМ переключится на другую. Есть способы посложнее, например через вызов кастомного или INPC события из дочерней ВМ, а в главной на него подписываться.

aepot
  • 49,560
  • Та же ошибка. MainViewModel https://pastebin.com/BgM8A7qx Page1 https://pastebin.com/gqtAWv7L Page2 https://pastebin.com/TbJjuKsj IPageViewModel https://pastebin.com/qpTyjfrt

    Ошибка при навигации _navigator.Navigate<Page1ViewModel>(); Object reference not set to an instance of an object

    – Mr. Dandomi Feb 17 '22 at 20:58
  • У меня почему-то _navigator всегда null, но если я уберу null в конструкторе, то тогда я в App.xaml.css не смогу подключить – Mr. Dandomi Feb 17 '22 at 21:07
  • @Mr.Dandomi здесь нет никаких null. Я показал лишь логическую структуру, как оно может работать. И этот код 100% рабочий, просто в нем нет ничего лишнего. Как вы реально эту схему реализуете - вам решать. Я не думаю, что в 3 классах легко запутаться. Где точка сборки и как вы создаете вьюмодели - тоже на ваше усмотрение. Здесь лишь видно, где они нужны, а како они доедут до своих мест - тоже вопрос творческий, хоть IoC контейнер используйте, особо не важно как именно. – aepot Feb 17 '22 at 22:11
  • @Mr.Dandomi _selectedPageViewModel = _pageViewModels.First(vm => vm is T); вы присваиваете поле, а надо свойство, будьте внимательнее. Уберите в конструкторе = null, он недопустим там. – aepot Feb 17 '22 at 22:15
  • только сейчас понял, о чем вы говорили (про то, что перепутал поле и свойство). Но решение своей проблемы так и не нашёл. – Mr. Dandomi Sep 11 '22 at 15:59