如何使用自定义样式与自定义命令绑定?

时间:2014-08-20 10:35:57

标签: wpf xaml

这是我的情况。到目前为止,我知道我可以创建资源字典来定义自定义样式,如选项卡控件。我想在主窗口中使用那些样式。问题是在哪里定义事件,例如按钮单击样式中的按钮?说我想做Visibility="Binding {Button A's click}"

<Window x:Class="test_tabControl3.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<DockPanel LastChildFill="True">
    <Grid DockPanel.Dock="Bottom">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <TextBlock Text="Zoom:"
                VerticalAlignment="Center"
                Margin="5"/>
        <!-- Allows to zoom the TabControl (see the ScaleTransform defined on the TC) -->
        <Slider x:Name="uiScaleSlider" 
             Grid.Column="1"
             ToolTip="Drag the slider to change the zoom-level ..."
             SmallChange="0.1"
             LargeChange="1"
             Minimum="1" 
             Maximum="10"
             Value="2" 
             Margin="5"/>
    </Grid>
    <Grid x:Name="Layer1" Visibility="Binding {Button A's click}">
    <TabControl x:Name="tc" Margin="5" SelectedIndex="0" Style="{StaticResource FirstTab}" Visibility="Visible">
        <TabControl.LayoutTransform>
            <!-- Allows to zoom the control's content using the slider -->
            <ScaleTransform CenterX="0" 
                     CenterY="0"
                     ScaleX="{Binding ElementName=uiScaleSlider,Path=Value}"
                     ScaleY="{Binding ElementName=uiScaleSlider,Path=Value}"/>
        </TabControl.LayoutTransform>
        <TabItem Header="Tab 1" Style="{StaticResource FirstTabItem}">
            <Canvas Background="AliceBlue"/>
        </TabItem>
        <TabItem Header="Tab 2" Style="{StaticResource FirstTabItem}">
            <Canvas Background="Lavender"/>
        </TabItem>
        <TabItem Header="Tab 3" IsEnabled="False"  Style="{StaticResource FirstTabItem}"
              ToolTip="I'm disabled.">
            <Canvas Background="PaleGreen"/>
        </TabItem>
    </TabControl>
    </Grid>
</DockPanel>
</Window>

enter image description here

更新 TabControl在资源字典中定义

<Style TargetType="{x:Type TabControl}" x:Key="FirstTab">
    <Setter Property="SnapsToDevicePixels" Value="true"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="TabControl">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="*"/>
                    </Grid.RowDefinitions>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"></ColumnDefinition>
                            <ColumnDefinition Width="Auto"></ColumnDefinition>
                        </Grid.ColumnDefinitions>
                        <Border Background="AliceBlue"
                             Padding="{StaticResource TabItemPanel_Padding}">
                            <!-- This is the area in which TabItems (the strips) will be drawn. -->
                            <TabPanel IsItemsHost="True"/>
                        </Border>
                        <Button Grid.Column="1">A</Button>
                    </Grid>
                    <Border BorderThickness="1,0,1,1"
....

UPDATE2 关于样式模板的更多问题。 我知道我需要定义ContentPresenter ContentSource来在模板中呈现属性。 下面我有两个样式首先用于TabControl,第二个用于Tabitem。 在TabItem样式中,我有ContentSource="Header",其中Header是tabItem的属性,而在TabControl样式ContentSource="SelectedContent"中。所以我想知道为什么不能ContentSource="Content" Content下tabItem控件中定义的tabItem属性。

<Style TargetType="{x:Type TabControl}">
    <Setter Property="Template">
    ....
    <ContentPresenter ContentSource="SelectedContent" Margin="0"/>
</Setter>
</Style>

<Style TargetType="{x:Type TabItem}" >
                <Setter Property="Header"
                    Value="{Binding Header}" />
                <Setter Property="Content"
                    Value="{Binding Content}" />
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="TabItem">
                            <Grid Height="35" VerticalAlignment="Bottom">
                                <Border Name="Border"
                         Background="{StaticResource TabItem_BackgroundBrush_Unselected}"
                         BorderBrush="{StaticResource TabItem_BorderBrush_Selected}" 
                         Margin="{StaticResource TabItemMargin_Base}"
                         BorderThickness="2,1,1,0" 
                         CornerRadius="3,3,0,0" 
                         >
                                    <Grid>
                                        <Grid.ColumnDefinitions>
                                            <!-- Text / TabItem's Caption -->
                                            <ColumnDefinition/>
                                            <!-- Close button -->
                                            <ColumnDefinition/>
                                        </Grid.ColumnDefinitions>
                                        <ContentPresenter x:Name="ContentSite"
                                         VerticalAlignment="Center"
                                         HorizontalAlignment="Center"
                                         ContentSource="Header"
                                         Margin="7,2,12,2"
                                         RecognizesAccessKey="True">

                                        </ContentPresenter>

1 个答案:

答案 0 :(得分:1)

这是一个如何使用命令的完整示例,此示例也演示了如何使用视图模型来填充选项卡控件。

这将为您提供构建基于mvvm的应用程序的开始

XAML

<Grid xmlns:l="clr-namespace:CSharpWPF">
    <Grid.DataContext>
        <l:ViewModel />
    </Grid.DataContext>
    <TabControl ItemsSource="{Binding TabItems}"
                SelectedItem="{Binding CurrentItem}"
                x:Name="tabControl">
        <TabControl.Resources>
            <Style TargetType="TabItem">
                <Setter Property="Header"
                        Value="{Binding Header}" />
                <Setter Property="Content"
                        Value="{Binding Content}" />
            </Style>
        </TabControl.Resources>
    </TabControl>
    <Button Content="X"
            IsEnabled="{Binding HasItems,ElementName=tabControl}"
            Command="{Binding CloseTab}"
            CommandParameter="{Binding SelectedItem,ElementName=tabControl}"
            HorizontalAlignment="Right"
            VerticalAlignment="Top" />
</Grid>

我保持示例小,但您可以应用任何您想要的样式,您只需要找到合适的绑定路径。

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Input;

namespace CSharpWPF
{
    class ViewModel : INotifyPropertyChanged
    {
        public ViewModel()
        {
            TabItems = new ObservableCollection<TabData>();

            //dummy data
            TabItems.Add(new TabData() { Header = "Tab 1", Content = "Tab content 1" });
            TabItems.Add(new TabData() { Header = "Tab 2", Content = "Tab content 2" });
            TabItems.Add(new TabData() { Header = "Tab 3", Content = "Tab content 3" });
            TabItems.Add(new TabData() { Header = "Tab 4", Content = "Tab content 4" });
            TabItems.Add(new TabData() { Header = "Tab 5", Content = "Tab content 5" });

            CurrentItem = TabItems[3];

            CloseTab = new SimpleCommand(OnCloseTab);
        }

        public ObservableCollection<TabData> TabItems { get; private set; }

        private TabData _currentItem;

        public TabData CurrentItem
        {
            get { return _currentItem; }
            set { _currentItem = value; }
        }

        public ICommand CloseTab { get; private set; }

        private void OnCloseTab(object obj)
        {
            TabData tab = obj as TabData;
            if (tab != null && TabItems.Contains(tab))
            {
                TabItems.Remove(tab);
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    class TabData
    {
        public string Header { get; set; }
        public object Content { get; set; }
    }

    class SimpleCommand : ICommand
    {
        Action<object> action;
        public SimpleCommand(Action<object> action)
        {
            this.action = action;
        }

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            action(parameter);
        }
    }
}

尝试新的测试项目并分享您的反馈


修改

看完你发送的样本后,我必须说你做得很好。你几乎就在那里,你只是错过了一件微不足道的事情

按钮上的命令在示例代码中工作,因为按钮的DataContext与命令所在的位置相同,但是当您将按钮放在选项卡本身时,数据上下文将更改为TabData,没有那个命令

所以你需要找到tabitem的父标签控件上的相应datacontext,你可以使用RelativeSource和FindAncestor找到适当的控件并绑定到DataContext的CloseTab属性,这是你想要的命令

例如

Command="{Binding DataContext.CloseTab,RelativeSource={RelativeSource FindAncestor,AncestorType=TabControl}}"

还要弄清楚这种问题密切关注“输出”窗口,我在调试代码时发现了这个问题

  

System.Windows.Data错误:40:BindingExpression路径错误:&#39; CloseTab&#39;在&#39; object&#39;上找不到的属性&#39;&#39; TabData&#39; (的HashCode = 37561097)&#39 ;. BindingExpression:路径= CloseTab;的DataItem =&#39; TabData&#39; (的HashCode = 37561097);目标元素是&#39;按钮&#39; (名称=&#39;&#39);目标财产是&#39; Command&#39; (键入&#39; ICommand&#39;)

这告诉我绑定正在尝试解析TabData类

实例上的CloseTab属性

对于你的第二个问题,你可以简单地参考WPF: Multiple content presenters in a custom control?它有一个明确的答案