问题
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