views:

810

answers:

2

I have a View with ViewModel as datacontext ( set in code) . In my view I have a list

<UserControl x:Class="ZPOS.Modules.Menu.Views.DepartmentView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:prism="clr-namespace:Microsoft.Practices.Composite.Presentation.Commands;assembly=Microsoft.Practices.Composite.Presentation">
    <Grid>
<Grid.Background>
            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                <GradientStop Color="#FF9CA48A"/>
                <GradientStop Color="#FFFFFFFF" Offset="1"/>
                <GradientStop Color="#FF90A85C" Offset="0.5"/>
            </LinearGradientBrush>
        </Grid.Background>
        <ListBox ItemsSource="{Binding Departments}"
                 SelectionChanged="ListBox_SelectionChanged">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.Background>
                            <LinearGradientBrush>
                                <GradientStop Color="Black" Offset="0"/>
                                <GradientStop Color="White" Offset="1"/>
                            </LinearGradientBrush>
                        </Grid.Background>
                        <Grid.RowDefinitions>
                            <RowDefinition/>
                            <RowDefinition/>
                        </Grid.RowDefinitions>
                        <Button Height="Auto" HorizontalAlignment="Left" Margin="1,1,1,1" Grid.Column="0" Grid.Row="0" Content="{Binding Path=Name}"  
                                prism:Click.Command="{Binding displayMenubyCategory}" VerticalAlignment="Bottom" Width="Auto"/>
                        <TextBlock  Grid.Column="0" Grid.Row="1"   Text="{Binding Path=Note}" />
                    </Grid>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <Button Height="Auto" HorizontalAlignment="Left" Margin="1,1,1,1" Grid.Column="0" Grid.Row="0" Content="{Binding Path=Name}" prism:Click.Command="{Binding displayMenubyCategory}" VerticalAlignment="Bottom" Width="Auto"/>

    </Grid>
</UserControl>

ViewModel

using System;
using System.ComponentModel;
using Microsoft.Practices.Composite.Events;
using System.Collections.Generic;
using ZPOS.Infrastructure;
using ZPOS.Objects;
using System.Collections.ObjectModel;
using ZPOS.Modules.Menu.Views;
using ZPOS.Contracts;
using Microsoft.Practices.Composite.Presentation.Commands;
namespace ZPOS.Modules.Menu.PresentationModels
{
    public class DepartmentViewModel : IDepartmentViewModel, INotifyPropertyChanged
    {


        private readonly IEventAggregator eventAggregator;
        private string _message;
        IMenuService service;

        public DelegateCommand<POSDepartment> displayMenubyCategory { get; private set; } 

        public string Name { get; set; }



        private ObservableCollection<POSDepartment> deptItems;
        public ObservableCollection<POSDepartment> Departments
        {
            get { return deptItems; }
            private set
            {
                if (deptItems != value)
                {
                    deptItems = value;
                    PropertyChanged(this, new PropertyChangedEventArgs("deptItems"));
                }
            }
        }

        public string Message
        {
            get { return _message; }
            set
            {
                if (_message != value)
                {
                    _message = value;
                    PropertyChanged(this, new PropertyChangedEventArgs("deptItems"));
                }
            }
        }



        public IDepartmentView View { get; private set; }

        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        private void NotifyPropertyChanged(string propertyName)
        {
            var handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }





        public DepartmentViewModel(IDepartmentView deptView, IEventAggregator eventAggregator, IMenuService service)
        {
            this.View = deptView;
            this.View.Model = this;
            this.eventAggregator = eventAggregator;
            this.service = service;
            this.Name = "View for DepartmentModel";

            this.eventAggregator.GetEvent<DepartmentSelectionChangedEvent>().Subscribe(departmentSelectionChanged);
            displayMenubyCategory = new DelegateCommand<POSDepartment>(ExecuteCommand1, CanExecuteCommand1);

            PopulateDepartmentItems();
        }


        private void ExecuteCommand1(POSDepartment commandParameter)
        {
        }

        private bool CanExecuteCommand1(POSDepartment commandParameter)
        {
            return true;
        }

        public void departmentSelectionChanged(POSDepartment item)
        {
            this.Message = item.Name;
        }



        private void PopulateDepartmentItems()
        {
            try
            {

                List<POSDepartment> items = service.GetAllDepartments();

                deptItems = new ObservableCollection<POSDepartment>(items);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }


    }
}

Clicking on Button inside the listBox does not fire the command.

If I place the same button outside the list box the delage gets called.

Am I doing something wrong?

Is there a better way. I am still new to Prism. I also want to pass the parameter ( data context for listbox item) when command gets fired.

Thank you All

A: 

When you say inside the ListBox, I think you're saying inside the ItemTemplate of the ListBox, right?

The ItemTemplate is applied to each and every item generated for the ListBox. Each generated item has a DataContext set to the data item for which it was generated. Therefore, that data item needs a property called displayMenubyCategory or the binding will fail. Look in your output window in Visual Studio and check for binding errors.

Moving the Button outside the ListBox implies a different DataContext (your view model), which means the binding will succeed and hence everything will work.

Your options include:

  1. Add a displayMenubyCategory property to your data class, which may call into the same property of your main view model.
  2. Change your binding to look outside the current DataContext (see RelativeSource).

HTH, Kent

Kent Boogaart
@Kent- Thank you for superfast reply. I will check No 2. It seems to make sense. No 1. when you say DataClass is it my Object (POSDepartment)?
TheMar
@Kent - No 2. Did the trick - got to work it from the sample that Anderson gave. Still I will like to know how to do it using Property
TheMar
He's saying your POSDepartment type might need to have a property that references the command on the parent viewModel that you can bind to. Ex: public DelegateCommand<POSDepartment> displayMenubyCategory { get { return parentViewModel.displayMenubyCategory ; } }. This has the advantage of not having to use the ancestor binding in your Xaml, but the disadvantage of having the very same indirection in C#. It would allow your binding for your button to go back to a simple {Binding displayMenubyCategory}. It's about taste more than anything.
Anderson Imes
+2  A: 

As Kent mentioned, you can use RelativeSource. It's not always clear how to use it the first time, so here's a sample for you. I think this will work (removed some button attributes for brevity):

<Button prism:Click.Command="{Binding 
RelativeSource={RelativeSource FindAncestor, 
AncestorType={x:Type UserControl}}, Path=DataContext.displayMenubyCategory}" />

This ought to do it. It assumes you are setting DataContext to your parent ViewModel.

Anderson Imes
You did it again for me. It worked. Thanks.
TheMar