TemplateBinding没有传递正确的值

时间:2013-01-23 11:30:29

标签: wpf xaml combobox styles templatebinding

在我的WPF应用程序中,我重新演绎了ComboBox。问题是我似乎无法正确应用BorderThickness。我认为我正确地做了但我必须遗漏一些东西,因为结果不是我所期望的(它总是保持为1的厚度)。

UserControl中使用的ComboBox(注意厚度为3):

<ComboBox DockPanel.Dock="Top" SelectedItem="{Binding CurrentAnalysisKey}" 
          ItemsSource="{Binding AnalysisKeys}" Height="25"
          BorderBrush="{StaticResource ListBoxBorderBrush}"
          BorderThickness="3"
          DisplayMemberPath="ReaffectedName" Margin="0,5" />

资源文件中定义的ComboBox样式:

<Style TargetType="{x:Type ComboBox}">
  <Setter Property="Foreground"
          Value="White" />
  <Setter Property="SnapsToDevicePixels"
          Value="true" />
  <Setter Property="BorderBrush"
          Value="{StaticResource BlackBorderBrush}" />
  <Setter Property="BorderThickness"
          Value="2" />
  <Setter Property="Template"
          Value="{DynamicResource ComboBoxTemplate}" />
</Style>

<Style d:IsControlPart="True"
       TargetType="{x:Type ComboBoxItem}">
  <Setter Property="Foreground"
          Value="White" />
  <Setter Property="SnapsToDevicePixels"
          Value="true" />
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type ComboBoxItem}">
        <ControlTemplate.Resources>
          <Storyboard x:Key="HoverOn">

            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                           Storyboard.TargetName="HoverRectangle"
                                           Storyboard.TargetProperty="(UIElement.Opacity)">
              <SplineDoubleKeyFrame KeyTime="00:00:00.1000000"
                                    Value="1" />
            </DoubleAnimationUsingKeyFrames>

          </Storyboard>
          <Storyboard x:Key="HoverOff">

            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                           Storyboard.TargetName="HoverRectangle"
                                           Storyboard.TargetProperty="(UIElement.Opacity)">
              <SplineDoubleKeyFrame KeyTime="00:00:00.4000000"
                                    Value="0" />
            </DoubleAnimationUsingKeyFrames>

          </Storyboard>
          <Storyboard x:Key="SelectedOn">

            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                           Storyboard.TargetName="SelectedRectangle"
                                           Storyboard.TargetProperty="(UIElement.Opacity)">
              <SplineDoubleKeyFrame KeyTime="00:00:00.1000000"
                                    Value="1" />
            </DoubleAnimationUsingKeyFrames>

          </Storyboard>
          <Storyboard x:Key="SelectedOff">

            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                           Storyboard.TargetName="SelectedRectangle"
                                           Storyboard.TargetProperty="(UIElement.Opacity)">
              <SplineDoubleKeyFrame KeyTime="00:00:00.4000000"
                                    Value="0" />
            </DoubleAnimationUsingKeyFrames>

          </Storyboard>

        </ControlTemplate.Resources>
        <Grid SnapsToDevicePixels="true"
              Margin="1"
              IsHitTestVisible="True">
          <Rectangle x:Name="Background"
                     IsHitTestVisible="True"
                     Opacity="0.25"
                     Fill="{StaticResource NormalBrush}"
                     RadiusX="1"
                     RadiusY="1" />
          <Rectangle x:Name="HoverRectangle"
                     IsHitTestVisible="True"
                     Opacity="0"
                     Fill="{StaticResource NormalBrush}"
                     RadiusX="1"
                     RadiusY="1" />
          <Rectangle x:Name="SelectedRectangle"
                     IsHitTestVisible="False"
                     Opacity="0"
                     Fill="{StaticResource SelectedBackgroundBrush}"
                     RadiusX="1"
                     RadiusY="1" />
          <ContentPresenter Margin="5,2,0,2"
                            x:Name="contentPresenter"
                            VerticalAlignment="Center" />
          <Rectangle x:Name="FocusVisualElement"
                     Visibility="Collapsed"
                     Stroke="{StaticResource HoverShineBrush}"
                     StrokeThickness="1"
                     RadiusX="1"
                     RadiusY="1" />
        </Grid>
        <ControlTemplate.Triggers>
          <Trigger Property="IsHighlighted"
                   Value="true">
            <Trigger.ExitActions>
              <BeginStoryboard Storyboard="{StaticResource SelectedOff}"
                               x:Name="SelectedOff_BeginStoryboard" />
            </Trigger.ExitActions>
            <Trigger.EnterActions>
              <BeginStoryboard Storyboard="{StaticResource SelectedOn}"
                               x:Name="SelectedOn_BeginStoryboard" />
            </Trigger.EnterActions>

          </Trigger>
          <Trigger Property="IsMouseOver"
                   Value="True">
            <Trigger.ExitActions>
              <BeginStoryboard Storyboard="{StaticResource HoverOff}"
                               x:Name="HoverOff_BeginStoryboard" />
            </Trigger.ExitActions>
            <Trigger.EnterActions>
              <BeginStoryboard Storyboard="{StaticResource HoverOn}" />
            </Trigger.EnterActions>
          </Trigger>
          <Trigger Property="IsEnabled"
                   Value="false">
            <Setter Property="Foreground"
                    Value="{DynamicResource DisabledForegroundBrush}" />
          </Trigger>
        </ControlTemplate.Triggers>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

<Style x:Key="{x:Static ToolBar.ComboBoxStyleKey}"
       TargetType="{x:Type ComboBox}">
  <Setter Property="FontSize"
          Value="10" />
  <Setter Property="SnapsToDevicePixels"
          Value="true" />
  <Setter Property="Template"
          Value="{DynamicResource ComboBoxTemplate}" />
  <Setter Property="Foreground"
          Value="White" />
</Style>

组合框模板:(使用ThicknessConverter吐出收到调试窗口的厚度)

<ControlTemplate x:Key="ComboBoxTemplate"
                 TargetType="{x:Type ComboBox}">
  <Grid x:Name="grid">
    <Grid.Resources>
      <converters:ComboBoxThicknessConverter x:Key="thicknessConv" />
    </Grid.Resources>
    <ToggleButton Template="{DynamicResource ComboBoxToggleButton}"
                  BorderThickness="{TemplateBinding BorderThickness}"
                  BorderBrush="{TemplateBinding BorderBrush}"
                  x:Name="ToggleButton"
                  Focusable="false"
                  IsChecked="{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                  ClickMode="Press" />
    <ContentPresenter HorizontalAlignment="Left"
                      x:Name="ContentSite"
                      Margin="{TemplateBinding BorderThickness, Converter={StaticResource thicknessConv}}"
                      VerticalAlignment="Center"
                      Content="{TemplateBinding SelectedItem}"
                      ContentTemplate="{TemplateBinding ItemTemplate}"
                      ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
                      IsHitTestVisible="False" />
    <Popup IsOpen="{TemplateBinding IsDropDownOpen}"
           Placement="Bottom"
           x:Name="Popup"
           Focusable="False"
           AllowsTransparency="True"
           PopupAnimation="Slide">
      <Grid MaxHeight="{TemplateBinding MaxDropDownHeight}"
            MinWidth="{TemplateBinding ActualWidth}"
            x:Name="DropDown"
            SnapsToDevicePixels="True">
        <Border x:Name="DropDownBorder"
                Background="{DynamicResource ControlBackgroundBrush}"
                CornerRadius="3" />
        <ScrollViewer Margin="4,6"
                      Style="{DynamicResource NuclearScrollViewer}"
                      SnapsToDevicePixels="True"
                      HorizontalScrollBarVisibility="Auto"
                      VerticalScrollBarVisibility="Auto"
                      CanContentScroll="True"
                      Foreground="{DynamicResource {x:Static SystemColors.ActiveCaptionTextBrushKey}}">
          <StackPanel IsItemsHost="True"
                      KeyboardNavigation.DirectionalNavigation="Contained" />
        </ScrollViewer>
      </Grid>
    </Popup>
  </Grid>
  <ControlTemplate.Triggers>
    <Trigger Property="HasItems"
             Value="false">
      <Setter Property="MinHeight"
              Value="95"
              TargetName="DropDownBorder" />
    </Trigger>
    <Trigger Property="IsEnabled"
             Value="false">
      <Setter Property="Foreground"
              Value="{DynamicResource DisabledForegroundBrush}" />
      <Setter Property="Opacity"
              TargetName="grid"
              Value="0.5" />
    </Trigger>
    <Trigger Property="IsGrouping"
             Value="true">
      <Setter Property="ScrollViewer.CanContentScroll"
              Value="false" />
    </Trigger>
    <Trigger Property="AllowsTransparency"
             SourceName="Popup"
             Value="true">
      <Setter Property="Margin"
              Value="0,2,0,0"
              TargetName="DropDownBorder" />
    </Trigger>
    <Trigger Property="local:Dragging.IsDragTarget"
             Value="True">
      <Setter Property="BorderBrush"
              Value="{StaticResource DragTargetBorderBrush}"
              TargetName="ToggleButton" />
    </Trigger>
  </ControlTemplate.Triggers>
</ControlTemplate>

ComboBoxToggleButton:

<ControlTemplate x:Key="ComboBoxToggleButton"
                 TargetType="{x:Type ToggleButton}">
  <Grid x:Name="grid">
    <Grid.Resources>
      <converters:SpecialThicknessConverter x:Key="cv2" />
    </Grid.Resources>
    <Grid.ColumnDefinitions>
      <ColumnDefinition />
      <ColumnDefinition Width="20" />
    </Grid.ColumnDefinitions>
    <Rectangle Grid.ColumnSpan="2"
               HorizontalAlignment="Stretch"
               x:Name="Rectangle"
               VerticalAlignment="Stretch"
               RadiusX="3"
               RadiusY="3"
               StrokeThickness="{TemplateBinding BorderThickness, Converter={StaticResource cv2}}"
               Fill="{DynamicResource LightBrush}"
               Stroke="{TemplateBinding BorderBrush}" />

    <Border Grid.Column="1"
            Margin="2"
            Background="{DynamicResource BorderBrush}"
            CornerRadius="3"
            x:Name="border" />
    <Border Grid.Column="1"
            Margin="2"
            Background="{DynamicResource HoverBrush}"
            CornerRadius="3"
            x:Name="HoverBorder"
            Opacity="0" />
    <Border Grid.Column="1"
            Margin="2"
            Background="{DynamicResource HoverShineBrush}"
            CornerRadius="3"
            x:Name="HoverShineBorder"
            Opacity="0" />
    <Path Grid.Column="1"
          HorizontalAlignment="Center"
          x:Name="Arrow"
          VerticalAlignment="Center"
          Fill="{x:Null}"
          Data="M0.5,0.5 L3,6.5 5.5,0.5"
          Stroke="{DynamicResource GlyphBrush}"
          Margin="5,0"
          Height="7"
          StrokeThickness="2"
          Stretch="Fill" />
    <Border Grid.Column="1"
            Margin="2"
            Background="{DynamicResource ShineBrush}"
            CornerRadius="3"
            x:Name="ShineBorder" />
  </Grid>
  <ControlTemplate.Triggers>
    <Trigger Property="IsMouseOver"
             Value="true">
      <Trigger.ExitActions>
        <BeginStoryboard Storyboard="{StaticResource HoverOff}"
                         x:Name="HoverOff_BeginStoryboard" />
      </Trigger.ExitActions>
      <Trigger.EnterActions>
        <BeginStoryboard Storyboard="{StaticResource HoverOn}" />
      </Trigger.EnterActions>
    </Trigger>
    <Trigger Property="IsChecked"
             Value="true" />
    <Trigger Property="IsEnabled"
             Value="False">
      <Setter Property="Foreground"
              Value="{DynamicResource DisabledForegroundBrush}" />
      <Setter Property="Stroke"
              TargetName="Arrow"
              Value="{DynamicResource DisabledForegroundBrush}" />
      <Setter Property="Background"
              TargetName="border"
              Value="{DynamicResource DisabledBorderBrush}" />
      <Setter Property="Opacity"
              TargetName="grid"
              Value="0.8" />
    </Trigger>
  </ControlTemplate.Triggers>
</ControlTemplate>

其中一个厚度转换器用于为组合框右侧的构成按钮的边框和路径留出空间(20像素)。两者都包含在下面:

public class ComboBoxThicknessConverter : IValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var oldT = (Thickness)value;
        //Debug.WriteLine("cbx templ thickness = {" + oldT.Left + ", " + oldT.Top + ", " + oldT.Right + ", " + oldT.Bottom + "}");
        return new Thickness(oldT.Left, oldT.Top, oldT.Right + 20, oldT.Bottom);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}


public class SpecialThicknessConverter : IValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var oldT = (Thickness)value;
        Debug.WriteLine("toggle btn thickness = {" + oldT.Left + ", " + oldT.Top + ", " + oldT.Right + ", " + oldT.Bottom + "}");
        return oldT;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

除非我在ComboBoxToggleButton中手动将StrokeThickness更改为硬编码值,否则我总是以相同的厚度结束:1。

我出错的任何想法?据我所知,我正在将ComboBox中的BorderThickness正确地沿着层次结构的每个级别传递到ComboBoxToggleButton中的Rectangle。但是,当我查看调试窗口时,我看到ComboBox样式中的setter中使用的值,而不是声明ComboBox时使用的值。

提前致谢!

肖恩

2 个答案:

答案 0 :(得分:1)

TemplateBinding与普通Binding不同。它被优化为接收特定类型的值并将其直接传递给相同类型的属性。转换器,StringFormat等其他选项在TemplateBinding上不起作用,并且烦人地不会出现任何错误。如果您需要进行任何转换或尝试依靠内置类型转换来连接不同类型的属性,则需要使用与RelativeSource TemplatedParent的常规绑定:

{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness, Converter={StaticResource cv2}}

答案 1 :(得分:0)

最后,问题是Rectangle.Stroke(Shape.StrokeThickness是double)和Control.BorderThickness(厚度...... 4倍)之间的类型差异。

添加了一个转换器,并按照接受的答案中的建议使用了TemplateBinding,但它确实有效。

StrokeThickness="{Binding BorderThickness, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource rect2DoubleConv}}"


public class RectangleToDoubleConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var oldT = (Thickness)value;
        return (oldT.Left + oldT.Top + oldT.Right + oldT.Bottom) / 4.0;
    }
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}