Passing ListView Items to Commands using Prism Library

ぃ、小莉子 提交于 2019-12-24 15:24:12

问题


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.


回答1:


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>


来源:https://stackoverflow.com/questions/35653879/passing-listview-items-to-commands-using-prism-library

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