Представление, ViewModel и DataContext

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

Для навигации между различными пользовательскими элементами управления я использовал код Рэйчел, который вы можете найти здесь. который работал нормально, пока я не внес некоторые изменения.

В боковой части экрана у меня есть главное меню (HomeViewModel()), щелкая элементы, которые я переключаю между пользовательскими элементами управления, и в каждом из этих пользовательских элементов управления есть еще одна строка меню, где я могу переключаться между другими пользовательскими элементами управления.

Но это второе меню (CateringMenuViewModel()) больше не работает. UserControl отображается, но ничего не происходит, когда я нажимаю в строке меню.

На первый взгляд я подумал, что это потому, что нет DataContext. Поэтому я добавил его в код позади следующим образом:

public CateringMenuView()
{
    InitializeComponent();
    this.DataContext = new CateringMenuViewModel(ApplicationService.Instance.EventAggregator);
}

Но это все еще не работает.

Я не понимаю, свойство Name хорошо ограничено, потому что имена отображаются в меню, а команда ChangePageCommand нет.

Модель HomeView

public class HomeViewModel : ObservableObject
{
    #region Fields

    private ICommand _changePageCommand;
    private IPageViewModel _currentPageViewModel;
    private List<IPageViewModel> _pageViewModels;

    #endregion

    public HomeViewModel()
    {
        // Add available pages
        PageViewModels.Add(new HomeOrderViewModel());
        PageViewModels.Add(new CateringMenuViewModel(ApplicationService.Instance.EventAggregator));
        PageViewModels.Add(new HomeAdminViewModel());   


        // Set starting page
        CurrentPageViewModel = PageViewModels[0];
    }

    #region Properties / Commands
}

CateringMenuViewModel

public class CateringMenuViewModel : ObservableObject, IPageViewModel
{

    protected readonly IEventAggregator _eventAggregator;

    public CateringMenuViewModel(IEventAggregator eventAggregator)
    {
        this._eventAggregator = eventAggregator;

        PageViewModels.Add(new NewRegularOrderViewModel(ApplicationService.Instance.EventAggregator));
        PageViewModels.Add(new NewDeliveryComOrderViewModel());

        PageViewModels2.Add(new FillOrderViewModel());

        // Set starting page
        CurrentUserControl = PageViewModels[0];

        this._eventAggregator.GetEvent<GoToFillOrder>().Subscribe(GoToFillOrder);

    }

    public string Name
    {
        get
        {
            return "Catering";
        }
    }

    public string imageSource
    {
        get
        {
            return "catering.ico";
        }
    }

    #region Fields

    private List<IUserContentViewModel> _pageViewModels;
    public List<IUserContentViewModel> PageViewModels
    {
        get
        {
            if (_pageViewModels == null)
                _pageViewModels = new List<IUserContentViewModel>();

            return _pageViewModels;
        }
    }

    private IUserContentViewModel _currentUserControl;
    public IUserContentViewModel CurrentUserControl
    {
        get { return _currentUserControl; }
        set
        {
            if (value != _currentUserControl)
            {
                _currentUserControl = value;
                OnPropertyChanged("CurrentUserControl");
            }
        }
    }

    #region Methods

    private void ChangeViewModel(IUserContentViewModel viewModel)
    {
        if (!PageViewModels.Contains(viewModel))
            PageViewModels.Add(viewModel);

        CurrentUserControl = PageViewModels
            .FirstOrDefault(vm => vm == viewModel);

        var x = this.GetHashCode();
    }

    #endregion

    private ICommand _changePageCommand;
    #endregion
    public ICommand ChangePageCommand
    {
        get
        {
            if (_changePageCommand == null)
            {
                _changePageCommand = new RelayCommand(
                    p => ChangeViewModel((IUserContentViewModel)p),
                    p => p is IUserContentViewModel);
            }

            return _changePageCommand;
        }
    }

    private void GoToFillOrder(int i)
    {
        CurrentUserControl = PageViewModels2[0];
    }

}

CateringMenuView

 <UserControl.Resources>
    <DataTemplate DataType="{x:Type cvm:NewDeliveryComOrderViewModel}">
        <cv:NewDeliveryComOrderView/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type cvm:NewRegularOrderViewModel}">
        <cv:NewRegularOrderView/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type cvm:FillOrderViewModel}">
        <cv:FillOrderView/>
    </DataTemplate>
</UserControl.Resources>

<Grid  Margin="5">
        <Grid>
            <StackPanel>
                <Menu>
                    <MenuItem Header="New Order">
                        <ItemsControl ItemsSource="{Binding PageViewModels}" Width="168" >
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <TextBlock>
                                        <Hyperlink Command="{Binding ChangePageCommand, Mode=OneWay}" CommandParameter="{Binding}" TextDecorations="{x:Null}">
                                            <InlineUIContainer>
                                                <TextBlock Text="{Binding Name}"/>
                                            </InlineUIContainer>
                                        </Hyperlink>
                                    </TextBlock>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                    </MenuItem>
                </Menu>
            </StackPanel>
        </Grid>

   <ContentControl Content="{Binding CurrentUserControl}"/>

</Grid>

person Cantinou    schedule 25.01.2016    source источник
comment
Немедленно сократите свой код! Оставьте только описание вашей проблемы!   -  person Kirill Kobelev    schedule 25.01.2016
comment
Для голосующего только потому, что есть присутствующий код, не обязательно означает, что это хороший вопрос, как упоминает Кирилл.   -  person MickyD    schedule 25.01.2016
comment
@Micky, может быть полезно воспроизвести проблему, чтобы использовать существующий код, вместо того, чтобы пытаться создать аналогичную ситуацию только из описания.   -  person Vadim Martynov    schedule 25.01.2016
comment
Выполняется ли команда изменения или нет?   -  person lyz    schedule 25.01.2016
comment
@lyz, совсем нет. Вроде отображается только вид.   -  person Cantinou    schedule 25.01.2016
comment
Вы пытались использовать Snoop, чтобы проверить, что ваш контекст данных — это то, что вы думаете?   -  person Paul Michaels    schedule 25.01.2016
comment
@pm_2, я бы с удовольствием, но я не знаю, что это такое.   -  person Cantinou    schedule 25.01.2016
comment
Здесь: snoopwpf.codeplex.com   -  person Paul Michaels    schedule 25.01.2016
comment
@pm_2, хорошо, спасибо. Я смотрю.   -  person Cantinou    schedule 25.01.2016


Ответы (2)


Здесь две проблемы.

  1. Во-первых, вы не хотите устанавливать .DataContext вашего UserControl вручную, потому что вы хотите использовать CateringMenuViewModel из PageViewModels[1], а не создавать его новый экземпляр.

    Так что обязательно удалите строку кода

    DataContext = new CateringMenuViewModel(ApplicationService.Instance.EventAggregator);
    
  2. Вторая проблема заключается в том, почему ваше событие не запускается. Я просмотрел ваш код в вашей истории версий вопроса и не вижу, чтобы вы где-либо транслировали событие.

    Эта строка кода правильно говорит "каждый раз, когда транслируется событие типа GoToFillOrder, запускать метод GoToFillOrder"

    _eventAggregator.GetEvent<GoToFillOrder>().Subscribe(GoToFillOrder);
    

    однако я не вижу никакого кода, который фактически транслирует это событие. Вам нужна строка кода, подобная следующей, чтобы транслировать сообщение GoToFillOrder во все ваше приложение:

    _eventAggregator.GetEvent<GoToFillOrder>().Publish();
    
person Rachel    schedule 25.01.2016
comment
Событие запускается в NewRegularOrderViewModel(). И это работает нормально. - person Cantinou; 26.01.2016
comment
Так как же связаны CateringMenuViewModel(ApplicationService.Instance.EventAggregator) и CateringMenuView()? Я не могу установить соединение в XAML, потому что CateringMenuViewModel имеет аргумент. - person Cantinou; 26.01.2016
comment
@Cantinou Ваш DataTemplate поставляет его. Ваш неявный DataTemplate говорит: всякий раз, когда вы сталкиваетесь с CateringMenuViewModel в дереве пользовательского интерфейса, нарисуйте его с помощью CateringMenuView, и он автоматически установит .DataContext за CateringMenuView в объект CateringMenuViewModel - person Rachel; 26.01.2016
comment
Что касается того, почему ваша команда может работать неправильно, ваш метод GoToFillOrder устанавливает CurrentUserControl = PageViewModels2[0];, что является NewRegularOrderViewModel .... вы имели в виду установить это на PageViewModels2[2], что будет FillOrderViewModel? - person Rachel; 26.01.2016
comment
Событие Aggregator работает нормально, поэтому я не добавлял код. Это было полезно, потому что мне нужно было нажать кнопку от NewRegularOrderView до перехода к FillOrderView, и благодаря вам это отлично работает. Я хочу сказать, что мой MenuBar в «CateringMenuView» не работает, потому что DataContext не установлен. - person Cantinou; 27.01.2016
comment
@Cantinou О, это другая проблема ... Меню не являются частью того же визуального дерева, что и остальная часть пользовательского интерфейса, поэтому DataContext не наследуется, как ожидалось. Я вижу, вы уже это поняли :) - person Rachel; 27.01.2016

Я наконец нашел решение.

В CateringMenuView() я заменил

<Hyperlink Command="{Binding ChangePageCommand, Mode=OneWay}" 
           CommandParameter="{Binding}" 
           TextDecorations="{x:Null}">

by

<Hyperlink Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"                             
            CommandParameter="{Binding}" 
            TextDecorations="{x:Null}">

Большое спасибо Рейчел!

person Cantinou    schedule 27.01.2016