View, ViewModel and DataContext

那年仲夏 提交于 2019-12-13 21:41:04

问题


In order to solve a navigation issue in my application I have used an Event Aggregator which has solved the problem but has created an other one.

To navigate between different UserControls I used the Rachel's code you can find here which was working fine until I made some changes.

On the side of my screen I have a Main Menu (HomeViewModel()), by clicking on the items I switch between UserControls and in each of these UserControls there is a another menu bar where I can switch between other UserControls.

But this second menu (CateringMenuViewModel()) doesn't work anymore. The UserControl is displayed but nothing is happening when I am clicking in the menu bar.

At the first sight I thought it's because there is no DataContext. So I added it in the code behind like this:

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

But it still doesn't work.

I don't understand, the property Name is well bounded because the names are displayed in the menu but the command ChangePageCommand is not.

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:


Two problems here.

  1. First off, you do not want to set the .DataContext of your UserControl manually because you want to use the CateringMenuViewModel from PageViewModels[1], not create a new instance of it.

    So definitely remove the line of code

    DataContext = new CateringMenuViewModel(ApplicationService.Instance.EventAggregator);
    
  2. Second problem is why your event is not firing. I took a look at your code in your question's version history, and I do not see you broadcasting the event anywhere.

    This line of code is correct to say "any time an event of type GoToFillOrder is broadcast, run the method GoToFillOrder"

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

    however I don't see any code which actually broadcasts that event. You need a line of code like the following to broadcast the GoToFillOrder message to throughout your application :

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



回答2:


I finally found the solution.

In CateringMenuView(), I have replaced

<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}">

Big thanks to Rachel!



来源:https://stackoverflow.com/questions/34987514/view-viewmodel-and-datacontext

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!