wpfdatatemplate命令

wpfdatatemplate命令,wpf,command,datatemplate,parameters,Wpf,Command,Datatemplate,Parameters,我有一个tabcontrol的itemtemplate的datatemplate,如下所示 <DataTemplate x:Key="TabItemTemplate"> <DockPanel Width="120"> <Button Command="{Binding Path=DataContext.DeleteTimeTableCommand, RelativeSource={RelativeSource Fin

我有一个tabcontrol的itemtemplate的datatemplate,如下所示

<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中所有选项卡上的所有按钮。我只希望当前选项卡受到影响

我有一个属性CanDelete,我想把它包含在我的命令中。我试图找到一个关于CommandParameters的好例子,因为我认为这是我需要走的路

有没有人对最好的方法有好的建议


谢谢。

我怀疑你在这方面是否还需要帮助,但我想我还是会试着回答

我在过去做过的方法是将绑定到TabControl的项集合设置为简单ViewModel对象的集合。这样,您就可以为每个选项卡实现CanXXX逻辑,而不是将TabControl或view作为一个整体

在本例中,我使用的是中显示的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;
        }
    }
}
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"); }
        }
    }

}
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));
            }
        }
    }
}
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
    }
}
ViewModelBase.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;
        }
    }
}
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"); }
        }
    }

}
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));
            }
        }
    }
}
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
    }
}
RelayCommand.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;
        }
    }
}
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"); }
        }
    }

}
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));
            }
        }
    }
}
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
    }
}
使用系统;
使用系统诊断;
使用System.Windows.Input;
命名空间选项卡绑定
{
/// 
///一种命令,其唯一目的是
///将其功能传递给其他用户
///通过调用委托创建对象
///CanExecute的默认返回值
///方法为“true”。
/// 
公共类中继命令:ICommand
{
#区域字段
只读操作_执行;
只读谓词_canExecute;
#endregion//字段
#区域构造函数
/// 
///创建始终可以执行的新命令。
/// 
///执行逻辑。
公共中继命令(操作执行)
:此(执行,空)
{
}
/// 
///创建一个新命令。
/// 
///执行逻辑。
///执行状态逻辑。
公共RelayCommand(操作执行,谓词canExecute)
{
if(execute==null)
抛出新的ArgumentNullException(“执行”);
_执行=执行;
_canExecute=canExecute;
}
#endregion//构造函数
#区域ICommand成员
[调试步骤至]
公共布尔CanExecute(对象参数)
{
返回_canExecute==null?true:_canExecute(参数);
}
公共事件事件处理程序CanExecuteChanged
{
添加{CommandManager.RequerySuggested+=value;}
删除{CommandManager.RequerySuggested-=value;}
}
public void Execute(对象参数)
{
_执行(参数);
}
#endregion//ICommand成员
}
}
main window.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>
<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>

App.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>
<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>

如果还有人对答案感兴趣,可以使用CommandParameter绑定扩展传递当前模型

<Button Command="{Binding Path=DataContext.DeleteTimeTableCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" CommandParameter="{Binding}" />

传递的对象将成为选项卡项的DataContext。解决方案要求ICommand实现正确处理给定参数(强制转换等)。此外,由于WPF无法确定何时重新查询选项卡上的CanExecute方法,因此应在对模型进行任何修改后引发RequerySuggested事件。使用异步编程模型时要记住的另一件事是仅从UI线程引发刷新事件。否则什么也不会发生