【问题标题】:Bind Button Command from within style template从样式模板中绑定按钮命令
【发布时间】:2016-01-14 03:41:48
【问题描述】:

我有一个选项卡部分,我正在尝试连接用于动态关闭和打开新选项卡的命令。问题是我无法理解如何从我的 tabItem 模板(有一个按钮)中绑定命令。代码如下:

(包含选项卡部分的用户控件,简化..):

<UserControl.DataContext>
    <vm:InicioViewModel />
</UserControl.DataContext>
<UserControl.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="../Visual Resources/TabResource.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</UserControl.Resources>        

    <ContentPresenter HorizontalAlignment="Stretch" Grid.Column="1">
        <ContentPresenter.Content>
            <TabControl Name="tc">
                <TabControl.DataContext>
                    <vm:WorkSpaceViewModel/>
                </TabControl.DataContext>  
                <TabControl ItemsSource="{Binding Items}"/>
            </TabControl>

        </ContentPresenter.Content>
    </ContentPresenter>
</Grid>

(这里是 tabItem 的资源字典):

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TabItem}">
                <Border x:Name="TaBorder" Width="auto" Height="auto"
                        BorderBrush="LightGray"
                        BorderThickness="0.5,0.5,0.5,0"
                        CornerRadius="3,3,0,0"
                        Background="WhiteSmoke">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="20" />
                        </Grid.ColumnDefinitions>
                        <TextBlock Margin="2" Grid.Column="0" Text="{TemplateBinding Header}" />
                        <Button x:Name="CloseButton" Grid.Column="1" Width="11" Height="11"
                                VerticalAlignment="Center"
                                HorizontalAlignment="Center"
                                Background="Transparent"
                                DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabControl}}, Path=DataContext}"
                                Command="{Binding CloseWorkSpaceCommand}"
                                (Donkt know what yo put in command to           reference my viewmodel Icommand)
                                BorderThickness="0"
                                >
                            <!-- ETC -->                

(这里是我的 viewModel):

class InicioViewModel : ViewModelBase
{
    private WorkSpaceViewModel _workSpaceVm;

    public InicioViewModel()
    {

    }

    public WorkSpaceViewModel WorkSpaceVm
    {
        get { return _workSpaceVm; }
        set { _workSpaceVm = value; }
    }
}

(WorkSpaceViewModel..):

public class WorkSpaceViewModel
{
    private ObservableCollection<IWorkSpaceItemVm> _items;        

    private RelayCommand _closeWorkSpaceCommand;        

    public WorkSpaceViewModel()
    {
        _items = new ObservableCollection<IWorkSpaceItemVm>();
    }

    public ObservableCollection<IWorkSpaceItemVm> Items
    {
        get { return _items; }
        set { _items = value; }
    }      

    public ICommand CloseWorkSpaceCommand
    {
        get
        {
            return _closeWorkSpaceCommand ?? (_closeWorkSpaceCommand = new RelayCommand(
                param => CloseWorkSpace_Execute(param),
                param => CloseWorkSpace_CanExecute(param)
                ));
        }
    }       

    private void CloseWorkSpace_Execute(object parm)
    {
        MessageBox.Show("asdasdasd");
    }

    private bool CloseWorkSpace_CanExecute(object parm)
    {
        return true;
    }
}

如您所见,我在 CloseWorkSpace_Execute 中仅显示一个 MessageBox 用于测试目的。

1) 我如何在我的 tabItem 样式模板中引用我的视图模型中的 Icommand,或者,如果有更好的方法来获得相同的结果,我将受到欢迎。

2) 为什么当我运行应用程序时会创建一个空选项卡,但我的可观察集合列表是空的

编辑: RelayCommand 在程序的另一部分工作正常,这不是问题,tabItem 在触发器工作的情况下渲染正常,我仍然无法理解如何将视图模型中的命令与我制作的模板化 tabItem 绑定。

编辑 2: 该命令现在正在工作,显然该命令在资源字典中未被识别并标记为:“Cant resolve property 'CloseWorkSpaceCommand' in DataContext of type object”,但将按钮的 DataContext 设置为: DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabControl}}, Path=DataContext}" 在运行应用程序时完成工作(Visual Studio 仍在为 DataContext 类型道歉,不知道这意味着什么)。 默认情况下仍然创建一个选项卡,为什么? 有没有办法纠正 DataContext 类型的代码异味?

【问题讨论】:

  • 乍一看,您的困难似乎与模板在单独的资源字典中的位置有关,这使得访问您想要绑定的任何内容都不太方便。想到的一种可能的解决方案是在同一个 XAML 文件中声明一个适当的Command 并绑定到该文件,然后在其他地方设置一个绑定(例如CommandBinding)以在执行时响应该命令。但是这里的代码太多,无法确定模棱两可问题的哪一部分给您带来了麻烦,别介意花时间尝试修复。
  • 请编辑,以便仅包含可靠地重现问题的a good, minimal, complete code example
  • 嘿彼得,我编辑了它,我不明白你在说什么,你说问题是资源字典声明的引用?那么,相对资源是什么?还有另一种方法可以实现吗?我想将命令绑定到样式模板内的按钮,无论我是否必须在模板内声明、内联在我的用户或我的 tabcontrol 资源中,但我无法理解如何引用我在我的 tabItem 模板中创建的按钮跨度>
  • “你说的问题是资源字典声明的引用”——不,我说没有好的代码示例,我不能肯定地说什么问题是什么。请将问题分解为您自己无法解决的具体问题,并针对每个问题提出一个问题,并确保包含a good, minimal, complete code example,它清楚地说明了问题并可靠地重现了所描述的任何问题。
  • 我开始认为我走错了路,如果我必须创建另一个模拟命令然后再次绑定..有什么想法吗?

标签: c# wpf xaml


【解决方案1】:

您似乎想在每个选项卡上添加一个关闭按钮,就像我们在浏览器中看到的那样。这样做似乎很复杂。但让我试着为你分解它。

首先让我们先说明阻碍我们这样做的障碍:

  1. TabItem 没有可用于绑定 CloseWorkSpaceCommand 的命令属性。
  2. 标签项没有关闭按钮。这就是您创建模板的原因。但是您仍然不能将模板绑定到命令属性,因为 TabItem 没有这样的命令属性。
  3. 如何将按钮的命令连接到视图模型的 CloseWorkSpaceCommand 属性?

现在让我们尝试一一解决每个问题。

  1. 要解决这个问题,我们需要为具有命令属性的 TabItem 创建一个自定义控件。

    public class ClosableTabItem : TabItem
    {
        public static readonly DependencyProperty CloseCommandProperty = DependencyProperty.Register("CloseCommand", typeof(ICommand), typeof(ClosableTabItem), new PropertyMetadata(null));        
        public ICommand CloseCommand
        {
          get { return (ICommand)GetValue(CloseCommandProperty); }
          set { SetValue(CloseCommandProperty, value); }
        }
    }  
    

    由于我们有一个自定义选项卡项,我们还需要一个自定义 TabControl,因为我们需要覆盖 GetContainerForItemOverride() 方法。

    public class ClosableTabControl : TabControl { protected override DependencyObject GetContainerForItemOverride() { return new ClosableTabItem(); } }

    这解决了问题 #1。

  2. 与您所做的一样,我们需要一个 ControlTemplate,以便我们可以在每个选项卡上放置一个关闭按钮。

    <ControlTemplate TargetType="{x:Type local:ClosableTabItem}"> <Border x:Name="TaBorder" Width="auto" Height="auto" Background="LightGray" CornerRadius="4,4,0,0" Margin="0,2,3,0"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="20" /> </Grid.ColumnDefinitions> <TextBlock Margin="2" Grid.Column="0" Text="{TemplateBinding Header}" /> <Button x:Name="CloseButton" Grid.Column="1" Width="11" Height="11" VerticalAlignment="Center" HorizontalAlignment="Center" Command="{TemplateBinding CloseCommand}" BorderThickness="0" Content="X" Background="Red" FontSize="8"> </Button> </Grid> </Border> </ControlTemplate>

  3. 要将 viewmodel.CloseWorkSpaceCommand 绑定到选项卡项,我们在 ItemContainerStyle 的设置器中执行此操作。

    <local:ClosableTabControl.ItemContainerStyle> <Style TargetType="local:ClosableTabItem" BasedOn="{StaticResource {x:Type TabItem}}"> <Setter Property="CloseCommand" Value="{Binding DataContext.CloseWorkSpaceCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" /> ...

    您会注意到我使用的是相对于 Window 的 DataContext 的源。

    现在要将 ClosableTabItem 的 CloseCommand 属性绑定到模板内 Button 的 Command 属性,我们现在在控件模板的按钮内进行模板绑定。

    您也可以在答案 #2 代码示例中看到这一点。

    Command="{TemplateBinding CloseCommand}"

这是完整的 xaml 代码:

<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApplication1" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525"> <Window.DataContext> <local:MainViewModel /> </Window.DataContext> <Grid> <local:ClosableTabControl ItemsSource="{Binding Items}"> <local:ClosableTabControl.ItemContainerStyle> <Style TargetType="local:ClosableTabItem" BasedOn="{StaticResource {x:Type TabItem}}"> <Setter Property="CloseCommand" Value="{Binding DataContext.CloseWorkSpaceCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:ClosableTabItem}"> <Border x:Name="TaBorder" Width="auto" Height="auto" Background="LightGray" CornerRadius="4,4,0,0" Margin="0,2,3,0"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="20" /> </Grid.ColumnDefinitions> <TextBlock Margin="2" Grid.Column="0" Text="{TemplateBinding Header}" /> <Button x:Name="CloseButton" Grid.Column="1" Width="11" Height="11" VerticalAlignment="Center" HorizontalAlignment="Center" Command="{TemplateBinding CloseCommand}" BorderThickness="0" Content="X" Background="Red" FontSize="8"> </Button> </Grid> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> </local:ClosableTabControl.ItemContainerStyle> <TabControl.Items> </TabControl.Items> </local:ClosableTabControl> </Grid> </Window>

【讨论】:

  • 嘿,谢谢,我在想我必须将 tabItem 注册为新控件,很好的建议和很好的解释。顺便说一句,我现在要尝试一下,我应该如何处理 mi 字典中选项卡项的样式资源?我可以用吗?我可以在同一个字典内的 mainWindow 中声明您直接在 mainWindow 中声明的控件模板吗?最后一个如果你不介意的话:我应该传入什么命令参数来请求关闭命令?再次感谢!
  • 是的,您仍然可以使用您在字典中创建的样式,我只是将样式放在 xaml 中,只是为了使代码更具可读性,因此您不必查找它从字典。对于命令参数,它是可选的。我认为将您的命令参数也连接起来是正确的。我会更新代码。
猜你喜欢
  • 1970-01-01
  • 2010-12-27
  • 2013-10-09
  • 1970-01-01
  • 1970-01-01
  • 2012-03-18
  • 2019-05-11
  • 1970-01-01
  • 2015-07-03
相关资源
最近更新 更多