视图、ViewModel 和 DataContext

View, ViewModel and DataContext

为了解决我的应用程序中的导航问题,我使用了一个事件聚合器,它解决了这个问题但创建了另一个。

为了在不同的用户控件之间导航,我使用了 Rachel 的代码,您可以找到 here 在我进行一些更改之前它工作正常。

在我的屏幕一侧,我有一个主菜单 (HomeViewModel()),通过单击我在 UserControl 之间切换的项目,在每个 UserControl 中都有另一个菜单栏,我可以在其中切换其他用户控件。

但是第二个菜单 (CateringMenuViewModel()) 不再起作用了。显示了 UserControl,但是当我在菜单栏中单击时没有任何反应。

乍一看还以为是因为没有DataContext。 所以我像这样在后面的代码中添加了它:

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

但是还是不行。

我不明白,属性 Name 是有界的,因为名称显示在菜单中但命令 ChangePageCommand 没有。

HomeViewModel

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>

这里有两个问题。

  1. 首先,您不想手动设置 UserControl 的 .DataContext,因为您想使用 PageViewModels[1] 中的 CateringMenuViewModel,而不是创建它的新实例。

    所以一定要把这行代码去掉

    DataContext = new CateringMenuViewModel(ApplicationService.Instance.EventAggregator);
    
  2. 第二个问题是为什么您的事件没有触发。我查看了您 question's version history 中的代码,但我没有看到您在任何地方广播该事件。

    这行代码是正确的 "any time an event of type GoToFillOrder is broadcast, run the method GoToFillOrder"

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

    但是我没有看到任何实际广播该事件的代码。您需要像下面这样的一行代码来将 GoToFillOrder 消息广播到整个应用程序:

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

我终于找到了解决方案。

CateringMenuView()中,我已经替换了

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

来自

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

非常感谢瑞秋!