我有一个简单的应用程序,其ObservableCollection<Item>
集合绑定到UniformGrid
。如果我使用:
Items.Add(new Item
{
ID = i.ToString(),
Name = i.ToString(),
TestCommand = new RelayCommand<Item>((Item) => ChangeName(Item))
});
然后绑定到UI的RelayCommand
按预期触发,但如果我将上一行更改为:
Application.Current.Dispatcher.BeginInvoke(
new Action(()=>
{
Items.Add(new Item
{
ID=i.ToString(),
Name=i.ToString(),
TestCommand=new RelayCommand<Item>((Item)=>ChangeName(Item))
});
}));
UI不会调用RelayCommand
。你能解释一下原因吗?
代码:
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new Data();
}
}
public class Item:INotifyPropertyChanged
{
private string id;
public string ID
{
get
{
return this.id;
}
set
{
this.id = value;
RaisePropertyChanged("ID");
}
}
private string name;
public string Name
{
get
{
return this.name;
}
set
{
this.name = value;
this.RaisePropertyChanged("Name");
}
}
public RelayCommand<Item> TestCommand { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string info)
{
if (PropertyChanged!=null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
public class Data
{
public ObservableCollection<Item> Items { get; set; }
public Data()
{
Items = new ObservableCollection<Item>();
CreateBoxes();
}
public void ChangeName(Item Item)
{
Items.Select(x=>x.ID== Item.ID);
Item.Name = "Changed";
}
public void CreateBoxes()
{
for (int i=0;i<4;i++)
{
//Application.Current.Dispatcher.BeginInvoke( new Action(()=>{ Items.Add(new Item {ID=i.ToString(),Name=i.ToString(),TestCommand=new RelayCommand<Item>((Item)=>ChangeName(Item)) });}));
Items.Add(new Item { ID = i.ToString(), Name = i.ToString(), TestCommand = new RelayCommand<Item>((Item) => ChangeName(Item)) });
}
}
}
}
Xaml代码:
<Grid>
<ListView ItemsSource="{Binding Items}">
<ListView.ItemTemplate>
<DataTemplate>
<Border BorderThickness="1" BorderBrush="LightBlue">
<Grid VerticalAlignment="Center" HorizontalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Grid.Row="0" Content="ID:"/>
<Label Grid.Column="1" Grid.Row="0" Content="Name:"/>
<Label Grid.Column="0" Grid.Row="1" Content="{Binding ID}"/>
<Label Grid.Column="1" Grid.Row="1" Content="{Binding Name}"/>
<Button Grid.Row="2" Grid.ColumnSpan="2" Content="Test" Command="{Binding TestCommand}" CommandParameter="{Binding .}"/>
</Grid>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="2" Columns="2">
</UniformGrid>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</Grid>
答案 0 :(得分:0)
我无法重现您描述的问题。当我使用你共享的代码时(添加一个简单的RelayCommand<T>
实现,因为你省略了),单击按钮时可以正确调用该命令,无论我是直接调用Items.Add()
还是使用{{1稍后再调用它。
那就是说,你做在你的代码中有一个严重的问题:你使用捕获的循环变量BeginInvoke()
来调用i
,而不是一个新的变量BeginInvoke()
循环块的范围本地。这引入了这样的可能性:当任何被调用的委托被执行时,循环变量将被增加到循环值的末尾,即for
。
在您的示例中,您应该更改4
方法,使其看起来更像这样:
CreateBoxes()
即。在调用public void CreateBoxes()
{
for (int i = 0; i < 4; i++)
{
string itemText = i.ToString();
Application.Current.Dispatcher.BeginInvoke((Action)(() =>
Items.Add(new Item
{
ID = itemText,
Name = itemText,
TestCommand = new RelayCommand<Item>(item => ChangeName(item))
})));
}
}
之前将ID转换为字符串。这具有为循环的每次迭代创建要捕获的单独变量的效果,确保每个委托调用获得其自己的变量的私有副本(以及仅需要评估BeginInvoke()
的快乐副作用每循环迭代:))。
如果没有a good, minimal, complete code example实际上再现了你描述的问题(即命令没有被按钮调用),我不知道上面是否与该问题有关。根据您的实际代码的样子,对捕获的变量的错误处理可能会导致调用命令出现问题,或者它可能与此特定错误完全无关。
如果以上内容无法帮助您解决问题,请编辑您的问题,以便它包含可靠地重现问题的良好代码示例。
顺便说一句:在您的i.ToString()
方法中,您的陈述看起来完全是多余的:ChangeName()
。我不知道你的意思是什么,但它实际上只是返回一个Items.Select(x=>x.ID== Item.ID);
对象,当枚举时,它将返回一个元素为IEnumerable<bool>
的序列,其中{{1}传入的true
对象的值与在相应的原始ID
元素中找到的值相同。代码不会将返回的Item
对象用于任何事情,更不用说实际枚举它,因此简单地丢弃该对象,并且该语句在程序中没有实际效果。