2015-11-02 45 views
-1

我有一个简单的应用程序,其ObservableCollection<Item>集合绑定到UniformGrid。如果我使用:使用BeginInvoke设置RelayCommand时未触​​发

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

我无法重现您所描述的问题。当我使用您共享的代码时(添加一个简单的RelayCommand<T>实现,因为您省略了该代码),单击该按钮时是否正确调用该命令,无论我直接调用Items.Add()还是使用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)) 
      }))); 
    } 
} 

即在调用BeginInvoke()之前将ID转换为字符串。这样可以创建一个单独的变量,以便为循环的每次迭代捕获一个变量,以确保每个委托调用都获得自己的变量私有副本(以及每个循环迭代仅需评估i.ToString()一次的令人开心的副作用: ))。

没有a good, minimal, complete code example实际上重现您描述的问题(即命令不被按钮调用),我不知道以上是否与该问题有关。根据实际代码的外观,错误处理捕获的变量可能会导致调用的命令出现问题,或者可能与此特定的错误完全无关。

如果上述内容不能帮助您解决问题,请编辑您的问题,以便它包含一个可靠地重现问题的良好代码示例。


顺便说一句:在你的ChangeName()方法,你必须出现完全是多余的声明:Items.Select(x=>x.ID== Item.ID);。我不知道你要怎么做,但实际上所做的只是返回一个IEnumerable<bool>对象,枚举时将返回一个序列,其中元素为true,其中传入的Item对象的ID值为与相应的原始Items元素中找到的相同。该代码不会将返回的IEnumerable<bool>对象用于任何事情,不必介意实际枚举它,因此该对象将被简单地丢弃,并且该语句在程序中没有实际效果。