具有自定义控件的Visual Studio WPF UI设计器

时间:2014-11-07 21:26:55

标签: c# wpf visual-studio-2013

这是一个奇怪的问题:

我有一个继承自TextBox的自定义控件,并提供" ghost"文本 - 例如它说"用户名"在一个盒子里,直到你在里面点击它,然后"鬼"文本消失,用户可以输入他们的用户名。

"幽灵文字"对于控件而言,它只是TextBox子类中的属性。然后,只要相关,我就会TextBox.Text设置它。

在Visual Studio WPF XAML预览窗口(标准UI设计窗口)中,我希望能够预览" "幽灵文字" - 就像设置文本框的实际文本一样,您可以在预览中看到它,而不仅仅是在运行应用程序时。

我尝试将Text属性设置为OnInitialised函数中的相关Ghost文本,但它对预览没有任何影响。

我应该在哪里放置影响设计器中控件预览的代码?

奖金问题:我所说的是什么名字"鬼"文本框?很高兴知道未来!

2 个答案:

答案 0 :(得分:2)

  

我称之为“鬼”文本框的实际名称是什么?对未来来说,上帝会是神!

我在描述其目的时看到这被称为“暗示”,或者在描述其外观时被称为“水印”。我倾向于使用前者,因为它描述了函数,它更符合WPF设计理念:实际表示由模板决定,概念“提示”可以不同方式呈现只需应用自定义样式/模板即可。当有人选择以另一种方式呈现它时,为什么暗示它应该是水印呢?

设计方面,我认为你正在以错误的方式接近这一点。我会这样实现这样一种方式,即TextBox以外的控制可以更容易地选择:使用附加属性。

我会创建一个静态类,比如HintProperties,它声明了几个附加的依赖属性:

  • Hint - 声明提示内容;通常是一个字符串,但它不一定是。它可能只是object,类似于Content的{​​{1}}属性。

  • ContentControl - 计算的只读HasHint属性,在bool更改时重新评估,并简单指示控件是否指定了Hint。用作Hint条件,可在控件模板中切换提示演示者的可见性。

然后,为Trigger(或其他控件)提供自定义样式,该样式将TextBox演示者覆盖在常规内容的顶部,默认情况下隐藏。当控件具有键盘焦点时,添加触发器以降低提示的不透明度,而当Hint为空字符串时,添加另一个触发器以提示提示Visible

如果你真的想要全力以赴,你可以投入TextHintTemplate属性。


但是,如果这看起来有点过分,您只需在派生的HintTemplateSelector类上直接声明HintWatermark属性即可。我不会尝试通过有条件地更改TextBox属性来实现此功能,因为这会干扰数据绑定,并且可能会影响值优先级。

答案 1 :(得分:0)

您可以使用通常在App.xaml中声明的样式以可重复使用的方式执行此操作。在此样式中,您将控件模板替换为您自己的实现,并将一些控件包装在一起。基本上,您使用透明背景从正常WatermarkTextBox组成TextBox,并在TextBlock后面放置TextBox控件和标准文字。此Visibility的{​​{1}}使用特定的TextBlock绑定到TextBox,因此当TextInputToVisibilityConverter包含文字或只有焦点时它会消失。

虽然这可能看起来像很多代码,但您可以定义一次这样的代码,只需设置TextBox

的样式,您就可以重复使用此代码。

某些资源的声明

TextBox

风格宣言:

xmlns:c="clr-namespace:YourNameSpace.Converters"
<SolidColorBrush x:Key="brushWatermarkBackground" Color="White" />
<SolidColorBrush x:Key="brushWatermarkForeground" Color="LightSteelBlue" />
<c:TextInputToVisibilityConverter x:Key="TextInputToVisibilityConverter" />

<Style x:Key="SearchTextBox" TargetType="{x:Type TextBox}"> <Setter Property="KeyboardNavigation.TabNavigation" Value="None"/> <Setter Property="FocusVisualStyle" Value="{x:Null}"/> <Setter Property="AllowDrop" Value="true"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type TextBox}"> <Grid Background="{StaticResource brushWatermarkBackground}"> <TextBlock Margin="5,5" Text="Search..." Foreground="{StaticResource brushWatermarkForeground}" > <TextBlock.Visibility> <MultiBinding Converter="{StaticResource TextInputToVisibilityConverter}"> <Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=TextBox}" Path="Text.IsEmpty" /> <Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=TextBox}" Path="IsFocused" /> </MultiBinding> </TextBlock.Visibility> </TextBlock> <Border x:Name="Border" Background="Transparent" BorderBrush="{DynamicResource SolidBorderBrush}" BorderThickness="1" Padding="2" CornerRadius="2"> <!-- The implementation places the Content into the ScrollViewer. It must be named PART_ContentHost for the control to function --> <ScrollViewer Margin="0" x:Name="PART_ContentHost" Style="{DynamicResource SimpleScrollViewer}" Background="Transparent"/> </Border> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> 的实现,只需要文本输入,转换为bool并将其转换为TextInputToVisibilityConverter。同时考虑到焦点。

Visibility

现在所有的基础设施都已到位。在你的view / usercontrol / window中只是改变了Textbox的样式,那就是你的水印文本框..

namespace YourNameSpace
{
    public class TextInputToVisibilityConverter : IMultiValueConverter
    {
        public object Convert(object[] values, 
              Type targetType, object parameter, 
              System.Globalization.CultureInfo culture)
        {
            if (values[0] is bool && values[1] is bool)
            {
                bool hasText = !(bool)values[0];
                bool hasFocus = (bool)values[1];

                if (hasFocus || hasText)
                    return Visibility.Collapsed;
            }

            return Visibility.Visible;
        }

        public object[] ConvertBack(object value, 
            Type[] targetTypes, object parameter, 
            System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}