我有一个tabcontrol的itemtemplate的数据窗口,如下所示;
<DataTemplate x:Key="TabItemTemplate">
<DockPanel Width="120">
<Button
Command="{Binding Path=DataContext.DeleteTimeTableCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
Content="X"
Cursor="Hand"
DockPanel.Dock="Right"
Focusable="False"
Margin="0,1,0,0"
Padding="0"
VerticalContentAlignment="Bottom"
Width="16" Height="16" />
这没关系,因为它在tabcontrol中给了我一个按钮,允许删除当前的tabitem。
麻烦我遇到的是我正在绑定的Delete命令有一个canExecute方法,它更新tabcontrol中所有选项卡上的所有按钮。我只想让当前的标签受到影响。
我有我想要包含在我的Command中的属性CanDelete。我试图在CommandParameters上找到一个很好的例子,因为我认为这是我需要的方式。
有没有人对最好的方法有一个好的建议?
感谢。
答案 0 :(得分:4)
我怀疑你仍然需要这方面的帮助,但我认为无论如何我都会采取行动解决问题。
我过去做过的方法是将绑定到TabControl的项目集合作为简单ViewModel对象的集合。这样,您可以为每个选项卡实现CanXXX逻辑,而不是TabControl或整个视图。
在此示例中,我使用的是Josh Smith's MVVM article中显示的RelayCommand类。
<强> MainViewModel.cs 强>
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
namespace TabBinding.ViewModels
{
class MainViewModel : ViewModelBase
{
private ObservableCollection<TabViewModel> _Tabs;
public ObservableCollection<TabViewModel> Tabs
{
get { return _Tabs; }
set
{
_Tabs = value;
OnPropertyChanged(this, "Tabs");
}
}
public MainViewModel()
{
var tabs = new ObservableCollection<TabViewModel>();
tabs.Add(new TabViewModel() { TabHeader = "Tab1", Content="Content For Tab1" });
tabs.Add(new TabViewModel() { TabHeader = "Tab2", Content = "Content For Tab2" });
tabs.Add(new TabViewModel() { TabHeader = "Tab3", Content = "Content For Tab3" });
tabs.Add(new TabViewModel() { TabHeader = "Tab4", Content = "Content For Tab4" });
Tabs = tabs;
}
}
}
<强> TabViewModel.cs 强>
using System.Windows.Input;
using System.Windows;
namespace TabBinding.ViewModels
{
class TabViewModel : ViewModelBase
{
RelayCommand _CloseTabCommand;
private string _TabHeader;
public string TabHeader
{
get { return _TabHeader; }
set
{
_TabHeader = value;
OnPropertyChanged(this, "TabHeader");
}
}
private string _Content;
public string Content
{
get { return _Content; }
set
{
_Content = value;
OnPropertyChanged(this, "Content");
}
}
public ICommand CloseTabCommand
{
get
{
if (_CloseTabCommand == null)
{
_CloseTabCommand = new RelayCommand(
param => this.CloseTab(),
param => this.CanCloseTab
);
}
return _CloseTabCommand;
}
}
public void CloseTab()
{
MessageBox.Show("Close Me!");
}
bool CanCloseTab
{
get { return (TabHeader == "Tab2" || TabHeader == "Tab4"); }
}
}
}
<强> ViewModelBase.cs 强>
using System.ComponentModel;
namespace TabBinding.ViewModels
{
class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(object sender, string propertyName)
{
if (this.PropertyChanged != null)
{
PropertyChanged(sender, new PropertyChangedEventArgs(propertyName));
}
}
}
}
<强> RelayCommand.cs 强>
using System;
using System.Diagnostics;
using System.Windows.Input;
namespace TabBinding
{
/// <summary>
/// A command whose sole purpose is to
/// relay its functionality to other
/// objects by invoking delegates. The
/// default return value for the CanExecute
/// method is 'true'.
/// </summary>
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
/// <summary>
/// Creates a new command that can always execute.
/// </summary>
/// <param name="execute">The execution logic.</param>
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion // ICommand Members
}
}
<强> MainWindow.xaml 强>
<Window x:Class="TabBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:TabBinding.ViewModels"
Title="MainWindow" Height="360" Width="550">
<Window.Resources>
<vm:MainViewModel x:Key="Data" />
</Window.Resources>
<Grid DataContext="{StaticResource Data}">
<TabControl
HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="10,10,10,10"
Width="500"
Height="300"
ItemsSource="{Binding Tabs}">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Content="X" Margin="0,0,10,0" Command="{Binding CloseTabCommand}" />
<TextBlock Text="{Binding TabHeader}"/>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Content" Value="{Binding Content}"/>
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
</Grid>
</Window>
<强>的App.xaml 强>
<Application x:Class="TabBinding.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Views/MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
答案 1 :(得分:0)
如果有人仍然对答案感兴趣,您可以使用CommandParameter绑定扩展来传递当前模型。
The context
传递的对象将成为选项卡项的DataContext。该解决方案需要ICommand实现来正确处理给定参数(强制转换等)。此外,在对模型进行任何修改之后,应该引发RequerySuggested事件,因为WPF无法确定何时在选项卡上重新查询CanExecute方法。使用异步编程模型时要记住的另一件事是仅从UI线程引发刷新事件。否则什么都不会发生。