0

在我的 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>

ComboBox 模板:(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>

组合框切换按钮:

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

其中一个厚度转换器用于为构成 ComboBox 右侧的按钮的边框和路径留出空间(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 样式中设置器中使用的值,而不是声明 ComboBox 时使用的值。

提前致谢!

肖恩

4

2 回答 2

1

TemplateBinding 与普通的 Binding 不同。它经过优化,可以接收特定类型的值并将其直接传递给相同类型的属性。Converters、StringFormat 等其他选项在 TemplateBinding 上不起作用,而且令人讨厌的是不会给出任何错误。如果您需要进行任何转换或尝试通过依赖内置类型转换来连接不同类型的属性,则需要使用普通的 Binding 和 RelativeSource TemplatedParent:

{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness, Converter={StaticResource cv2}}
于 2013-01-23T13:59:59.807 回答
0

double最后,问题是 Rectangle.Stroke (Shape.StrokeThickness 是 a )和 Control.BorderThickness (这是一个 Thickness... 4 doubles)之间的类型差异。

添加了一个转换器并按照接受的答案中的建议使用了 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();
    }
}
于 2013-01-24T10:31:27.213 回答