C# 如果文本超出文本框的可用宽度,则显示工具提示

C# 如果文本超出文本框的可用宽度,则显示工具提示,c#,wpf,xaml,C#,Wpf,Xaml,当文本框中的文本超过文本框的可用大小时,我试图显示工具提示,我已编写了一个附加属性,但如果我在文本框上设置了显式宽度,则该属性可以工作,但在重新调整窗口大小时,该属性不起作用(当我重新调整窗口大小时,文本框的大小会减小) 我的XAML: <Window.Resources> <Style TargetType="TextBox"> <Setter Property="local:TextBoxCropBehavior.TextBoxTool

当文本框中的文本超过文本框的可用大小时,我试图显示工具提示,我已编写了一个附加属性,但如果我在文本框上设置了显式宽度,则该属性可以工作,但在重新调整窗口大小时,该属性不起作用(当我重新调整窗口大小时,文本框的大小会减小)

我的XAML:

 <Window.Resources>
    <Style TargetType="TextBox">
        <Setter Property="local:TextBoxCropBehavior.TextBoxToolTip" Value="True"/>
        <Setter Property="BorderBrush" Value="Green"/>
    </Style>
</Window.Resources>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="10"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <TextBox Text=" first Textbox with long text dfdsfdsfdsfdslkjfwlekjorifdsfmldskfwepisdmfds;fdsfsfdsfdslkjfwlekjorifdsfmldskfwepisdmfds;fdsf"  x:Name="x" HorizontalAlignment="Right"/>
    <TextBox Text="Seceond text box" x:Name="y" Grid.Row="2" HorizontalAlignment="Right"/>
</Grid>

我的附属财产:

  public static class TextBoxCropBehavior
{
    public static bool GetTextBoxToolTip(DependencyObject obj)
    {
        return (bool)obj.GetValue(TextBoxCropToolTipProperty);
    }
    public static void SetTextBoxToolTip(DependencyObject obj, bool value)
    {
        obj.SetValue(TextBoxCropToolTipProperty, value);
    }

    public static readonly DependencyProperty TextBoxCropToolTipProperty = DependencyProperty.RegisterAttached("TextBoxToolTip", typeof(bool), typeof(TextBoxCropBehavior), new UIPropertyMetadata(false, OnTextCropChanges));
    private static void OnTextCropChanges(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var textbox = d as TextBox;
        textbox.SizeChanged += textbox_SizeChanged;
    }

    static void textbox_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        var textbox = sender as TextBox;
        textbox.Measure(new Size(Double.MaxValue, Double.MaxValue));
        var width = textbox.DesiredSize.Width;

        if (textbox.ActualWidth < width)
        {
            ToolTipService.SetToolTip(textbox, textbox.Text);
        }
        else
        {
            ToolTipService.SetToolTip(textbox, null);
        }
    }
}
公共静态类TextBoxCropBehavior
{
公共静态bool GetTextBoxToolTip(DependencyObject obj)
{
返回(bool)对象获取值(textBoxCroptolTipProperty);
}
公共静态void SetTextBoxToolTip(DependencyObject对象,布尔值)
{
对象设置值(TextBoxCroptolTipProperty,值);
}
public static readonly dependencProperty textboxcroptoltipproperty=dependencProperty.RegisterAttached(“TextBoxToolTip”、typeof(bool)、typeof(TextBoxCropBehavior)、new UIPropertyMetadata(false、OnTextCropChanges));
私有静态void OnTextCropChanges(DependencyObject d、DependencyPropertyChangedEventArgs e)
{
var textbox=d作为textbox;
textbox.SizeChanged+=textbox\u SizeChanged;
}
静态无效文本框\u SizeChanged(对象发送方,SizeChangedEventArgs e)
{
var textbox=发送方作为textbox;
textbox.Measure(新大小(Double.MaxValue,Double.MaxValue));
var width=textbox.DesiredSize.width;
if(textbox.ActualWidth

谁能告诉我哪里做错了吗?

我不知道您是否必须使用附加属性。但是试试这个

我修改了文本框的默认样式


转换器

public类ToolTipConverter:IMultiValueConverter
{
公共对象转换(对象[]值,类型targetType,对象参数,CultureInfo区域性)
{
如果(values==null | | values.Count()<2 | | values[0]==null){return null;}
如果((双)值[0]>0){返回值[1];}
else{return null;}
}
公共对象[]转换回(对象值,类型[]目标类型,对象参数,CultureInfo区域性)
{
抛出新的NotImplementedException();
}
}
编辑:行为版本


行为

公共部分类主窗口:窗口
{
公共主窗口()
{
初始化组件();
}
}
公共类OptionalToolTipBehavior:行为
{
受保护的覆盖无效附加()
{
this.AssociatedObject.Loaded+=AssociatedObject\u Loaded;
base.onatached();
}
已加载无效关联对象(对象发送方,事件参数e)
{
this.AssociatedObject.Loaded-=AssociatedObject\u Loaded;
ScrollViewer ScrollViewer=this.FindScrollViewer(关联对象);
如果(scrollViewer!=null)
{
this.AssociatedObject.SetBinding(TextBox.ToolTipProperty,
新绑定(){Source=scrollViewer,
路径=新属性路径(ScrollViewer.ScrollableWidthProperty),
Converter=ScrollableWithToTooltipConverter.Instance,
ConverterParameter=this.AssociatedObject.Text});
}
}
私有ScrollViewer FindScrollViewer(DependencyObject元素)
{
if(元素为ScrollViewer){返回元素为ScrollViewer;}
int childCount=VisualTreeHelper.GetChildrenCount(元素);
for(int i=0;i0)
{
返回参数.ToString();
}
返回null;
}
公共对象转换回(对象值、类型targetType、对象参数、CultureInfo区域性)
{
抛出新的NotImplementedException();
}
}
}

最好的方法是总是有工具提示,而不是在上面做任何逻辑。您只需要将ToolTip属性应用于文本框X。我希望它具有样式,以便其他开发人员可以自动获得该属性。@XAMLLover我尝试了相同的方法,但由于某些原因,它不起作用,仅当文本框具有显式的say Width=“300”时才起作用。我使用SizeChanged事件修复了该问题。谢谢大家的帮助。是的,我试过这种方法,但我觉得使用附加的行为更容易。谢谢你分享你的想法。我很高兴你解决了这个问题。我想告诉您,可以使用VisualTreeHelper将此代码更改为附加行为:)
<Window.Resources>
    <study:ToolTipConverter x:Key="ToolTipConverter"/>
    <Style TargetType="{x:Type TextBox}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TextBox}">
                    <Themes:ListBoxChrome x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderFocused="{TemplateBinding IsKeyboardFocusWithin}" SnapsToDevicePixels="True">
                        <Themes:ListBoxChrome.ToolTip>
                            <MultiBinding Converter="{StaticResource ToolTipConverter}">
                                <Binding ElementName="PART_ContentHost" Path="ScrollableWidth"/>
                                <Binding RelativeSource="{RelativeSource AncestorType={x:Type TextBox}}" Path="Text"/>
                            </MultiBinding>
                        </Themes:ListBoxChrome.ToolTip>
                        <ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                    </Themes:ListBoxChrome>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsEnabled" Value="False">
                            <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="10"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <TextBox Text=" first Textbox with long text dfdsfdsfdsfdslkjfwlekjorifdsfmldskfwepisdmfds;fdsfsfdsfdslkjfwlekjorifdsfmldskfwepisdmfds;fdsf" HorizontalAlignment="Right"/>
    <TextBox Text="Seceond text box" Grid.Row="2" HorizontalAlignment="Right"/>
</Grid>
public class ToolTipConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values == null || values.Count() < 2 || values[0] == null) { return null; }
        if ((double)values[0] > 0) { return values[1]; }
        else { return null; }
    }


    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
<TextBox Text=" first Textbox with long text dfdsfdsfdsfdslkjfwlekjorifdsfmldskfwepisdmfds;fdsfsfdsfdslkjfwlekjorifdsfmldskfwepisdmfds;fdsf" HorizontalAlignment="Right" x:Name="PART_Textbox">
    <i:Interaction.Behaviors>
        <study:OptionalToolTipBehavior/>
    </i:Interaction.Behaviors>
</TextBox>
public partial class MainWindow : Window
{

    public MainWindow()
    {
        InitializeComponent();
    }
}


public class OptionalToolTipBehavior : Behavior<TextBox>
{
    protected override void OnAttached()
    {
        this.AssociatedObject.Loaded += AssociatedObject_Loaded;
        base.OnAttached();
    }


    void AssociatedObject_Loaded(object sender, EventArgs e)
    {
        this.AssociatedObject.Loaded -= AssociatedObject_Loaded;

        ScrollViewer scrollViewer = this.FindScrollViewer(AssociatedObject);
        if (scrollViewer != null)
        {
            this.AssociatedObject.SetBinding(TextBox.ToolTipProperty,
                new Binding() { Source = scrollViewer, 
                    Path = new PropertyPath(ScrollViewer.ScrollableWidthProperty), 
                        Converter = ScrollableWithToTooltipConverter.Instance, 
                        ConverterParameter=this.AssociatedObject.Text});
        }
    }



    private ScrollViewer FindScrollViewer(DependencyObject element)
    {
        if (element is ScrollViewer) { return element as ScrollViewer; }

        int childCount = VisualTreeHelper.GetChildrenCount(element);
        for (int i = 0; i < childCount; i++)
        {
            return this.FindScrollViewer(VisualTreeHelper.GetChild(element, i));
        }
        return null;
    }



    private class ScrollableWithToTooltipConverter : IValueConverter
    {
        public static readonly ScrollableWithToTooltipConverter Instance = new ScrollableWithToTooltipConverter();

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if((double) value > 0)
            {
                return parameter.ToString();
            }
            return null;
        }


        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

}