user2847238
user2847238

Reputation: 169

Adding Items to Collection using MVVM pattern

I am having truble accessing ObservableCollection (which is my ItemsSource) from command attached to each of the items.

I am trying make two list, one with all the objects and the second one with objects picked by user.

Here is my view model.

class ViewModel : VMBase
{
    private ObservableCollection<Card> _cardsCollection;
    public ObservableCollection<Card> CardsCollection
    {
        get { return _cardsCollection; }
        set { _cardsCollection = value; }
    }

    static private ObservableCollection<Card> _pickedCards;
    static public ObservableCollection<Card> PickedCards
    {
        get { return _pickedCards; }
        set { _pickedCards = value;
              NotifyPropertyChanged("PickedCards");
            }
    }
}
class Card : VMBase
{
    public string Name { get; set; }
    public Card(string name, int cost, CardType type, CardRarity rarity)
    {
        this.Name = name;
        this.BackgroundImage = String.Format("/Images/Cards/{0}.png", name);

        this.PickCardCommand = new MvvmCommand();
        this.PickCardCommand.CanExecuteFunc = obj => true;
        this.PickCardCommand.ExecuteFunction = PickCard;
    }
    public MvvmCommand PickCardCommand { get; set; }
    public void PickCard(object parameter)
    {
        PickedCards.Add(currentCard); 
        //Above Does not work, not accessible
        CreateDeckModel.PickedCards.Add(currentCard);
        //Above does work but only if Collection is static
        //but if collection is static I am unable to call NotifyPropertyChanged()
    }
}

Here is my XAML file with binding

<GridView Grid.Row="1" ItemsSource="{Binding CardsCollection, Mode=TwoWay}">
     <GridView.ItemTemplate>
         <DataTemplate>
             <Grid>
                  <Button Height="258" Width="180" Content="{Binding}" Margin="0,0,0,0" 
                          Command="{Binding PickCardCommand}" CommandParameter="{Binding}">
                      <Button.Template>
                          <ControlTemplate>
                              <StackPanel Orientation="Vertical">
                                  <Border BorderThickness="2" BorderBrush="White" Height="258" Width="180">
                                      <Border.Background>
                                          <ImageBrush ImageSource="{Binding BackgroundImage}" />
                                      </Border.Background>
                                   </Border>
                              </StackPanel>
                          </ControlTemplate>
                       </Button.Template>
                   </Button>
               </Grid>
           </DataTemplate>
       </GridView.ItemTemplate>
   </GridView>

Here is my MvvmCommand Class

class MvvmCommand : ICommand
{
    public Predicate<object> CanExecuteFunc { get; set; }
    public Action<object> ExecuteFunction { get; set; }
    public void Execute(object parameter)
    {
        ExecuteFunction(parameter);
    }

    public event EventHandler CanExecuteChanged;
    public bool CanExecute(object parameter)
    {
        return CanExecuteFunc(parameter);
    }
}

}

Is there a way to access ItemsSource from Item or DataContext alternatively make command accessible for ViewModel Class?

Upvotes: 1

Views: 3662

Answers (3)

Scroog1
Scroog1

Reputation: 3589

You can just pass the Add method of PickedCards to the Card when you create it:

class Card : VMBase
{
    private readonly Action<Card> _addCard;

    public Card(..., Action<Card> addCard)
    {
        ...
        _addCard = addCard;

        this.PickCardCommand = new MvvmCommand();
        this.PickCardCommand.CanExecuteFunc = obj => true;
        this.PickCardCommand.ExecuteFunction = PickCard;
    }

    public MvvmCommand PickCardCommand { get; set; }

    public void PickCard(object parameter)
    {
        _addCard(this);
    }
}

Then when you create the card:

var card = new Card(..., ..., ..., ..., PickedCards.Add)

Upvotes: 0

Bijington
Bijington

Reputation: 3751

You can point the Command to your ViewModel class by changing the button in your xaml file to the following:

<Button Height="258" Width="180" Content="{Binding}" Margin="0,0,0,0" Command="{Binding DataContext.PickCardCommand,RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type vw:ViewClass}}}" CommandParameter="{Binding}">

In the RelativeSource binding you will need to change the following:

vw is the namespace for your View, this will have to be declared with the other namespaces in your xaml file.

ViewClass is the name of your class.

Then you obviously need to move the Command over to the ViewModel class from your Card class.

Windows Phone

<GridView x:Name="myGridView" Grid.Row="1" ItemsSource="{Binding CardsCollection, Mode=TwoWay}">
     <GridView.ItemTemplate>
         <DataTemplate>
             <Grid>
                  <Button Height="258" Width="180" Content="{Binding}" Margin="0,0,0,0" 
                          Command="{Binding ElementName=myGridView,
                   Path=DataContext.PickCardCommand}" CommandParameter="{Binding}">
                      <Button.Template>
                          <ControlTemplate>
                              <StackPanel Orientation="Vertical">
                                  <Border BorderThickness="2" BorderBrush="White" Height="258" Width="180">
                                      <Border.Background>
                                          <ImageBrush ImageSource="{Binding BackgroundImage}" />
                                      </Border.Background>
                                   </Border>
                              </StackPanel>
                          </ControlTemplate>
                       </Button.Template>
                   </Button>
               </Grid>
           </DataTemplate>
       </GridView.ItemTemplate>
   </GridView>

You will see that I have now named the GridView and then used the name of the GridView in the binding as the ElementName. I believe this should work.

Upvotes: 2

Muds
Muds

Reputation: 4116

You can bind your collection to the Command parameter. Command parameter is currently bound to Item DataSource and not collection

CommandParameter="{Binding}"

Instead use RelativeBinding and bind to itemSource of grid

Upvotes: -1

Related Questions