Passing ListView Items to Commands using Prism Library
Asked Answered
G

1

4

I'm trying to execute methods based on listview items data. In addition to that, the button, which triggers the command, should only be enabled, if "CanExecute" method of the listview item returns true.

Both methods, "MyCommand" and "CanExecute", are included in my ViewModel. Unfortunately I'm not sure how to pass the items information correctly to both methods in order to be conform with the PRISM 6 framework.

So my first approach was to do it like the following :

Model

public class MyModel
{
    public string Name { get; set; }
    public string Version { get; set; }
    public int Identifier { get; set; }
}

ViewModel

public class MyViewModel : BindableBase
{

    private ObservableCollection<MyModel> _models = new ObservableCollection<MyModel>();
    public ObservableCollection<MyModel> Models
    {
        get { return _models; }
        set { SetProperty(ref _models, value); }
    }

    public DelegateCommand VerifyCommand { get; set; }


    public MyViewModel()
    {
        //Add test data
        for (int i = 0; i < 5; i++)
        {
            MyModel model = new MyModel();
            model.Name = "Random Text";
            model.Version = "Random Text";
            model.Identifier = i;

            Models.Add(model);
        }

        //Doesn't work, because I don't reference to "Models"
        //How to do that?
        VerifyCommand = new DelegateCommand(DoCommand, CanExecute).ObservesProperty<string>(() => Name).ObservesProperty<string>(() => Version);
    }

    private bool CanExecute()
    {
        //Obviously this doesn't work, because "Version" and "Name" 
        //don't belong to the selected "Models" item of the listview

        //What is the "bridge", to know which item of Models was clicked (button)
        return !String.IsNullOrWhiteSpace(Version) && !String.IsNullOrWhiteSpace(Name);
    }

    private void DoCommand()
    {
        //Do something special
    }
}

View

<ListView ItemsSource="{Binding Models}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <Grid Height="Auto" Margin="0,0,0,10">
                <Grid.RowDefinitions>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
                </Grid.RowDefinitions>
                <TextBox Grid.Row="0" Tag="VERSION" Text="{Binding Version, UpdateSourceTrigger=PropertyChanged}" />
                <TextBox Grid.Row="1" Tag="NAME" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
                <Button Command="{Binding ElementName=root, Path=DataContext.VerifyCommand}" Content="Verify" Grid.Row="2">
            </Grid>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

The link between View and ViewModel is done by using:

prism:ViewModelLocator.AutoWireViewModel="True"

in my View (this works).

So in summary: How does it work, PRISM conform, to 1. Enable the items button only if CanExecute is true and 2. to execute "DoCommand" method and passing items information to that (root element of the button -> In this case the ListViewItem (MyModel).

Any help would be greatly appreciated.

Giannini answered 26/2, 2016 at 14:9 Comment(2)
You should have view models for your models - those then carry the command. I.e. the view has a view model, and that view model exposes a list of "ModelViewModel"s. BTW - the Models property probably needs no setter, unless you plan to set it from the outside (by databinding)...Highly
Do you select one item that you want validated for or is it possible to select multiple items from the list?Affinity
H
6

Short answer: put the command in the item's viewmodel.

Long answer:

Here's an example of what I mean in the comment above. I've omitted the observability of the collections, if you really need an observable collection of models and an observable collection of view models, prepare yourself for a lot of boring two-way-sync-code...

Model:

internal class ItemModel
{
    public string Name { get; set; }
    public string Version { get; set; }
    public int Identifier { get; set; }
}

ViewModels (one for the collection of items, that is, your MyViewModel, and one for the item):

internal class MyCollectionViewModel : BindableBase
{
    private readonly List<ItemModel> _models = new List<ItemModel>();

    public MyCollectionViewModel()
    {
        //Add test data
        for (var i = 0; i < 5; i++)
            _models.Add( new ItemModel
            {
                // to prove that CanExecute is actually evaluated...
                Name = i == 3 ? "Random Text" : string.Empty,
                Version = "Random Text",
                Identifier = i
            } );
    }

    public IReadOnlyCollection<ItemViewModel> TheCollection => _models.Select( x => new ItemViewModel( x ) ).ToList();
}

internal class ItemViewModel : BindableBase
{
    public ItemViewModel( ItemModel item )
    {
        _item = item;
        VerifyCommand = new DelegateCommand( () =>
                                             {
                                                 /* Do something */
                                             }, () => !string.IsNullOrWhiteSpace( Version ) && !string.IsNullOrWhiteSpace( Name ) );
    }

    public string Name => _item.Name;
    public string Version => _item.Version;
    public int Identifier => _item.Identifier;

    public DelegateCommand VerifyCommand
    {
        get;
    }

    private readonly ItemModel _item;
}

View:

<ListView ItemsSource="{Binding TheCollection}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <Grid Height="Auto" Margin="0,0,0,10">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
                <TextBox Grid.Column="0" Text="{Binding Version, Mode=OneWay}" />
                <TextBox Grid.Column="1" Text="{Binding Name, Mode=OneWay}" />
                <Button Grid.Column="2" Command="{Binding VerifyCommand}" Content="Verify"/>
            </Grid>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
Highly answered 26/2, 2016 at 16:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.