【问题标题】:WPF XAML show ellipsis if text does not fit in TextBlock如果文本不适合 TextBlock,WPF XAML 将显示省略号
【发布时间】:2016-09-24 18:37:12
【问题描述】:

如果我的文件名和路径不适合 TextBlock,我想显示省略号。

例如,如果我的文件名和路径是这样的:

C:\examples\example\folderA\folderB\folderC\myfilename.txt

我想在我的TextBlock 中显示它,如下所示:

C:\examples...myfilename.txt

更新: 我知道我可以使用TextTrimming 在末尾设置省略号。但我需要一种在中间某处设置省略号的方法。

【问题讨论】:

  • 我知道这可以在字符串的 end 处完成,但我认为没有任何内置方法可以在中间。
  • 我知道如何在末尾添加省略号。 TextTrimming 可以处理这个问题。我需要的是中间的省略号。谢谢你们。
  • Measuring the text 并不难做到,但要确定要显示的正确字符数以及在哪里分割文本就有点困难了。我会先弄乱IValueConverter(或IMultiValueConverter)来衡量文本是否适合框。如果没有,请将文本转换为适合并包含您的规则的内容。也许使用Path.GetFileName(..) 来获取文件名部分,将其从完整字符串中删除,并尝试修剪剩余的字符串以适合文本框的大小。
  • @dbnex14 您可能只能使用 XAML 来做到这一点,但这会很棘手。基本上,您将有两个属性,一个用于 FileName,一个用于 FilePath。它们都将进入彼此相邻的 TextBoxes,并为 FilePath 框打开 TextTrimming。然后你必须找到一种方法来设置它们的样式以隐藏框之间的边框并使它们重叠一点。很可能,您实际上会有一个第三个文本框,它位于包含完整字符串的所有内容之上,并且仅在编辑时可见。
  • 续。因此,在编辑时,用户会看到一个包含完整文件路径的文本框,但一旦失去焦点,模板就会更改为绑定到路径/名称的两个文本框,并且有一点重叠。由于路径/名称框将是只读模板,因此如果您想编写将完整字符串转换为文件路径或文件名的转换器,您甚至可以使用单个属性来完成它。到那时,我只需将这个东西做成一个带有 FileName 属性的自定义 UserControl,这样它就可以被重用:)

标签: wpf xaml textblock ellipsis


【解决方案1】:

我修改了 Rachel 提供的 XAML 以修复路径中出现在窗口大小调整上的额外空间。

    <Window.Resources>
    <local:FileNameConverter x:Key="FileNameConverter" />
    <local:FilePathConverter x:Key="FilePathConverter" />

    <Style x:Key="CustomFileText" TargetType="{x:Type TextBox}">
        <Style.Triggers>
            <DataTrigger Binding="{Binding IsKeyboardFocused, RelativeSource={RelativeSource Self}}" Value="false">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="TextBox">
                            <Grid>
                                <TextBox Grid.ColumnSpan="2" IsHitTestVisible="False"/>
                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition MaxWidth="{Binding ActualWidth, ElementName=PART_DirMaxWidth}"/>
                                        <ColumnDefinition Width="Auto"/>
                                    </Grid.ColumnDefinitions>
                                    <Canvas x:Name="PART_Canvas">
                                        <TextBlock x:Name="PART_DirMaxWidth" Margin="0" Padding="0" Text="{TemplateBinding Text, Converter={StaticResource FilePathConverter}}" Visibility="Hidden"/>
                                        <TextBlock Width="{Binding ActualWidth, ElementName=PART_Canvas}" Margin="0" Padding="0" Text="{TemplateBinding Text, Converter={StaticResource FilePathConverter}}" TextTrimming="CharacterEllipsis" Background="Transparent"/>
                                    </Canvas>
                                    <StackPanel Grid.Column="1" Orientation="Horizontal">
                                        <TextBlock Margin="0" Padding="0" Text="\" Background="Transparent"/>
                                        <TextBlock Margin="0" Padding="0" Text="{TemplateBinding Text, Converter={StaticResource FileNameConverter}}" Background="Transparent"/>
                                    </StackPanel>
                                </Grid>
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</Window.Resources>

【讨论】:

    【解决方案2】:

    好的,我很感兴趣,如果这可以只用基本的 XAML 来完成,而不是乱用测量或绘图,所以开始乱搞。我今天没有时间完成这个,但我想我会作为一个起点分享给你,如果你想把它清理掉剩下的问题。

    到目前为止,XAML 看起来像这样:

    <Window.Resources>
        <local:FileNameConverter x:Key="FileNameConverter" />
        <local:FilePathConverter x:Key="FilePathConverter" />
    
        <Style x:Key="CustomFileText" TargetType="{x:Type TextBox}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsKeyboardFocused, RelativeSource={RelativeSource Self}}" Value="false">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="TextBox">
                                <Grid>
                                    <TextBox IsHitTestVisible="False" /> <!-- for Border -->
                                    <DockPanel>
                                        <TextBlock Text="{TemplateBinding Text, Converter={StaticResource FileNameConverter}}" DockPanel.Dock="Right" Margin="-3,3,4,3" />
                                        <TextBlock Text="{TemplateBinding Text, Converter={StaticResource FilePathConverter}}" TextTrimming="CharacterEllipsis" Margin="4,3,0,3" />
                                    </DockPanel>
                                </Grid>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    
    <StackPanel Margin="10">
        <TextBox Text="C:\Program Files\Some Directory\SomeFile.txt" Style="{StaticResource CustomFileText}"/>
        <TextBox Margin="0,10" />
    </StackPanel>
    

    最终结果是,如果 TextBox 有编辑焦点,它会显示为普通的 TextBox :

    但是一旦用户将注意力转移到表单的其他位置,它就会分裂成两个单独的 TextBlock,它们使用转换器来解析 Directory 和 FileName。在目录上使用 TextTrimming 以产生您在问题中描述的效果:

    这样做的主要问题是在调整大小时,两个文本框之间会添加额外的空间。

    我可以在这里想到两个选项:

    1. 根据某种触发器制作模板,上面写着 “仅在 TextBox.DesiredSize.Width &gt; TextBox.ActualWidth 时使用此模板”

    2. 更改模板中的 XAML,使名称文本框以某种方式显示 “占用您需要的所有空间作为最小尺寸。如果有多余的空间,也将其分配给此框”,而目录文本框说“尽可能占用所有空间,但不要变得比你的内容大”。我不确定执行此操作的最佳方法,但我想它与不同的面板、TextBox 上的一些我现在想不到的属性或一些自定义转换器/绑定以限制大小有关。

    我猜#1 会更容易实现,但我现在没有时间弄清楚。希望这能给你一个好的起点,祝你好运! :)

    哦,转换器只是非常基本的。您可能想要添加更多保护措施,但这是我用于测试的内容:

    public class FileNameConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value != null && value is string)
                return System.IO.Path.GetFileName((string)value);
    
            return null;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    
    public class FilePathConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value != null && value is string)
                return System.IO.Path.GetDirectoryName((string)value);
    
            return null;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    

    【讨论】:

    • 谢谢瑞秋,我会看看这个。非常感激。会让您知道我能走多远,但目前正在尝试通过调整表单大小来解决问题。我已经用截图更新了它。如果你能照亮一些灯,那将不胜感激。问题是stackoverflow.com/questions/37465077/…
    • @dbnex14 哦,我没有意识到你也是这个问题 :)
    猜你喜欢
    • 1970-01-01
    • 2012-04-13
    • 1970-01-01
    • 2018-06-05
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    • 2015-09-06
    相关资源
    最近更新 更多