尝试创建允许文本选择的 WPF 标签模板时出现格式问题

发布于 2024-11-05 23:55:02 字数 1376 浏览 4 评论 0原文

我需要允许选择只读屏幕中显示的文本。

我们的一位开发人员提出的一个简单解决方案是使用 TextBox 而不是 Label 或 TextBlock,具有以下样式:

<Style x:Key="ControlData" TargetType="{x:Type TextBox}">
    <Setter Property="Background" Value="Transparent" />
    <Setter Property="BorderThickness" Value="0" />
    <Setter Property="IsReadOnly" Value="True" />
    <Setter Property="TextWrapping" Value="Wrap" />
    <!-- unrelated properties ommitted -->
</Style>

我不喜欢在那里使用 TextBox 的想法,除其他外,因为它迫使我使用 Binding Mode=OneWay 作为只读属性,所以我试图定义一个可以应用于标签以获得相同结果的样式:

<Style x:Key="SelectableLabel" TargetType="Label">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Label">
                <TextBox Style="{StaticResource ControlData}"
                         Text="{Binding Path=Content, Mode=OneWay,
                                RelativeSource={RelativeSource FindAncestor,
                                                AncestorType=Label}}"/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

问题是,我的一些绑定已设置 StringFormat,但该设置已丢失。

  • 有没有办法保持外部绑定的格式?
  • 我应该以不同的方式创建我的模板/绑定吗?
  • 有没有比这更好的完全不同的方法?
  • 我应该停止挑剔并使用文本框吗?

I have a requirement to allow selecting the text displayed in read only screens.

A simple solution one of our developers came up with is using a TextBox instead of a Label or TextBlock, with the following style:

<Style x:Key="ControlData" TargetType="{x:Type TextBox}">
    <Setter Property="Background" Value="Transparent" />
    <Setter Property="BorderThickness" Value="0" />
    <Setter Property="IsReadOnly" Value="True" />
    <Setter Property="TextWrapping" Value="Wrap" />
    <!-- unrelated properties ommitted -->
</Style>

I don't like the idea of using a TextBox there, among other things because it forces me to use Binding Mode=OneWay for read-only properties, so I was trying to define a Style that I can apply to a label to get the same result:

<Style x:Key="SelectableLabel" TargetType="Label">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Label">
                <TextBox Style="{StaticResource ControlData}"
                         Text="{Binding Path=Content, Mode=OneWay,
                                RelativeSource={RelativeSource FindAncestor,
                                                AncestorType=Label}}"/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

The problem is, some of my bindings have StringFormat set, and that is lost.

  • Is there a way to keep the formatting of the outer binding?
  • Should I create my template/binding differently?
  • Is there a whole different approach that's better than this?
  • Should I stop with the nitpicking and go with the TextBox?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

靑春怀旧 2024-11-12 23:55:02

我认为使用标签并覆盖控件模板是个好主意。最后,这就是 WPF 中控制逻辑和布局分离的目的,您应该利用它来获得更清晰、更易于理解的 xaml 代码。

您是否尝试使用 TemplateBinding 代替普通 Binding ?我不确定这是否会保留外部绑定,但这是建议在数据模板中使用的绑定。

您还应该看一下 ContentPresenter 类,尽管我不确定它是否适用于此处,因为您的内部文本框仅具有字符串类型的 Text 属性...

编辑:
我解决了这个问题,虽然它比我想象的更复杂...

创建样式如下:

<Application.Resources>
    <LabelTest:ContentToTextConverter x:Key="contentConverter"  />
    <Style TargetType="Label">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Label">
                    <TextBox DataContext="{Binding Mode=OneWay, RelativeSource={RelativeSource FindAncestor,AncestorType=Label}}">
                        <TextBox.Text>
                            <MultiBinding Converter="{StaticResource contentConverter}" >
                                <Binding Mode="OneWay" Path="ContentStringFormat" />
                                <Binding Mode="OneWay" Path="Content" />
                            </MultiBinding>
                        </TextBox.Text>
                    </TextBox>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Application.Resources>

ContentToTextConverter 定义如下:

public class ContentToTextConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var format = values[0] as string;

        if (string.IsNullOrEmpty(format)) format = "{0}";
        else if(format.IndexOf('{') < 0) format = "{0:" + format + "}";

        return string.Format(culture, format, values[1]);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

如何使用的示例:

<Label ContentStringFormat="{}{0}$">
     <System:Int64>15</System:Int64>
</Label>

找到比这更好的解决方案会很好,但是对于此刻它应该按预期工作。

I think it's a good idea to use a Label and override the control template. At the end this is what the separation of Control logic and layout in WPF is for and you should make use of it to have a cleaner, more understandable xaml code.

Did you try using a TemplateBinding instad of normal Binding? I'm not sure if this will preserve the outer binding, but it's the recommended Binding to use in DataTemplates.

You should also take a look at the ContentPresenter class, although I'm not sure if it's applicable here, since your inner text box only has a Text property of type string...

Edit:
I solved the problem, although it was more complicated then I thought...

Create the style as following:

<Application.Resources>
    <LabelTest:ContentToTextConverter x:Key="contentConverter"  />
    <Style TargetType="Label">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Label">
                    <TextBox DataContext="{Binding Mode=OneWay, RelativeSource={RelativeSource FindAncestor,AncestorType=Label}}">
                        <TextBox.Text>
                            <MultiBinding Converter="{StaticResource contentConverter}" >
                                <Binding Mode="OneWay" Path="ContentStringFormat" />
                                <Binding Mode="OneWay" Path="Content" />
                            </MultiBinding>
                        </TextBox.Text>
                    </TextBox>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Application.Resources>

The ContentToTextConverter is defined as following:

public class ContentToTextConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var format = values[0] as string;

        if (string.IsNullOrEmpty(format)) format = "{0}";
        else if(format.IndexOf('{') < 0) format = "{0:" + format + "}";

        return string.Format(culture, format, values[1]);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

Example of how to use:

<Label ContentStringFormat="{}{0}$">
     <System:Int64>15</System:Int64>
</Label>

It would be nice to find a nicer solution than this, but for the moment it should work as expected.

蹲墙角沉默 2024-11-12 23:55:02

我应该停止挑剔并使用文本框吗?

确实。

Should I stop with the nitpicking and go with the TextBox?

Definitely.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文