C# MVVM WPF 中的 ICommand
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/642043/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
ICommand in MVVM WPF
提问by
I'm having a look at this MVVM stuff and I'm facing a problem.
我正在查看这个 MVVM 的东西,但我遇到了一个问题。
The situation is pretty simple.
情况很简单。
I have the following code in my index.xaml page
我的 index.xaml 页面中有以下代码
<Grid>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<view:MovieView ></view:MovieView>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
and in my index.xaml.cs
并在我的 index.xaml.cs
...
...
InitializeComponent(); base.DataContext = new MovieViewModel(ent.Movies.ToList()); ....
初始化组件(); base.DataContext = new MovieViewModel(ent.Movies.ToList()); ....
and here is my MoveViewModel
这是我的 MoveViewModel
public class MovieViewModel
{
readonly List<Movies> _m;
public ICommand TestCommand { get; set; }
public MovieViewModel(List<Movies> m)
{
this.TestCommand = new TestCommand(this);
_m = m;
}
public List<Movies> lm
{
get
{
return _m;
}
}
}
finally
最后
here is my control xaml MovieView
这是我的控件 xaml MovieView
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label VerticalAlignment="Center" Grid.Row="0" Grid.Column="0">Title :</Label><TextBlock VerticalAlignment="Center" Grid.Row="0" Grid.Column="1" Text="{Binding Title}"></TextBlock>
<Label VerticalAlignment="Center" Grid.Row="1" Grid.Column="0">Director :</Label><TextBlock VerticalAlignment="Center" Grid.Row="1" Grid.Column="1" Text="{Binding Director}"></TextBlock>
<Button Grid.Row="2" Height="20" Command="{Binding Path=TestCommand}" Content="Edit" Margin="0,4,5,4" VerticalAlignment="Stretch" FontSize="10"/>
</Grid>
So the problem I have is that if I set ItemsSource at Binding
所以我的问题是,如果我在 Binding 设置 ItemsSource
it doesn't make anything
它什么也不做
if I set ItemsSource="{Binding lm}"
如果我设置 ItemsSource="{Binding lm}"
it populates my itemsControl but the Command (Command="{Binding Path=TestCommand}" ) doesn't not work.
它填充我的 itemsControl 但命令 (Command="{Binding Path=TestCommand}" ) 不起作用。
Of course it doesn't not work because TestCommand doesn't not belong to my entity object Movies.
当然它不起作用,因为 TestCommand 不属于我的实体对象电影。
So finally my question is,
所以最后我的问题是,
what do I need to pass to the ItemsControl to make it working?
我需要传递给 ItemsControl 什么才能使其工作?
Thx in advance
提前谢谢
采纳答案by Bas Bossink
Got it working
搞定了
here is the thing
这是事情
<ItemsControl DataContext="{Binding}" ItemsSource="{Binding lm}">
Command="{Binding Path=DataContext.TestCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"
so the RelativeSource was the thing I've missed.
所以RelativeSource 是我错过的东西。
if somebody has a good explaination of this, I would be definitely happy.
如果有人对此有很好的解释,我肯定会很高兴。
回答by Arcturus
Try implementing the INotifyPropertyChanged interface:
尝试实现 INotifyPropertyChanged 接口:
public class MovieViewModel : INotifyPropertyChanged
{
readonly List<Movies> _m;
private ICommand _testCommand = null;
public ICommand TestCommand { get { return _testCommand; } set { _testCommand = value; NotifyPropertyChanged("TestCommand"); } }
public MovieViewModel(List<Movies> m)
{
this.TestCommand = new TestCommand(this);
_m = m;
}
public List<Movies> lm
{
get
{
return _m;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string sProp)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(sProp));
}
}
}
What happens is that the TestCommand has a value, and the UI gets no notification that a change is taking place.. On controls you solve this problem using Dependency properties, on data object, you can use the INotifyPropertyChanged interface..
发生的情况是 TestCommand 有一个值,而 UI 没有收到正在发生更改的通知。在控件上,您使用依赖属性解决此问题,在数据对象上,您可以使用 INotifyPropertyChanged 接口。
Secondly, the Movie objects have no reference to the parent object..
其次,Movie 对象没有对父对象的引用。
You can solve this problem in three different ways:
您可以通过三种不同的方式解决此问题:
have a reference back to the model on Movie, and make the Bind path like so: ie.. if you property is named ParentMovieModel, then your Binding will be like:
{Binding Path=ParentMovieModel.TestCommand}
Make a binding based on ElementName like so: Seek up the parent control where you set your DataContext on, and give it a name: i.e. Root. Now create a binding based on the ElementName like so:
{Binding ElementName=Root, Path=DataContext.TextCommand}
Make a binding based on a RelativeSource like so: Seek up the parent control by type, and use the same path as the one above...
{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type yourparentwindowtype}}, Path=DataContext.TextCommand}
引用回电影上的模型,并像这样制作绑定路径:即..如果您的属性名为ParentMovieModel,则您的绑定将如下所示:
{绑定路径=ParentMovieModel.TestCommand}
像这样根据 ElementName 进行绑定:查找您设置 DataContext 的父控件,并为其命名:即 Root。现在创建一个基于 ElementName 的绑定,如下所示:
{绑定元素名称=根,路径=DataContext.TextCommand}
像这样基于 RelativeSource 进行绑定:按类型查找父控件,并使用与上面相同的路径...
{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type yourparentwindowtype}}, Path=DataContext.TextCommand}
回答by Bas Bossink
What about <ItemsControl ItemsSource="{Binding Path=lm}">
?
怎么样<ItemsControl ItemsSource="{Binding Path=lm}">
?
回答by Bas Bossink
in the ItemsSource="{Binding Path=lm}"> case
在 ItemsSource="{Binding Path=lm}"> 情况下
the itemsControl works well but I complety bypass my MovieViewModel
itemsControl 运行良好,但我完全绕过了我的 MovieViewModel
and I got this in the output window
我在输出窗口中得到了这个
System.Windows.Data Error: 39 : BindingExpression path error: 'TestCommand' property not found on 'object' ''Movies' (HashCode=1031007)'. BindingExpression:Path=TestCommand; DataItem='Movies' (HashCode=1031007); target element is 'Button' (Name=''); target property is 'Command' (type 'ICommand')
System.Windows.Data 错误:39:BindingExpression 路径错误:在“对象”“电影”(HashCode=1031007)上找不到“TestCommand”属性。BindingExpression:Path=TestCommand; DataItem='电影' (HashCode=1031007); 目标元素是 'Button' (Name=''); 目标属性是“命令”(类型“ICommand”)
Movies is my entity object and owns only the Title and Director properties
电影是我的实体对象,仅拥有 Title 和 Director 属性
回答by Arcturus
As soon as your items are rendered, each item gets set to the DataContext of the specific row it represents, so you are no longer able to reference to your first DataContext.. Also, due to the fact that you are in a DataTemplate, your bindings will start working when there is need for that Template.. so in that case you need to look up your parent control through a RelativeSource binding...
一旦您的项目被呈现,每个项目都会被设置为它所代表的特定行的 DataContext,因此您无法再引用您的第一个 DataContext。此外,由于您在 DataTemplate 中,您的当需要该模板时,绑定将开始工作..所以在这种情况下,您需要通过 RelativeSource 绑定查找父控件...
Hope that explains some things..
希望能解释一些事情..
回答by Sam
//include namespace
using Microsoft.Practices.Composite.Wpf.Commands;
readonly List<Movies> _m;
public ICommand TestCommand { get; set; }
public MovieViewModel(List<Movies> m)
{
this.TestCommand = new DelegateCommand<object>(TestcommandHandler);
_m = m;
}
public List<Movies> lm
{
get
{
return _m;
}
}
void TestcommandHandler(object obj)
{
// add your logic here
}
}