GavinB
GavinB

Reputation: 595

MVVM Light Commands within an ItemsControl

I'm just trying my hand at WP7 dev using the MVVM Light framework.

I'm trying to fire a button command inside an ItemsControl, essentialy it's a list of cars and I'd like each element to have an edit button. The Relevant piece of the View:

<ItemsControl ItemsSource="{Binding MyCars}" >
<ItemsControl.ItemTemplate>
    <DataTemplate>
        <Grid x:Name="CarViewGrid">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" MinWidth="100" />
                <ColumnDefinition Width="Auto" MinWidth="302"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" MinHeight="40" />
                <RowDefinition Height="Auto" MinHeight="32" />
                <RowDefinition Height="Auto" MinHeight="32" />
                <RowDefinition Height="Auto" MinHeight="32" />
            </Grid.RowDefinitions>
            <TextBlock x:Name="CarName" Text="{Binding Name, Mode=TwoWay}" Margin="7,0" Grid.Row="0" Grid.ColumnSpan="2" FontSize="32" FontWeight="Bold" FontStyle="Normal" />
            <TextBlock x:Name="Make" Text="{Binding Make, Mode=TwoWay}" Margin="15,0" Grid.Row="1" Grid.Column="0" FontSize="24" />
            <TextBlock x:Name="Model" Text="{Binding Model, Mode=TwoWay}" Grid.Row="1" Grid.Column="1" FontSize="24" />
            <TextBlock x:Name="Odometer" Text="{Binding Odometer, Mode=TwoWay}" Margin="15,0"  Grid.Row="2" Grid.ColumnSpan="2" FontSize="24" />
            <Button x:Name="EditCarButton" Content="Edit" Grid.Row="3" Grid.Column="1" HorizontalAlignment="Right" Width="100" >
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="Click">
                        <cmd:EventToCommand Command="{Binding EditCar}" CommandParameter="{Binding}"/>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </Button>
        </Grid>
    </DataTemplate>
</ItemsControl.ItemTemplate>

My ViewModel contains this:

    public RelayCommand OpenNewForm { get; private set; }

    public CarViewModel()
    {
        //Snip
        EditCar = new RelayCommand<Car>(c =>
        {
            CurrentCar = c;
            FormVisible = true;
        });
    }

Now as you can see I'm trying to pass the current Car object that is bound through the CommandParameter. My delegate never fires so I'm guessing I've got something wrong in my binding regarding the current DataContext.

Anybody got any ideas as to what I'm doing wrong?

Upvotes: 2

Views: 1659

Answers (3)

Derek Beattie
Derek Beattie

Reputation: 9478

Its going to fire EditCar on a car item. There are a couple ways to solve this, since you're using mvvm light try.

Appologies to Laurent. I posted the wrong link. My intention was that since the original poster was using MVVM Light that Dan Wahlin's DataContextProxy or a RelativeSource binding solution would work. I was going to go on and explain how if using CM an event from a child item could bubble up but I didn't. The link to CM dotnetrocks was something I pasted previously.

Upvotes: 1

ecathell
ecathell

Reputation: 1030

I have found that its alot easier to make my collections VM collections instead of Entitycollections. I used to use entitycollections and then I started running into those problems like you are describing. But Now each VM in the Collection is 'selfaware' and can act on itself without jumping through major hoops.

You would have the button that you are clicking as part of the CarsVM and it would have access to all the properties of the carVM which would have access to all the properties of your Car Entity.

Sample from My App:

 public partial class ReadmitPatientListViewModel : ViewModelBase
    {
        /// <summary>
        /// Initializes a new instance of the ReadmitPatientListViewModel class.
        /// </summary>

        ////public override void Cleanup()
        ////{
        ////    // Clean own resources if needed

        ////    base.Cleanup();
        ////}

        #region Declarations

        ICommand _openSurveyCommand;
        Messenger _messenger = Messenger.Default;

        #endregion

        #region Command Properties
        public ICommand OpenSurveyCommand
        {
            get
            {
                if (_openSurveyCommand == null)
                {
                    _openSurveyCommand = new RelayCommand(() => OnSurveyCommandExecute());
                }
                return _openSurveyCommand;
            }
            private set { }
        }
        #endregion

        #region Command Methods
        private void OnSurveyCommandExecute()
        {
            Wait.Begin("Loading Patient List...");
            _messenger.Send<ReadmitPatientListViewModel>(this);
            _messenger.Send<Messages.NavigationRequest<SubClasses.URI.PageURI>>(GetNavRequest_QUESTIONAIRRESHELL());

        }
        #endregion

        #region Properties

        #endregion


        private static Messages.NavigationRequest<SubClasses.URI.PageURI> GetNavRequest_QUESTIONAIRRESHELL()
        {
            Messages.NavigationRequest<SubClasses.URI.PageURI> navRequest =
                new Messages.NavigationRequest<SubClasses.URI.PageURI>(
                    new SubClasses.URI.PageURI(Helpers.PageLinks.QUESTIONAIRRESHELL, System.UriKind.Relative));
            return navRequest;
        }

        partial void OnCreated()
        {

        }
    }

These are the properties in the primary vm that my Expander binds to:

 public CollectionViewSource SearchResultsCVS { get; private set; }

        public ICollection<ViewModel.ReadmitPatientListViewModel> SearchResults { get; private set; }

The collection is the soure for the CVS.....when the completeSurveyButton is clicked a navigation request is sent,and a copy of the viewmodel is sent to any listeners to manipulate.

Upvotes: 0

LBugnion
LBugnion

Reputation: 6662

In a DataTemplate, the DataContext is set by default to the item that is represented by the DataTemplate (in that case, the Car object). If the EditCar command is on the main viewmodel (which also contains the MyCars collection), you need to explicitly set the Source of the Binding to that object. This would be (assuming that you are using the MVVM Light's ViewModelLocator and that your VM is named Main) {Binding Source={StaticResource Locator}, Path=Main.EditCar}

Cheers, Laurent

Upvotes: 7

Related Questions