WPF datatemplate命令

时间:2010-01-22 12:25:05

标签: wpf command datatemplate parameters

我有一个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上找到一个很好的例子,因为我认为这是我需要的方式。

有没有人对最好的方法有一个好的建议?

感谢。

2 个答案:

答案 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线程引发刷新事件。否则什么都不会发生。