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事件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <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; }
同理: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
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<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>
TextBoxTip Code
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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; } } }
TextBoxSign Code
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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(); } } }
TrimmingHelper Code
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/*********************************************************************** * 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; } } }
使用方式:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<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>