WPF 数据模板命令

标签 wpf command datatemplate parameters

我有一个选项卡控件的项目模板的数据模板,如下所示;

<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" />               

这没问题,因为它在选项卡控件中为我提供了一个按钮,允许删除当前的选项卡项。

我遇到的问题是我绑定(bind)的删除命令有一个 canExecute 方法,该方法可以更新选项卡控件中所有选项卡上的所有按钮。我只想影响当前选项卡。

我有 CanDelete 属性,我想将其包含在我的命令中。我正在尝试在 CommandParameters 上找到一个很好的示例,因为我认为这是我需要走的路。

有人对最好的方法有好的建议吗?

谢谢。

最佳答案

我怀疑您是否仍然需要帮助,但我想无论如何我都会尝试回答这个问题。

我过去的做法是使绑定(bind)到 TabControl 的项目集合成为简单 ViewModel 对象的集合。这样您就可以为每个选项卡实现 CanXXX 逻辑,而不是作为一个整体来实现 TabControl 或 View 。

在此示例中,我使用 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>

关于WPF 数据模板命令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2117136/

相关文章:

c# - 绑定(bind) DataGridTemplateColumn

wpf - 非正方形布局

C#在新线程中单击按钮更新图像

c# - 为什么要使用多绑定(bind)转换器?

c# - WPF:如何重新定义 ApplicationCommands 的 CanExecute 方法

linux - 如何将年转换为天(过去)?

java - 是否需要 Java 才能运行某些基于 .NET 的程序?

c# - ObservableCollection.CollectionChanged 没有在 ToolBar 上选择正确的 DataTemplate

c# - Wpf DataTemplate绘制多条线

c# - 在 ComboBox 的选中项中显示图像