How can I show a Balloon Tip over a textbox?

后端 未结 4 1348
情话喂你
情话喂你 2020-12-24 15:28

I have a C# WPF application using XAML and MVVM. My question is: How can I show a balloon tooltip above a text box for some invalid data entered by the user?

I want

相关标签:
4条回答
  • 2020-12-24 16:03

    I've been searching for a better solution than the BalloonDecorator, and ran across the http://www.hardcodet.net/projects/wpf-notifyicon project. It is using the WinAPI at the lowest level, which might give you a head start on building your own solution. It appears that at first glance it could potentially solve it, but I haven't had enough time to verify that the BalloonTip can be made to behave as you've described.

    Good luck on your project!

    0 讨论(0)
  • 2020-12-24 16:11

    Maybe you can host a Windows Forms control in WPF using the WindowsFormsHost type.

    There is a walkthrough available on MSDN on how to do this:

    Hosting a Windows Forms Composite Control in WPF

    Using this technique you could perhaps use the System.Windows.Forms.ToolTip control. If you set this control's IsBalloon property to true it will display as a balloon window.

    0 讨论(0)
  • 2020-12-24 16:26

    Just add a reference to System.Windows.Forms and C:\Program Files\Reference Assemblies\Microsoft\Framework.NETFramework\v4.0\WindowsFormsIntegration.dll and then:

        WindowsFormsHost host =new WindowsFormsHost();
    
        var toolTip1 = new System.Windows.Forms.ToolTip();
    
        toolTip1.AutoPopDelay = 5000;
        toolTip1.InitialDelay = 1000;
        toolTip1.ReshowDelay = 500;
        toolTip1.ShowAlways = true;
        toolTip1.IsBalloon = true;
        toolTip1.ToolTipIcon = System.Windows.Forms.ToolTipIcon.Info;
        toolTip1.ToolTipTitle = "Title:";
    
        System.Windows.Forms.TextBox tb = new System.Windows.Forms.TextBox();
        tb.Text="Go!";
        toolTip1.SetToolTip(tb, "My Info!");
        host.Child = tb;
        grid1.Children.Add(host);  //a container for windowsForm textBox 
    

    and this is a sample for WinForm ToolTip Ballon in WPF:

    enter image description here

    Hope this help!

    0 讨论(0)
  • 2020-12-24 16:27

    This BalloonDecorator Project is one that I am using on a current project to show help hints and error notifications. I know you could modify your error template to show this decorator, just like you could show an icon instead of the red borders. The benefit of using a decorator is you can make it look however you'd like, and won't have to depend on WinForms.

    BalloonDecorator.cs

    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media;
    
    namespace MyNamespace
    {
    public class BalloonDecorator : Decorator
    {
        private static double _thickness = 0;
        private static int OpeningGap = 10;
    
        public static readonly DependencyProperty BackgroundProperty =
            DependencyProperty.Register("Background", typeof (Brush), typeof (BalloonDecorator));
    
        public static readonly DependencyProperty BorderBrushProperty =
            DependencyProperty.Register("BorderBrush", typeof (Brush), typeof (BalloonDecorator));
    
        public static readonly DependencyProperty PointerLengthProperty = 
            DependencyProperty.Register("PointerLength", typeof (double), typeof (BalloonDecorator),
            new FrameworkPropertyMetadata(10.0, FrameworkPropertyMetadataOptions.AffectsRender |
            FrameworkPropertyMetadataOptions.AffectsMeasure));
    
        public static readonly DependencyProperty CornerRadiusProperty = 
            DependencyProperty.Register("CornerRadius", typeof (double), typeof (BalloonDecorator),
            new FrameworkPropertyMetadata(10.0, FrameworkPropertyMetadataOptions.AffectsRender |
            FrameworkPropertyMetadataOptions.AffectsMeasure));
    
        public Brush Background
        {
            get { return (Brush) GetValue(BackgroundProperty); }
            set { SetValue(BackgroundProperty, value); }
        }
    
        public Brush BorderBrush
        {
            get { return (Brush) GetValue(BorderBrushProperty); }
            set { SetValue(BorderBrushProperty, value); }
        }
    
        public double PointerLength
        {
            get { return (double) GetValue(PointerLengthProperty); }
            set { SetValue(PointerLengthProperty, value); }
        }
    
        public double CornerRadius
        {
            get { return (double) GetValue(CornerRadiusProperty); }
            set { SetValue(CornerRadiusProperty, value); }
        }
    
        protected override Size ArrangeOverride(Size arrangeSize)
        {
            UIElement child = Child;
            if (child != null)
            {
                double pLength = PointerLength;
                Rect innerRect =
                    Rect.Inflate(new Rect(pLength, 0, Math.Max(0, arrangeSize.Width - pLength), arrangeSize.Height),
                                 -1 * _thickness, -1 * _thickness);
                child.Arrange(innerRect);
            }
    
            return arrangeSize;
        }
    
        protected override Size MeasureOverride(Size constraint)
        {
            UIElement child = Child;
            Size size = new Size();
            if (child != null)
            {
                Size innerSize = new Size(Math.Max(0, constraint.Width - PointerLength), constraint.Height);
                child.Measure(innerSize);
                size.Width += child.DesiredSize.Width;
                size.Height += child.DesiredSize.Height;
            }
    
            Size borderSize = new Size(2 * _thickness, 2 * _thickness);
            size.Width += borderSize.Width + PointerLength;
            size.Height += borderSize.Height;
    
            return size;
        }
    
        protected override void OnRender(DrawingContext dc)
        {
            Rect rect = new Rect(0, 0, RenderSize.Width, RenderSize.Height);
    
            dc.PushClip(new RectangleGeometry(rect));
            dc.DrawGeometry(Background, new Pen(BorderBrush, _thickness), CreateBalloonGeometry(rect));
            dc.Pop();
        }
    
        private StreamGeometry CreateBalloonGeometry(Rect rect)
        {
            double radius = Math.Min(CornerRadius, rect.Height / 2);
            double pointerLength = PointerLength;
    
            // All the points on the path
            Point[] points =
                {
                    new Point(pointerLength + radius, 0), new Point(rect.Width - radius, 0), // Top
                    new Point(rect.Width, radius), new Point(rect.Width, rect.Height - radius), // Right
                    new Point(rect.Width - radius, rect.Height), // Bottom
                    new Point(pointerLength + radius, rect.Height), // Bottom
                    new Point(pointerLength, rect.Height - radius), // Left
                    new Point(pointerLength, radius) // Left
                };
    
            StreamGeometry geometry = new StreamGeometry();
            geometry.FillRule = FillRule.Nonzero;
            using (StreamGeometryContext ctx = geometry.Open())
            {
                ctx.BeginFigure(points[0], true, true);
                ctx.LineTo(points[1], true, false);
                ctx.ArcTo(points[2], new Size(radius, radius), 0, false, SweepDirection.Clockwise, true, false);
                ctx.LineTo(points[3], true, false);
                ctx.ArcTo(points[4], new Size(radius, radius), 0, false, SweepDirection.Clockwise, true, false);
                ctx.LineTo(points[5], true, false);
    
                ctx.ArcTo(points[6], new Size(radius, radius), 0, false, SweepDirection.Clockwise, true, false);
    
                // Pointer
                if (pointerLength > 0)
                {
                    ctx.LineTo(rect.BottomLeft, true, false);
                    ctx.LineTo(new Point(pointerLength, rect.Height - radius - OpeningGap), true, false);
                }
                ctx.LineTo(points[7], true, false);
    
                ctx.ArcTo(points[0], new Size(radius, radius), 0, false, SweepDirection.Clockwise, true, false);
            }
            return geometry;
        }
    }
    }
    

    Just make sure that this class's namespace is loaded into the XAML imports (I use a namespace called "Framework"), and it is simple to use:

        <Framework:BalloonDecorator  Background="#FFFF6600" PointerLength="50"
         CornerRadius="5" Opacity=".9" Margin="200,120,0,0"
         HorizontalAlignment="Left" VerticalAlignment="Top" Visibility="{Binding UnitPriceChangedBalloonVisibility}">
            <Border CornerRadius="2">
                <Border CornerRadius="2">
                    <Button Height="Auto" Command="{Binding CloseUnitPriceChangedBalloonCommand}" Background="Transparent" BorderBrush="{x:Null}">
                    <TextBlock Text="Please review the price. The Units have changed."
                         HorizontalAlignment="Left"
                         VerticalAlignment="Top"
                         FontStyle="Italic"
                         TextWrapping="Wrap"
                         Margin="10"
                         />
                         </Button>
                </Border>
            </Border>
        </Framework:BalloonDecorator>
    

    Obviously, I tie the visibility to a binding, but you could just set it to true and put this inside your Validation.ErrorTemplate.

    Hope this helps!

    0 讨论(0)
提交回复
热议问题