TextBox自定义控件

首先来一发图:

今天主要说的textBox内部给予提示:

使用自定义控件方式:TextBoxTip继承TextBox

利用TextBox的背景画刷功能

VisualBrush是一种比较特殊的笔刷,它的功能仍然是用来给元素填充图案,但它的内容却可以是各种控件。

你可以将其理解为一个普通的容器,但在其内部的所有控件都会失去交互能力,而只保留显示能力。

                                    <TextBox.Background>
                                        <VisualBrush Stretch="None" AlignmentX="Left">
                                            <VisualBrush.Transform>
                                                <TranslateTransform X="5" Y="0"/>
                                            </VisualBrush.Transform>
                                            <VisualBrush.Visual>
                                                <TextBlock x:Name="PART_EmptyText" Grid.Column="0" Text="{TemplateBinding TooTipText}" 
                                            FontSize="12"   Visibility="Collapsed" Foreground="{TemplateBinding Foreground}"  
                                                   Focusable="False"/>
                                            </VisualBrush.Visual>
                                        </VisualBrush>
                                    </TextBox.Background>

在背景色上给予一个画刷,使用textBlock来显示要提示的文本。

使用条件触发控制画刷的显示与隐藏:

                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding Path=IsFocused,ElementName=PART_Text}" Value="False"/>
                                <Condition Binding="{Binding Path=Text,ElementName=PART_Text}" Value=""/>
                            </MultiDataTrigger.Conditions>
                            <Setter TargetName="PART_EmptyText" Property="Visibility" Value="Visible"/>
                        </MultiDataTrigger>

当TextBox失去焦点和文本内容为“”等两个条件成立时 画刷显示。

文本内部还放置了一个Button用于Clear

                        <Button  Grid.Column="1" 
                                 x:Name="Xbtn"
                                 ToolTip="Clear"
                                 Visibility="{TemplateBinding XButtonVisibility}"
                                 VerticalAlignment="Center" Margin="7 0"
                                 Command="{x:Static local:TextBoxTip.XButtonCommand}"
                                 Style="{StaticResource XCloseButton}"/>

给Button注册Command事件

        /// <summary>
        /// Since we're using RoutedCommands for the increase/decrease commands, we need to
        /// register them with the command manager so we can tie the events
        /// to callbacks in the control.
        /// </summary>
        private static void InitializeCommands()
        {
            XButtonCommand = new RoutedCommand("XButtonCommand", typeof(TextBoxTip));
            CommandManager.RegisterClassCommandBinding(typeof(TextBoxTip),
                  new CommandBinding(XButtonCommand, CloseButtonCommand));
        }

        public static void CloseButtonCommand(Object sender, ExecutedRoutedEventArgs e)
        {
            TextBoxTip control = sender as TextBoxTip;
            if (control != null)
            {
                control.Text = "";
            }
        }

        public static RoutedCommand XButtonCommand { set; get; }
View Code

同理:TextBoxSign也继承TextBox

不过该模板中放置了两个TextBox,一个用于显示正常的数据,比如文本为:如果个性签名很长的话就会自定计算长度是否超过文本width,然后给予截取字符串显示省略号(...)

一个用于显示截取后的文本内容,ShowTip的方式是采用上面画刷方式就不再多介绍了。

关于动态计算是重写了文本Size方法,然后在方法内部使用一个根据字体,字号,字体风格,字体填充方式计算

   protected override Size ArrangeOverride(Size arrangeBounds)
        {
            Size size = base.ArrangeOverride(arrangeBounds);
            if (NormalIsFocus == false)
            {
                ControlTrimming(arrangeBounds.Width);
            }
            return size;
        }
        private void ControlTrimming(double width)
        {
            if (!string.IsNullOrEmpty(this.Text))
            {
                NormalTxt.Visibility = Visibility.Hidden;
                TrimmingTxt.Visibility = Visibility.Visible;
                Typeface face = new Typeface(this.FontFamily, this.FontStyle, this.FontWeight, this.FontStretch);
                bool IsShowTip = false;
                TrimmingTxt.Text = TrimmingHelper.Trim(Text, "...", "", width - 10, face, this.FontSize, ref IsShowTip);
                Console.WriteLine(IsShowTip);
                ShowToolTip(IsShowTip);
            }
        }

如下图所示:width-10是为了提前10个宽度就要显示省略号:(减去10是为了解决 刚好文本长度填满整个文本时不显示省略号,临界值)

完整代码如下:

样式Xaml

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TipPlugs">

    <Style x:Key="XCloseButton" TargetType="{x:Type Button}">
        <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
        <Setter Property="Height" Value="10"/>
        <Setter Property="Width" Value="10"/>
        <Setter Property="ToolTip" Value="X"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Grid >
                        <Path x:Name="mPath" Data="M2.859125,1.4685 L1.4217499,2.889875 3.5625651,5.015809 1.4682621,7.1103131 2.8748757,8.5319866 4.9848079,6.4375335 7.111161,8.5628746 8.5181325,7.1407751 6.3768616,5.0154436 8.5341407,2.8744748 7.1115867,1.4523758 4.9855734,3.624328 z" Fill="White" Stretch="Fill" Margin="0.203" Stroke="#FFFFA1A1" Visibility="Collapsed"/>
                        <Path x:Name="nPath" Data="M2.859125,1.4685 L1.4217499,2.889875 3.5625651,5.015809 1.4682621,7.1103131 2.8748757,8.5319866 4.9848079,6.4375335 7.111161,8.5628746 8.5181325,7.1407751 6.3768616,5.0154436 8.5341407,2.8744748 7.1115867,1.4523758 4.9855734,3.624328 z" Fill="#4C666666" Stretch="Fill" Margin="0.203" Stroke="{x:Null}"/>
                        <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"  Visibility="Collapsed"/>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsFocused" Value="True"/>
                        <Trigger Property="IsDefaulted" Value="True"/>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="Visibility" TargetName="nPath" Value="Collapsed"/>
                            <Setter Property="Visibility" TargetName="mPath" Value="Visible"/>
                        </Trigger>
                        <Trigger Property="IsPressed" Value="True"/>
                        <Trigger Property="IsEnabled" Value="False"/>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    
    <!--Tip-->
    <Style TargetType="{x:Type local:TextBoxTip}">
        <Setter Property="TopBrush" Value="{DynamicResource TextBoxTopBrush}"/>
        <Setter Property="BorderBrush" Value="{DynamicResource TextBoxBorder}"/>
        <Setter Property="Background" Value="{DynamicResource TextBoxBackground}"/>
        <Setter Property="XButtonVisibility" Value="Collapsed"/>
        <Setter Property="Foreground" Value="LightGray"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="Height" Value="26"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:TextBoxTip}">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                        <Rectangle x:Name="OuterglowRect" Visibility="Collapsed"
                                   Grid.ColumnSpan="2"
                                   RadiusX="2" RadiusY="2" Stroke="{DynamicResource OuterBlueBrush}"  StrokeThickness="1.5" />
                        <Border x:Name="Bd" SnapsToDevicePixels="True" 
                                CornerRadius="2"  Margin="1"
                                Grid.ColumnSpan="2"
                                Background="{TemplateBinding Background}"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="{TemplateBinding BorderThickness}">
                        </Border>
                        <TextBox x:Name="PART_Text" Grid.Column="0" 
                                     BorderThickness="0"
                                     Style="{x:Null}"
                                     Padding="2,2"
                                     VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                                     HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                                     IsReadOnly="{Binding IsReadOnly,Mode=TwoWay,
                                     UpdateSourceTrigger=PropertyChanged,RelativeSource={RelativeSource Mode=TemplatedParent}}"
                                     Text="{Binding Path=Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, 
                                RelativeSource={RelativeSource TemplatedParent}}">
                            <TextBox.Background>
                                <VisualBrush Stretch="None" AlignmentX="Left">
                                    <VisualBrush.Transform>
                                        <TranslateTransform X="5" Y="0"/>
                                    </VisualBrush.Transform>
                                    <VisualBrush.Visual>
                                        <TextBlock x:Name="PART_EmptyText" Grid.Column="0" Text="{TemplateBinding TooTipText}" 
                                            FontSize="12"   Visibility="Collapsed" Foreground="{TemplateBinding Foreground}"  
                                                   Focusable="False"/>
                                    </VisualBrush.Visual>
                                </VisualBrush>
                            </TextBox.Background>
                        </TextBox>
                        <Rectangle x:Name="glowRect" Margin="2 2 2 0" 
                                   RadiusX="2" RadiusY="2" Stroke="{x:Null}" 
                                   SnapsToDevicePixels="true" StrokeThickness="0" 
                                   Grid.ColumnSpan="2"
                                   Fill="{TemplateBinding TopBrush}" Height="3" VerticalAlignment="Top" >

                        </Rectangle>
                        <Button  Grid.Column="1" 
                                 x:Name="Xbtn"
                                 ToolTip="Clear"
                                 Visibility="{TemplateBinding XButtonVisibility}"
                                 VerticalAlignment="Center" Margin="7 0"
                                 Command="{x:Static local:TextBoxTip.XButtonCommand}"
                                 Style="{StaticResource XCloseButton}"/>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding Path=IsFocused,ElementName=PART_Text}" Value="False"/>
                                <Condition Binding="{Binding Path=Text,ElementName=PART_Text}" Value=""/>
                            </MultiDataTrigger.Conditions>
                            <Setter TargetName="PART_EmptyText" Property="Visibility" Value="Visible"/>
                        </MultiDataTrigger>

                        <!--<MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding Path=IsReadOnly}" Value="True"/>
                                <Condition Binding="{Binding Path=Text,ElementName=PART_Text}" Value=""/>
                            </MultiDataTrigger.Conditions>
                            <Setter TargetName="PART_EmptyText" Property="Visibility" Value="Visible"/>
                        </MultiDataTrigger>-->

                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="BorderBrush" Value="Transparent"/>
                            <Setter TargetName="OuterglowRect" Property="Visibility" Value="Visible"/>
                        </Trigger>
                        <Trigger Property="IsReadOnly" Value="True">
                            <Setter Property="BorderBrush" TargetName="Bd" Value="{DynamicResource TextBoxBorderRead}"/>
                            <Setter Property="Background" TargetName="Bd" Value="{DynamicResource TextBoxBgRead}"/>
                            <Setter Property="Visibility" TargetName="glowRect" Value="Collapsed"/>
                        </Trigger>
                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding Path=Text,ElementName=PART_Text}" Value=""/>
                            </MultiDataTrigger.Conditions>
                            <Setter Property="Visibility" TargetName="Xbtn" Value="Collapsed"/>
                        </MultiDataTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>

    </Style>

    <!-- Sign TextBox-->
    <local:TextBoxConverter x:Key="EmptyConverter"/>
    <Style TargetType="{x:Type local:TextBoxSign}">
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="FocusVisualStyle"  Value="{x:Null}"></Setter>
        <Setter Property="BorderBrush" Value="Transparent"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="VerticalContentAlignment" Value="Center"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:TextBoxSign}">

                    <Border x:Name="OutBorder" BorderThickness="1"  CornerRadius="2" SnapsToDevicePixels="True">
                        <Border x:Name="InnerBorder"
                                CornerRadius="2" SnapsToDevicePixels="True"
                                Background="{TemplateBinding Background}"
                                Padding="{TemplateBinding Padding}"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="{TemplateBinding BorderThickness}">
                            <Grid>
                                <TextBox x:Name="PART_Text" Grid.Column="0" 
                                        BorderThickness="0"
                                        Style="{x:Null}"
                                        FocusVisualStyle="{x:Null}"
                                        VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                                        HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                                        IsReadOnly="{Binding IsReadOnly,Mode=TwoWay,
                                        UpdateSourceTrigger=PropertyChanged,RelativeSource={RelativeSource Mode=TemplatedParent}}"
                                        Text="{Binding Path=Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, 
                                        RelativeSource={RelativeSource TemplatedParent}}">
                                    <TextBox.Background>
                                        <VisualBrush Stretch="None" AlignmentX="Left">
                                            <VisualBrush.Transform>
                                                <TranslateTransform X="5" Y="0"/>
                                            </VisualBrush.Transform>
                                            <VisualBrush.Visual>
                                                <TextBlock x:Name="PART_EmptyText" Grid.Column="0" Text="{TemplateBinding TooTipText}" 
                                            FontSize="12"   Visibility="Collapsed" Foreground="{TemplateBinding Foreground}"  
                                                   Focusable="False"/>
                                            </VisualBrush.Visual>
                                        </VisualBrush>
                                    </TextBox.Background>
                                </TextBox>
                                <TextBox x:Name="txbTrimming"
                                        Visibility="Hidden"
                                        BorderThickness="0"
                                        Style="{x:Null}"
                                        FocusVisualStyle="{x:Null}"
                                        Background="Transparent"
                                        VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                                        HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"/>
                            </Grid>
                        </Border>
                    </Border>
                    <ControlTemplate.Triggers>

                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding IsFocused, ElementName=PART_Text}" Value="False"/>
                                <Condition Binding="{Binding Text, ElementName=PART_Text,Converter={StaticResource EmptyConverter}}" Value=""/>
                            </MultiDataTrigger.Conditions>
                            <Setter TargetName="PART_EmptyText" Property="Visibility" Value="Visible"/>
                            <Setter TargetName="PART_Text" Property="Visibility" Value="Visible"/>
                        </MultiDataTrigger>

                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding IsFocused, ElementName=PART_Text}" Value="True"/>
                            </MultiDataTrigger.Conditions>
                            <Setter TargetName="OutBorder" Property="BorderBrush" Value="Gray"/>
                            <Setter Property="Background" Value="White"  TargetName="InnerBorder"/>
                            <Setter Property="BorderBrush" Value="Transparent" TargetName="InnerBorder"/>
                            <Setter Property="BorderThickness" Value="0" TargetName="InnerBorder"/>
                        </MultiDataTrigger>

                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding IsFocused, ElementName=PART_Text}" Value="False"/>
                                <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Mode=Self}}" Value="True"/>
                            </MultiDataTrigger.Conditions>
                            <Setter TargetName="OutBorder" Property="BorderBrush" Value="#7F353535"/>
                            <Setter Property="BorderBrush"  Value="#B2FFFFFF" TargetName="InnerBorder"/>
                            <Setter Property="Background" TargetName="InnerBorder">
                                <Setter.Value>
                                    <LinearGradientBrush StartPoint="0.5,1" EndPoint="0.5,0">
                                        <GradientStop Offset="0" Color="#A0FFFFFF"/>
                                        <GradientStop Offset="0.1" Color="#70FFFFFF"/>
                                        <GradientStop Offset="0.4" Color="#0CFFFFFF"/>
                                        <GradientStop Offset="0.5" Color="#00FFFFFF"/>
                                        <GradientStop Offset="0.6" Color="#0CFFFFFF"/>
                                        <GradientStop Offset="0.9" Color="#70FFFFFF"/>
                                        <GradientStop Offset="1" Color="#A0FFFFFF"/>
                                    </LinearGradientBrush>
                                </Setter.Value>
                            </Setter>
                        </MultiDataTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>
View Code

 TextBoxTip Code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace TipPlugs
{
    /// <summary>
    /// 按照步骤 1a 或 1b 操作,然后执行步骤 2 以在 XAML 文件中使用此自定义控件。
    ///
    /// 步骤 1a) 在当前项目中存在的 XAML 文件中使用该自定义控件。
    /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根 
    /// 元素中: 
    ///
    ///     xmlns:MyNamespace="clr-namespace:TextBoxTip"
    ///
    ///
    /// 步骤 1b) 在其他项目中存在的 XAML 文件中使用该自定义控件。
    /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根 
    /// 元素中: 
    ///
    ///     xmlns:MyNamespace="clr-namespace:TextBoxTip;assembly=TextBoxTip"
    ///
    /// 您还需要添加一个从 XAML 文件所在的项目到此项目的项目引用,
    /// 并重新生成以避免编译错误: 
    ///
    ///     在解决方案资源管理器中右击目标项目,然后依次单击
    ///     “添加引用”->“项目”->[浏览查找并选择此项目]
    ///
    ///
    /// 步骤 2)
    /// 继续操作并在 XAML 文件中使用控件。
    ///
    ///     <MyNamespace:TextBoxTip/>
    ///
    /// </summary>
    public class TextBoxTip : TextBox
    {
        static TextBoxTip()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(TextBoxTip), new FrameworkPropertyMetadata(typeof(TextBoxTip)));
            InitializeCommands();
        }



        protected override void OnTextChanged(TextChangedEventArgs e)
        {
            string text = this.Text;
            if (!string.IsNullOrEmpty(text) && IsShowXButton)
            {
                XButtonVisibility = Visibility.Visible;
            }
            //else if (string.IsNullOrEmpty(text))
            //{
            //    XButtonVisibility = Visibility.Collapsed;
            //}
            base.OnTextChanged(e);
        }

        /// <summary>
        /// 提示文本,如:请输入用户名
        /// </summary>
        public string TooTipText
        {
            get { return (string)GetValue(TooTipTextProperty); }
            set { SetValue(TooTipTextProperty, value); }
        }

        // Using a DependencyProperty as the backing store for TootipText.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TooTipTextProperty =
            DependencyProperty.Register("TooTipText", typeof(string), typeof(TextBoxTip), new UIPropertyMetadata("", null));


        /// <summary>
        /// 三个像素描边颜色
        /// </summary>
        public Brush TopBrush
        {
            get { return (Brush)GetValue(TopBrushProperty); }
            set { SetValue(TopBrushProperty, value); }
        }

        // Using a DependencyProperty as the backing store for TopBrush.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TopBrushProperty =
            DependencyProperty.Register("TopBrush", typeof(Brush), typeof(TextBoxTip), new UIPropertyMetadata(null));


        /// <summary>
        /// 控制显示X按钮
        /// </summary>
        public Visibility XButtonVisibility
        {
            get { return (Visibility)GetValue(XButtonVisibilityProperty); }
            set { SetValue(XButtonVisibilityProperty, value); }
        }

        // Using a DependencyProperty as the backing store for XButtonVisibility.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty XButtonVisibilityProperty =
            DependencyProperty.Register("XButtonVisibility", typeof(Visibility), typeof(TextBoxTip),
            new UIPropertyMetadata(Visibility.Collapsed));


        /// <summary>
        ///  是否显示X按钮
        /// </summary>
        public bool IsShowXButton
        {
            get { return (bool)GetValue(IsShowXButtonProperty); }
            set { SetValue(IsShowXButtonProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsShowXButton.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsShowXButtonProperty =
            DependencyProperty.Register("IsShowXButton", typeof(bool), typeof(TextBoxTip), new UIPropertyMetadata(false));


        /// <summary>
        /// Since we're using RoutedCommands for the increase/decrease commands, we need to
        /// register them with the command manager so we can tie the events
        /// to callbacks in the control.
        /// </summary>
        private static void InitializeCommands()
        {
            XButtonCommand = new RoutedCommand("XButtonCommand", typeof(TextBoxTip));
            CommandManager.RegisterClassCommandBinding(typeof(TextBoxTip),
                  new CommandBinding(XButtonCommand, CloseButtonCommand));
        }

        public static void CloseButtonCommand(Object sender, ExecutedRoutedEventArgs e)
        {
            TextBoxTip control = sender as TextBoxTip;
            if (control != null)
            {
                control.Text = "";
            }
        }

        public static RoutedCommand XButtonCommand { set; get; }
    }
}
View Code

TextBoxSign Code

using Commons;
using Commons.Helper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace TipPlugs
{
    /// <summary>
    /// 按照步骤 1a 或 1b 操作,然后执行步骤 2 以在 XAML 文件中使用此自定义控件。
    ///
    /// 步骤 1a) 在当前项目中存在的 XAML 文件中使用该自定义控件。
    /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根 
    /// 元素中: 
    ///
    ///     xmlns:MyNamespace="clr-namespace:TipPlugs"
    ///
    ///
    /// 步骤 1b) 在其他项目中存在的 XAML 文件中使用该自定义控件。
    /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根 
    /// 元素中: 
    ///
    ///     xmlns:MyNamespace="clr-namespace:TipPlugs;assembly=TipPlugs"
    ///
    /// 您还需要添加一个从 XAML 文件所在的项目到此项目的项目引用,
    /// 并重新生成以避免编译错误: 
    ///
    ///     在解决方案资源管理器中右击目标项目,然后依次单击
    ///     “添加引用”->“项目”->[浏览查找并选择此项目]
    ///
    ///
    /// 步骤 2)
    /// 继续操作并在 XAML 文件中使用控件。
    ///
    ///     <MyNamespace:TextBoxSign/>
    ///
    /// </summary>
    public class TextBoxSign : TextBox
    {
        static TextBoxSign()
        {

            DefaultStyleKeyProperty.OverrideMetadata(typeof(TextBoxSign), new FrameworkPropertyMetadata(typeof(TextBoxSign)));
        }

        TextBox NormalTxt;
        TextBox TrimmingTxt;
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            NormalTxt = this.Template.FindName("PART_Text", this) as TextBox;
            TrimmingTxt = this.Template.FindName("txbTrimming", this) as TextBox;
            TrimmingTxt.GotFocus += new RoutedEventHandler(TrimmingTxt_GotFocus);
            NormalTxt.LostFocus += new RoutedEventHandler(NormalTxt_LostFocus);
            NormalTxt.GotFocus += new RoutedEventHandler(NormalTxt_GotFocus);
            this.KeyUp += TextBoxSign_KeyUp;
        }

        private void TextBoxSign_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                //FocusManager.SetFocusedElement(this, null);
                //Keyboard.Focus(null);
                NormalTxt_LostFocus(null, null);
            }
        }

        void NormalTxt_GotFocus(object sender, RoutedEventArgs e)
        {
            NormalIsFocus = true;
        }

        protected override void OnTextChanged(TextChangedEventArgs e)
        {
            bool IsShow = false;
            if (!string.IsNullOrEmpty(this.Text))
            {
                IsShow = true;
            }
            else
            {
                IsShow = false;
            }
            ShowToolTip(IsShow);
            base.OnTextChanged(e);
        }

        /// <summary>
        /// 如何显示Tooltip文本
        /// </summary>
        /// <param name="isShow"></param>
        private void ShowToolTip(bool isShow)
        {
            if (isShow)
            {
                this.ToolTip = this.Text;
            }
            else
            {
                this.ToolTip = null;
            }
        }

        void NormalTxt_LostFocus(object sender, RoutedEventArgs e)
        {
            if (!string.IsNullOrEmpty(this.Text))
            {
                double wd = this.ActualWidth;
                ControlTrimming(wd);
            }
            NormalIsFocus = false;
        }

        bool NormalIsFocus = false;
        void TrimmingTxt_GotFocus(object sender, RoutedEventArgs e)
        {
            if (!string.IsNullOrEmpty(this.Text))
            {
                TrimmingTxt.Visibility = Visibility.Collapsed;
                NormalTxt.Visibility = Visibility.Visible;

            }
            NormalTxt.Focus();

        }

        protected override Size ArrangeOverride(Size arrangeBounds)
        {
            Size size = base.ArrangeOverride(arrangeBounds);
            if (NormalIsFocus == false)
            {
                ControlTrimming(arrangeBounds.Width);
            }
            return size;
        }
        private void ControlTrimming(double width)
        {
            if (!string.IsNullOrEmpty(this.Text))
            {
                NormalTxt.Visibility = Visibility.Hidden;
                TrimmingTxt.Visibility = Visibility.Visible;
                Typeface face = new Typeface(this.FontFamily, this.FontStyle, this.FontWeight, this.FontStretch);
                bool IsShowTip = false;
                TrimmingTxt.Text = TrimmingHelper.Trim(Text, "...", "中回复哈市的范德萨发", width - 10, face, this.FontSize, ref IsShowTip);
                Console.WriteLine(IsShowTip);
                ShowToolTip(IsShowTip);
            }
        }



        /// <summary>
        /// 提示文本,如:请输入用户名
        /// </summary>
        public string TooTipText
        {
            get { return (string)GetValue(TooTipTextProperty); }
            set { SetValue(TooTipTextProperty, value); }
        }

        // Using a DependencyProperty as the backing store for TootipText.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TooTipTextProperty =
            DependencyProperty.Register("TooTipText", typeof(string), typeof(TextBoxSign), new UIPropertyMetadata("", null));
    }

    class TextBoxConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {

            return "";
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}
View Code

TrimmingHelper Code

/***********************************************************************   
* Copyright(c) 2016-2050 ligl
* CLR 版本: 4.0.30319.42000   
* 文 件 名:TrimmingHelper   
* 创 建 人:ligl   
* 创建日期:2016/7/14 21:05:16   
* 修 改 人:ligl   
* 修改日期:   
* 备注描述:
************************************************************************/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows.Media;

namespace Commons.Helper
{
    /// <summary>
    /// 计算长度是否超出文本宽度的帮助类
    /// </summary>
    public class TrimmingHelper
    {


        /// <summary>
        /// 
        /// </summary>
        /// <param name="source">原始文本</param>
        /// <param name="suffix">省略文本符号</param>
        /// <param name="endNoTrimSource">追加省略号后面的文本,source+endNoTrimSource总体长度计算省略号</param>
        /// <param name="width">文本长度</param>
        /// <param name="face">字体类</param>
        /// <param name="fontsize">字体大小</param>
        /// <param name="ShowTip">True标示截取了文本</param>
        /// <returns></returns>
        public static string Trim(string source, string suffix, string endNoTrimSource, double width, Typeface face, double fontsize, ref bool ShowTip)
        {

            if (face != null)
            {
                //real display max width.
                double realWidth = width;

                //try to get GlyphTypeface.
                GlyphTypeface glyphTypeface;
                face.TryGetGlyphTypeface(out glyphTypeface);

                if (glyphTypeface != null)
                {
                    //calculate end string 's display width.
                    if (!string.IsNullOrEmpty(endNoTrimSource))
                    {
                        double notrimWidth = 0;
                        foreach (char c in endNoTrimSource)
                        {
                            ushort w;
                            glyphTypeface.CharacterToGlyphMap.TryGetValue(c, out w);
                            notrimWidth += glyphTypeface.AdvanceWidths[w] * fontsize;
                        }

                        realWidth = width - notrimWidth;
                    }

                    //calculate source 's screen width
                    double sourceWidth = 0;
                    if (!string.IsNullOrEmpty(source))
                    {
                        foreach (char c in source)
                        {
                            ushort w;
                            glyphTypeface.CharacterToGlyphMap.TryGetValue(c, out w);
                            sourceWidth += glyphTypeface.AdvanceWidths[w] * fontsize;
                        }
                    }

                    //don't need to trim.
                    if (sourceWidth <= realWidth) return source + endNoTrimSource;

                    //calculate suffix's display width
                    double suffixWidth = 0;
                    if (!string.IsNullOrEmpty(suffix))
                    {
                        foreach (char c in suffix)
                        {
                            ushort w;
                            glyphTypeface.CharacterToGlyphMap.TryGetValue(c, out w);
                            suffixWidth += glyphTypeface.AdvanceWidths[w] * fontsize;
                        }
                    }

                    realWidth = realWidth - suffixWidth;

                    if (realWidth > 0)
                    {
                        sourceWidth = 0;
                        string trimStr = string.Empty;
                        foreach (char c in source)
                        {
                            ushort w;
                            glyphTypeface.CharacterToGlyphMap.TryGetValue(c, out w);

                            double cWidth = glyphTypeface.AdvanceWidths[w] * fontsize;

                            if ((sourceWidth + cWidth) > realWidth)
                            {
                                ShowTip = true;
                                return trimStr + suffix + endNoTrimSource;
                            }
                            trimStr += c;
                            sourceWidth += cWidth;
                        }
                    }
                    else
                    {
                        ShowTip = true;
                        if (width > suffixWidth) return suffix;
                        else return "...";

                    }
                }
            }
            ShowTip = false;
            return source + endNoTrimSource;
        }
    }
}
View Code

 使用方式:

<UserControl x:Class="TipPlugs.UserControlTip"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:TipPlugs"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.Resources>
        <!--ffffff 60%透明 6-1-->
        <SolidColorBrush x:Key="Brush61"  Color="#99FFFFFF"/>

        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0" x:Key="TopBdBrush">
            <GradientStop Color="#5B808080" Offset="0"/>
            <GradientStop Color="#19808080" Offset="1"/>
        </LinearGradientBrush>

        <SolidColorBrush x:Key="BdBrush" Color="#6643484B"></SolidColorBrush>
    </UserControl.Resources>
    <Grid>
        <StackPanel>
            <local:TextBoxTip 
                          VerticalContentAlignment="Top"
                          Background="{StaticResource Brush61}"
                          BorderBrush="{StaticResource BdBrush}"
                          TopBrush="{StaticResource TopBdBrush}"
                          Foreground="#FF7F7F7F"
                          IsShowXButton="True"
                           Width="200"   
                          HorizontalAlignment="Left"
                          TooTipText="输入关键字,回车进行搜索"          
                         x:Name="txtSearch"></local:TextBoxTip>
            <local:TextBoxSign x:Name="txbSignatrue" Text="{Binding Signature,Mode=TwoWay}"
                           HorizontalAlignment="Left"
                           TooTipText="编辑个性签名"  Height="22"  Width="200"      
                             Margin="0,2"/>
        </StackPanel>
     
    </Grid>
</UserControl>
View Code

 源代码下载

posted @ 2016-07-15 21:49  ligl007  阅读(1573)  评论(2编辑  收藏  举报