简体   繁体   English

从样式模板中绑定按钮命令

[英]Bind Button Command from within style template

I have a tabbed section and I am trying to wire up the commands for closing and opening new tabs dynamically. 我有一个选项卡式的部分,我试图连接用于动态关闭和打开新选项卡的命令。 The problem is I cant understand how to bind the command from within my tabItem template(which has a button). 问题是我无法理解如何从我的tabItem模板(具有按钮)中绑定命令。 Here is the code: 这是代码:

(UserControl containing the tabbed section, simplified..): (UserControl包含选项卡式部分,已简化。):

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

(Here is the resource Dictionary for the tabItem): (以下是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 -->                

(Here my viewModel): (这是我的viewModel):

class InicioViewModel : ViewModelBase
{
    private WorkSpaceViewModel _workSpaceVm;

    public InicioViewModel()
    {

    }

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

(WorkSpaceViewModel..): (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;
    }
}

As you can note I only have a MessageBox showing in CloseWorkSpace_Execute for test purposes. 您可以注意到,出于测试目的,我仅在CloseWorkSpace_Execute中显示一个MessageBox。

1) How can I reference the Icommand in my viewmodel from within my tabItem style template, or, if is there a better way with same results will be welcome. 1)如何从我的tabItem样式模板中的视图模型中引用Icommand,或者,如果有更好的方法,可以得到相同的结果,将受到欢迎。

2) Why when I run the app one empty tab is created, I have my observable collection list empty 2)为什么当我运行该应用程序时,创建了一个空选项卡,但我的可观察的收藏列表却为空

EDIT: The RelayCommand is working OK in another part of the program, thats not the problem, The tabItem gets rendered OK with triggers workin and all, I still cant get my head into how to bind the command from my viewmodel with the Templated tabItem I made. 编辑:RelayCommand在程序的另一部分工作正常,那不是问题,通过触发workin和所有方法都可以使tabItem呈现为正常,我仍然无法理解如何将我的ViewModel命令与Templated选项卡绑定制作。

EDIT 2: The command is working now, apparently the Command is not recognized in the resource dictionary and is marked as: "Cant resolve property 'CloseWorkSpaceCommand' in DataContext of type object", but setting the DataContext of the button to: DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabControl}}, Path=DataContext}" gets the work done when running the app(visual studio still apologizing about DataContext type, dunno what it means). 编辑2:该命令现在正在工作,显然该命令在资源字典中无法识别,并标记为:“无法在类型对象的DataContext中解析属性'CloseWorkSpaceCommand'”,但将按钮的DataContext设置为:DataContext =“ {Binding RelativeSource = {RelativeSource FindAncestor,AncestorType = {x:Type TabControl}},Path = DataContext}”可以在运行应用程序时完成工作(Visual Studio仍然对DataContext类型表示歉意,这并不意味着什么)。 There is still one tab created by default, why? 默认情况下仍然创建一个标签,为什么? And is there a way to correct the code smell with DataContext type? 有没有一种方法可以纠正DataContext类型的代码异味?

It seems like you want to add a close button on every tab like the ones we see in browsers. 似乎您想像我们在浏览器中看到的那样在每个选项卡上添加一个关闭按钮。 It's seems pretty complicated to do that. 这样做似乎很复杂。 But let me try to break it down for you. 但是,让我尝试为您分解。

First let's start by stating the roadblocks that prevents us from doing that: 首先,让我们开始说明阻止我们这样做的障碍:

  1. The TabItem does not have a command property where you can bind your CloseWorkSpaceCommand. TabItem没有命令属性,可以在其中绑定CloseWorkSpaceCommand。
  2. The tab item does not have a close button. 该选项卡项没有关闭按钮。 That's the reason you created a Template. 这就是您创建模板的原因。 But still you can't do a template binding to a command property since the TabItem does not have such command property. 但是仍然不能将模板绑定到命令属性,因为TabItem没有这样的命令属性。
  3. How will you be able wire up the button's command to the viewmodel's CloseWorkSpaceCommand property? 您如何将按钮的命令连接到视图模型的CloseWorkSpaceCommand属性?

Now let's try to resolve each problem one by one. 现在,让我们尝试一个接一个地解决每个问题。

  1. To resolve this, we need to create a custom control for the TabItem that has a command property. 要解决此问题,我们需要为具有Command属性的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); } } } 

    Since we have a custom tab item, we also need a custom TabControl because we need to overide the GetContainerForItemOverride() method. 由于我们有一个自定义选项卡项目,因此我们还需要一个自定义TabControl,因为我们需要覆盖GetContainerForItemOverride()方法。

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

    This resolves problem #1. 这解决了问题1。

  2. Like what you did we need to have a ControlTemplate so we could place a close button on each tabs. 像您所做的一样,我们需要一个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. To bind the viewmodel.CloseWorkSpaceCommand to the tab item we do this in the ItemContainerStyle's setter. 要将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}}}" /> ...

    You'll notice that I'm using relative source to the DataContext of the Window. 您会注意到,我正在使用Window的DataContext的相对源。

    Now to bind the CloseCommand Property of the ClosableTabItem to the Command property of the Button inside the template, we now do the template binding inside the control template's button. 现在将ClosableTabItem的CloseCommand属性绑定到模板内部的Button的Command属性,现在我们在控件模板的按钮内部进行模板绑定。

    You can also see this in answer #2 code sample. 您还可以在答案#2代码示例中看到这一点。

    Command="{TemplateBinding CloseCommand}"

Here's the complete xaml code: 这是完整的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>

在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM