【问题标题】:How do you remove the space taken by an empty paragraph?如何删除空段落占用的空间?
【发布时间】:2015-09-01 19:31:09
【问题描述】:

如何去除段落中的空格?我尝试使用负数margin/padding,但它不接受这些属性的负值。有什么想法吗?

我的代码如下:

<FlowDocument>
    <Section>
        <Paragraph>1</Paragraph>
        <Paragraph>2</Paragraph>
        <Paragraph></Paragraph>
        <Paragraph>4</Paragraph>
    </Section>
</FlowDocument>

并且,上面代码的输出如下:

编辑:这是一个更有意义的示例(根据 cmets):

<FlowDocument>
    <Section>
        <Paragraph>
            <TextBlock Text="1" Visibility="Visible"/>
        </Paragraph>
        <Paragraph>
            <TextBlock Text="2" Visibility="Visible"/>
        </Paragraph>
        <Paragraph>
            <TextBlock Text="3" Visibility="Collapsed"/>
        </Paragraph>
        <Paragraph>
            <TextBlock Text="4" Visibility="Visible"/>
        </Paragraph>
    </Section>
</FlowDocument>

结果完全相同。

【问题讨论】:

  • 有什么原因你硬编码 4 段时,你的问题可能是空白的?如果它被压扁,用户将无法向该段落添加任何内容,那么它是否应该存在?如果段落显示来自其他地方的只读信息,也许最好根据需要添加段落?
  • 确实我也在想同样的事情。为什么不在视图模型级别过滤空段落?
  • 我的问题是我的问题的简化版本。该段落并不完全包含任何内容,它们有多个容器。但我可以处理这部分问题,我只需要知道如何完全隐藏一个段落,因为它没有任何“可见性”属性。
  • 您可以通过将段落从其父级中删除来隐藏它。但是,您仍然可以将其保留在内存中

标签: wpf xaml paragraph


【解决方案1】:

在 HTML 中,段落标记(即使为空)占用空间的原因是因为它是块级元素,因此它是 hasLayout。这意味着它具有由渲染代理分配给它的填充和行高等属性,并且它会导致换行

您能否找到一种方法来检测段落是否为空并更改其显示规则?可见性不会删除元素,只会使其不可见,因此段落标记仍会导致讨厌的换行符。事实上,之前发布的 line-height 解决方案仍然留在换行符中。 (An article about display vs visibility.)

CSS 规则display:none; 可用于将其从页面流中移除(参见上面的 hasLayout 链接)。我喜欢使用 CSS 类并以编程方式应用它。这样的事情通常可以解决问题:

.hidden {display:none;height:0;width:0;}

您还可以在该类中包含line-height:0; 以涵盖之前的建议。

【讨论】:

  • 这很有道理,但您将如何在 WPF 应用程序上实现这一点?
【解决方案2】:

试试这个

<FlowDocument>

            <Section>

                <Paragraph>
                   1
                </Paragraph>
                <Paragraph>
                    2
                </Paragraph>
                <Paragraph local:AttachNew.MyProperty="1">

                </Paragraph>

                    <Paragraph>
                    4
                </Paragraph>
            </Section>
        </FlowDocument>

public class AttachNew
    {
        public static int GetMyProperty(DependencyObject obj)
        {
            return (int)obj.GetValue(MyPropertyProperty);
        }

        public static void SetMyProperty(DependencyObject obj, int value)
        {
            obj.SetValue(MyPropertyProperty, value);
        }

        // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MyPropertyProperty =
            DependencyProperty.RegisterAttached("MyProperty", typeof(int), typeof(AttachNew), new PropertyMetadata(0, new PropertyChangedCallback(ChangeProp)));

        private static void ChangeProp(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Section objparent = (d as System.Windows.Documents.Paragraph).Parent as Section;
            objparent.Blocks.Remove((d as System.Windows.Documents.Paragraph));
        }
    }

【讨论】:

  • 这可行,但我想知道是否有比完全从其父块中删除块更简单的方法。不过感谢您的回答!
【解决方案3】:

如果在您的场景中让 VM 属性指示段落是否为空是可行的,那么这将起作用:-

<FlowDocument>
  <FlowDocument.Resources>
    <Style TargetType="{x:Type Paragraph}">
      <Setter Property="Margin" Value="0,0,0,18"/>
      <Style.Triggers>
        <Trigger Property="Tag" Value="True">
           <Setter Property="Margin" Value="0"/>
           <Setter Property="LineHeight" Value="0.1"/>
           <Setter Property="LineStackingStrategy" Value="BlockLineHeight"/>
        </Trigger>
      </Style.Triggers>
    </Style>
  </FlowDocument.Resources>
  <Section>
     <Paragraph x:Name="p1" Tag="{Binding IsPara1Empty}">1</Paragraph>
     <Paragraph x:Name="p2" Tag="{Binding IsPara2Empty}">2</Paragraph>
     <Paragraph x:Name="p3" Tag="{Binding IsPara3Empty}"></Paragraph>
     <Paragraph x:Name="p4" Tag="{Binding IsPara4Empty}">4</Paragraph>
  </Section>
</FlowDocument>

根据您的字体大小,您可能需要使用样式的“默认”边距值“0,0,0,18”。

或者,如果可以以编程方式确定段落是否为空,您可以创建一个继承的Paragraph 控件,该控件公开一个IsEmpty 依赖属性。触发器将使用它而不是 Tag,并且您不需要 VM 属性。

【讨论】:

  • “虚拟机属性”中的虚拟机是什么意思?
  • @MaximeTremblay-Savard - 查看模型(假设您使用的是 MVVM)
【解决方案4】:

我犹豫是否要发布这个,因为我确信一定有更好的方法,但由于没有其他人回复......

流文档Section 似乎用与段落的LineHeight 等效的空格来换行段落。

LineHeight 不能为 0,但可以非常小。在Section 上设置LineHeight 将删除所有段落周围的空格。

<FlowDocumentScrollViewer>
    <FlowDocumentScrollViewer.Resources>
        <Style TargetType="Paragraph">
            <Setter Property="Background" Value="LightBlue" />
        </Style>
    </FlowDocumentScrollViewer.Resources>

    <FlowDocument>
        <Section LineHeight="0.1">
            <Paragraph>1</Paragraph>
            <Paragraph>2</Paragraph>
            <Paragraph/>                       
            <Paragraph>4</Paragraph>
            <Paragraph>5</Paragraph>
        </Section>
    </FlowDocument>

</FlowDocumentScrollViewer>

像这样设置LineHeight一般不会影响段落内的文字,因为默认的LineStackingStrategy使用的是字体的高度。注意空白段落仍然有高度。

您可能认为只在空白段落上设置LineHeight 会起作用,但Section 仍然会尊重前一段的空白。由于上一段有正常的LineHeight,所以你还是得到了margin。

所以,为了完全删除你的空白段落,你需要在空白和前面的段落上设置LineHeight,并告诉你的空白段落使用LineHeight作为它的块高度:

<FlowDocumentScrollViewer>
    <FlowDocumentScrollViewer.Resources>
        <Style TargetType="Paragraph">
            <Setter Property="Background" Value="LightBlue" />
        </Style>
    </FlowDocumentScrollViewer.Resources>

    <FlowDocument>
        <Section>
            <Paragraph>1</Paragraph>
            <Paragraph LineHeight="0.1">2</Paragraph>
            <Paragraph LineHeight="0.1" LineStackingStrategy="BlockLineHeight"/>                       
            <Paragraph>4</Paragraph>
            <Paragraph>5</Paragraph>
        </Section>
    </FlowDocument>

</FlowDocumentScrollViewer>

我尝试编写一个触发器来自动为空白段落执行此操作,但不幸的是,Paragraph.Inlines.Count 不是 DependencyProperty,并且尝试使用它来检测空白段落是不可靠的,具体取决于填充段落的时间。

【讨论】:

  • 我正在遵循这个确切的策略并遇到 Inlines.Count=0 触发所有段落。 +1 :)
  • 看起来有点老套,但工作正常!关于 Paragraph.Inlines.Count,我不需要这样做,因为我已经知道段落中的内容。大多数时候(在我的情况下),该段落只包含一个 TextBlock,我知道其中的文本是什么,但是由于段落的原因,隐藏文本块仍然会留下一个空白区域。这就解释了为什么我需要知道如何摆脱这个空间。
  • 这确实是骇人听闻的,这就是我犹豫发布的原因。很高兴它似乎至少有效。
  • 我会留一些时间让其他人给出答案,如果没有人给出好的答案,我会给你“正确答案”和赏金。感谢您对我的问题的意见!
  • 没问题。我很想看看是否有“合适的”解决方案。