2017-03-23 22 views
0

我有一个嵌套的数据网格,其中有+和 - 按钮绑定到RelayCommands,分别添加新行或删除当前行。如果只有一个项目留在其类别中,则减号按钮命令的CanExecute逻辑应该禁用当前行的减号按钮。WPF命令在模板中执行验证

问题是,由于其模板性质,它会禁用所有类别中的所有减号按钮。

Image

这可怎么缓解?

这是代码。

XAML

<Grid> 
     <DataGrid x:Name="dataGrid1" 
        ItemsSource="{Binding DataCollection}" 
        SelectedItem="{Binding dataCollectionSelectedItem, Mode=TwoWay}" 
        AutoGenerateColumns="False" 
        CanUserAddRows="false" > 
      <DataGrid.Columns> 
       <DataGridTemplateColumn Header="Item/Price" Width="*"> 
        <DataGridTemplateColumn.CellTemplate > 
         <DataTemplate> 
          <DataGrid x:Name="dataGridItem" 
             ItemsSource="{Binding Items}" 
             SelectedItem="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.itemsSelectedItem, Mode=TwoWay}" 
             Background="Transparent" 
             HeadersVisibility="None" 
             AutoGenerateColumns="False" 
             CanUserAddRows="false" > 
           <DataGrid.Columns> 
            <DataGridTextColumn Binding="{Binding Name}" Width="*"/> 
            <DataGridTextColumn Binding="{Binding Price}" Width="50"/> 
            <DataGridTemplateColumn Header="Button"> 
             <DataGridTemplateColumn.CellTemplate> 
              <DataTemplate> 
               <StackPanel Orientation="Horizontal"> 
                <Button Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.AddItem }" Width="20" Height="20">+</Button> 
                <Button Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.DeleteItem }" Width="20" Height="20">-</Button> 
               </StackPanel> 
              </DataTemplate> 
             </DataGridTemplateColumn.CellTemplate> 
            </DataGridTemplateColumn> 
           </DataGrid.Columns> 
          </DataGrid> 
         </DataTemplate> 
        </DataGridTemplateColumn.CellTemplate> 
       </DataGridTemplateColumn> 
       <DataGridTextColumn Header="Category" Binding="{Binding Category}" Width="Auto"/> 
       <DataGridTemplateColumn Header="Buttons"> 
        <DataGridTemplateColumn.CellTemplate> 
         <DataTemplate> 
          <StackPanel Orientation="Horizontal"> 
           <Button Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.AddCategory}" Width="20" Height="20">+</Button> 
           <Button Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.DeleteCategory}" Width="20" Height="20">-</Button> 
          </StackPanel> 
         </DataTemplate> 
        </DataGridTemplateColumn.CellTemplate> 
       </DataGridTemplateColumn> 
      </DataGrid.Columns> 
     </DataGrid> 
    </Grid> 

C#

public class Item 
    { 
     public string Name { get; set; } 
     public int Price { get; set; } 
    } 

    public class DataTable 
    { 
     public ObservableCollection<Item> Items { get; set; } 
     public string Category { get; set; } 
    } 

    public class RelayCommand : ICommand 
    { 
     private Action<object> executeDelegate; 
     readonly Predicate<object> canExecuteDelegate; 

     public RelayCommand(Action<object> execute, Predicate<object> canExecute) 
     { 
      if (execute == null) 
       throw new NullReferenceException("execute"); 
      executeDelegate = execute; 
      canExecuteDelegate = canExecute; 
     } 

     public RelayCommand(Action<object> execute) : this(execute, null) { } 

     public event EventHandler CanExecuteChanged 
     { 
      add { CommandManager.RequerySuggested += value; } 
      remove { CommandManager.RequerySuggested -= value; } 
     } 

     public bool CanExecute(object parameter) 
     { 
      return canExecuteDelegate == null ? true : canExecuteDelegate(parameter); 
     } 

     public void Execute(object parameter) 
     { 
      executeDelegate.Invoke(parameter); 
     } 
    } 

    public class ViewModel 
    { 
     public ObservableCollection<DataTable> DataCollection { get; set; } 

     public DataTable dataCollectionSelectedItem { get; set; } 
     public Item itemsSelectedItem { get; set; } 

     public RelayCommand DeleteCategory { get; private set; } 
     public RelayCommand AddCategory { get; private set; } 
     public RelayCommand DeleteItem { get; private set; } 
     public RelayCommand AddItem { get; private set; } 

     public ViewModel() 
     { 
      DataCollection = new ObservableCollection<DataTable> 
      { 
       new DataTable() { 
        Items = new ObservableCollection<Item> { 
         new Item { Name = "Phone", Price = 220 }, 
         new Item { Name = "Tablet", Price = 350 }, 
        }, 
        Category = "Electronic gadgets" }, 
       new DataTable() { 
        Items = new ObservableCollection<Item> { 
         new Item { Name = "Teddy Bear Deluxe", Price = 2200 }, 
         new Item { Name = "Pokemon", Price = 100 }, 
        }, 
       Category = "Toys" } 
      }; 

      DeleteItem = new RelayCommand(innerDeleteItem, canUseDeleteItem); 
      AddItem = new RelayCommand(innerAddItem, canUseAddItem); 
     } 

     public void innerDeleteItem(object parameter) 
     { 
      var collectionIndex = DataCollection.IndexOf(dataCollectionSelectedItem); 
      if (DataCollection[collectionIndex].Items.Count != 1) 
      { 
       DataCollection[collectionIndex].Items.Remove(itemsSelectedItem); 
       CollectionViewSource.GetDefaultView(DataCollection).Refresh(); 
      } 

     } 
     public bool canUseDeleteItem(object parameter) 
     { 
      var collectionIndex = DataCollection.IndexOf(dataCollectionSelectedItem); 
      if ((dataCollectionSelectedItem != null) && (DataCollection[collectionIndex].Items.Count == 1)) 
      { 
       return false; 
      } 
      else return true; 
     } 
     public void innerAddItem(object parameter) 
     { 
      var collectionIndex = DataCollection.IndexOf(dataCollectionSelectedItem); 
      var itemIndex = DataCollection[collectionIndex].Items.IndexOf(itemsSelectedItem); 
      Item newItem = new Item() { Name = "Item_Name", Price = 0 }; 
      DataCollection[collectionIndex].Items.Insert(itemIndex + 1, newItem); 
      CollectionViewSource.GetDefaultView(DataCollection).Refresh(); 
     } 
     public bool canUseAddItem(object parameter) 
     { 
      return true; 
     } 
    } 

    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
      ViewModel newViewModel = new ViewModel(); 
      this.DataContext = newViewModel; 
     } 
    } 

回答

0

你你的两个命令绑定到窗口的数据上下文,它应该绑定到DataGrid的数据上下文。

您的XAML更改为:

<StackPanel Orientation="Horizontal"> 
    <Button Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=DataContext.AddItem }" Width="20" Height="20">+</Button> 
    <Button Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=DataContext.DeleteItem }" Width="20" Height="20">-</Button> 
</StackPanel> 
+0

没有。按钮以这种方式完全停止执行命令。我的问题不是关于命令绑定,绑定告诉编译器在哪里找到命令的代码。没问题。问题是CanExecute逻辑如何应用于视图。 – Disodium

0

我已经最终设置按钮的CanExecute总是返回true,并铺有禁用它Items.Count转弯时自定义触发按钮1.也许还有更优雅解决方案,但至少这个为我工作。

<Button Content="-" 
     Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.DeleteItem }" 
     Width="20" Height="20"> 
    <Button.Style> 
     <Style TargetType="Button"> 
      <Setter Property="IsEnabled" Value="True" /> 
      <Style.Triggers> 
       <DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=Items.Count }" Value="1"> 
        <Setter Property="IsEnabled" Value="False" /> 
       </DataTrigger> 
       </Style.Triggers> 
     </Style> 
    </Button.Style> 
</Button>