【问题标题】:WPF Does rectangle stroke thickness affect location?WPF矩形描边粗细会影响位置吗?
【发布时间】:2014-08-05 08:15:03
【问题描述】:

我需要一个关于绘制打印矩形的快速建议。如果我定义

new drawingContext.DrawRectangle(new SolidColorBrush(Colors.Brown), 
                                 new Pen(new SolidColorBrush(Colors.Black), 3), 
                                 new Rect(new Point (1, 1), new Size(Width, Height))) 

矩形的最左上角位于 (1, 1) 上还是因为笔画粗细而存在偏移?

我已经尝试了一些变体来解决这个问题,但总是得到不同的结果,并且在那里找不到模式。

编辑:

protected override void OnRender(DrawingContext drawingContext)
    {
        Point start = new Point(1.5, 1.5);
        drawingContext.DrawRectangle(new SolidColorBrush(Colors.Brown), new Pen(new SolidColorBrush(Colors.Black), 3), new Rect(start, new Size(Width-2.5, Height-2.5)));

        base.OnRender(drawingContext);
    }

我试过了,假设矩形的参考点位于笔划的中间,厚度为 3。这似乎是正确的,至少在矩形的左侧和上侧。我也确实定义了一个边距,厚度为 1。

Width 和 Height 对应于 printableWidth 和 printableHeight。因此我从 (1.5, 1.5) 到 (Width-2.5, Height-2.5)。 1 表示边距,1.5 表示在笔划的中间。但是看打印,矩形不适合右侧和底部。

【问题讨论】:

  • 你能展示你所做的测试和你发现的不一致吗?
  • 我相信线条的宽度为零,并且笔划应用在这些线条的顶部,但是通过做一些简单的绘图并查看放大的屏幕截图应该很容易测试。请注意,由于周围环境的原因,您的绘图可能从亚像素偏移开始,您需要在测试时避免这种情况。
  • @Martin,这是否意味着对于 4px 宽度的笔画,渲染线的 2px 将在零宽度线的左侧,而在垂直线的右侧则为 2?那么对于这里矩形的左垂直线,就其 x 坐标而言,这条线将从 -1 延伸到 3?
  • 抗锯齿很容易让你偏离轨道 :) 使用 SnapsToDevicePixels 也可以帮助/干扰。此外,如果包含的 canvas(?) 被缩放,那很容易搞砸。
  • 据我所知,实际上建议在大多数情况下使用 SnapsToDevicePixels。我通常将它设置在根窗口上,以便它向下渗透到控件。

标签: c# wpf


【解决方案1】:

简单地测试你的假设会很容易......也许比在这里提问更快:

<Grid Height="230">
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
    </Grid.RowDefinitions>
    <Rectangle Grid.RowSpan="4" Grid.Column="0" Fill="Black" Stroke="Black" StrokeThickness="1" Width="1" Height="230" Margin="5,0" HorizontalAlignment="Right" />
    <Rectangle Grid.Row="0" Grid.Column="1" Fill="White" Stroke="Black" StrokeThickness="1" Width="200" Height="50" />
    <Rectangle Grid.Row="1" Grid.Column="1" Fill="White" Stroke="Black" StrokeThickness="5" Width="200" Height="50" />
    <Rectangle Grid.Row="2" Grid.Column="1" Fill="White" Stroke="Black" StrokeThickness="10" Width="200" Height="50" />
    <Rectangle Grid.Row="3" Grid.Column="1" Fill="White" Stroke="Black" StrokeThickness="15" Width="200" Height="50" />
    <Rectangle Grid.RowSpan="4" Grid.Column="2" Fill="Black" Stroke="Black" StrokeThickness="1" Width="1" Height="230" Margin="5,0" HorizontalAlignment="Left" />
</Grid>

在这里,您可以看到不同的StrokeThickness 值对Rectangles 的大小或位置没有任何影响。

【讨论】:

  • link 两个矩形的大小和起点都相同,但红色的笔画粗细为 30,黑色的笔画粗细为 20。看来,正好相反。
  • 我刚刚添加了描述。刚刚有人跟我说话,所以花了一点时间。很抱歉。
  • 有趣...也许您应该改用 XAML,不会遇到这个问题?
  • XAML 将是一个选项,是的。事实上,我想避免它,因为我有更多以编程方式创建页面布局的经验。但是既然这里已经是这个问题了,恐怕还会有几个,我会试试XAML。感谢您的帮助!
  • 使用 XAML 和 data binding 绝对比在 WPF 中使用过程代码更可取。
【解决方案2】:

要自己测试,请在同一位置绘制两个矩形,边框颜色不同。

new drawingContext.DrawRectangle(new SolidColorBrush(Colors.Brown), 
                             new Pen(new SolidColorBrush(Colors.Red), 6),
                             new Rect(new Point (1, 1), new Size(Width, Height)));
// Note: second rectangle should have a *smaller* stroke thickness
new drawingContext.DrawRectangle(new SolidColorBrush(Colors.Brown), 
                             new Pen(new SolidColorBrush(Colors.Black), 3), 
                             new Rect(new Point (1, 1), new Size(Width, Height)));

如果画笔宽度影响矩形的位置,您会在第二个矩形的边缘看到红色边框。没有边框,没有效果。

【讨论】:

    【解决方案3】:

    据我所知,控件占用的总空间受其宿主 UIElement 的限制,并且不能在逻辑上超出这些边界,除非您专门对其进行了设置。 ClipToBounds 设置将在视觉上截断超出这些范围的任何内容。

    或者您正在与 Adorners 合作。

    【讨论】:

      【解决方案4】:

      在这里扩展答案,不要将StrokeThicknessBorderThickness 混淆。 FrameworkElement 继承层次有不同的分支

      • Shape 包括此处讨论的 UI 就绪 Rectangle 类与...
      • Decorator 具有 Child 属性的框架元素,其中框架具有厚度,例如Border ...
      • Control,通常带有Content的一些概念,其中一些也可能从Decorator模仿或暴露自己的BorderThickness/BorderBrush属性

      就当前问题而言,修改任何FrameworkElement 上的StrokeThicknessBorderThickness 只能在以下情况下影响其位置:

      1. 元素本身首先决定更改会影响其自身的整体大小。如果更改不影响其自身的实际大小,则其位置无法移动。但是如果元素的DesiredSize属性确实发生了变化,那么这个变化会被元素的布局父级注意到,给出第二个条件......
      2. ...还有,如果由于DesiredSize 更改,布局父级决定移动元素

      因此,所提出问题的正确答案是“可能”,因为它取决于所讨论的特定元素(在 OP 的情况下为 Rectangle以及布局父级它是附加的。

      对于 OP 的特定示例,显式设置 Rectangle 上的 WidthHeight 属性被解释为请求最终的总体大小,这将包括其 Stroke 的完整外部范围。这种模式的效果是总是减少内部区域,因为 Stroke 总是会从整体尺寸的内部中取出,或者“强制进入”,因为它对于最终的DesiredSize更重要同意Width/Height

      此外,我相信Rectangle 永远不会“增大”它的尺寸,以满足过度肥胖的StrokeThickness 填充形状的整个内部。因此,由于 Rectangle 永远无法满足条件 (1.),因此另一个条件没有实际意义,因此 Rectangle 上更改 StrokeThickness 永远不会导致它移动

      我修改了@Sheridan 提供的代码以比较Shape 派生元素Rectangle 的使用——它不包含任何布局子元素(左列)——与@ 987654354@-派生元素Border,包含TextBlock 作为报告内部可用大小的子元素(右列)。您可以看到,随着StrokeThickness 的变化,Rectangle 本身的ActualWidth/ActualHeight 不会发生变化,但TextBlock 包含在 Border 元素中的情况并非如此。

      这是实验代码,以防有人想进一步使用它:

      <Grid>
          <Grid.ColumnDefinitions>
              <ColumnDefinition />
              <ColumnDefinition />
          </Grid.ColumnDefinitions>
          <Grid.RowDefinitions>
              <RowDefinition />
              <RowDefinition />
              <RowDefinition />
              <RowDefinition />
          </Grid.RowDefinitions>
      
          <!-- Insert Grid.Resources (shown separately below) here -->
      
          <Rectangle x:Name="r1" Grid.Column="0" Grid.Row="0" StrokeThickness="1" />
          <Label Grid.Column="0" Grid.Row="0" Target="{x:Reference r1}" />
          <Rectangle x:Name="r2" Grid.Column="0" Grid.Row="1" StrokeThickness="5" />
          <Label Grid.Column="0" Grid.Row="1" Target="{x:Reference r2}" />
          <Rectangle x:Name="r3" Grid.Column="0" Grid.Row="2" StrokeThickness="10" />
          <Label Grid.Column="0" Grid.Row="2" Target="{x:Reference r3}" />
          <Rectangle x:Name="r4" Grid.Column="0" Grid.Row="3" StrokeThickness="15" />
          <Label Grid.Column="0" Grid.Row="3" Target="{x:Reference r4}" />
      
          <Border Grid.Column="1" Grid.Row="0" BorderThickness="1" Child="{StaticResource dt}" />
          <Border Grid.Column="1" Grid.Row="1" BorderThickness="5" Child="{StaticResource dt}" />
          <Border Grid.Column="1" Grid.Row="2" BorderThickness="10" Child="{StaticResource dt}" />
          <Border Grid.Column="1" Grid.Row="3" BorderThickness="15" Child="{StaticResource dt}" />
      
      </Grid>
      


      <Grid.Resources>
          <TextBlock x:Key="dt"
                     x:Shared="False" 
                     Foreground="White" 
                     DataContext="{Binding RelativeSource={RelativeSource Self}}">
              <Run Text="{Binding Path=DataContext.ActualWidth,StringFormat='ActualWidth: {0}',Mode=OneWay}" />
              <LineBreak />
              <Run Text="{Binding Path=DataContext.ActualHeight,StringFormat='ActualHeight: {0}',Mode=OneWay}" />
          </TextBlock>
      
          <Style TargetType="Label">
              <Setter Property="Template">
                  <Setter.Value>
                      <ControlTemplate TargetType="Label">
                          <TextBlock Foreground="White"
                                  DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"
                                  Width="{Binding Path=Target.ActualWidth}"
                                  Height="{Binding Path=Target.ActualHeight}">
                              <Run Text="{Binding Path=Target.ActualWidth,StringFormat='ActualWidth: {0}',Mode=OneWay}" />
                              <LineBreak />
                              <Run Text="{Binding Path=Target.ActualHeight,StringFormat='ActualHeight: {0}',Mode=OneWay}" />
                          </TextBlock>
                      </ControlTemplate>
                  </Setter.Value>
              </Setter>
          </Style>
      
          <Style x:Key="common_style" TargetType="FrameworkElement">
              <Setter Property="HorizontalAlignment" Value="Center" />
              <Setter Property="VerticalAlignment" Value="Center" />
              <Setter Property="Width" Value="150" />
              <Setter Property="Height" Value="80" />
          </Style>
      
          <Style TargetType="Border" BasedOn="{StaticResource common_style}">
              <Setter Property="BorderBrush" Value="Navy" />
              <Setter Property="Background" Value="Gray" />
          </Style>
      
          <Style TargetType="Rectangle" BasedOn="{StaticResource common_style}">
              <Setter Property="Stroke" Value="Navy" />
              <Setter Property="Fill" Value="Gray" />
          </Style>
      </Grid.Resources>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-10-10
        • 1970-01-01
        • 2020-09-20
        • 2014-09-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多