我有一个使用 XAML 和 MVVM 的 C# WPF 应用程序。我的问题是:如何在文本框上方显示用户输入的某些无效数据的气球工具提示?
我想为此使用 Microsoft 的本机气球控件。我如何将其实现到我的应用程序中?
只需添加对 System.Windows.Forms 和 C:\Program Files\Reference Assemblies\Microsoft\Framework.NETFramework 4.0\WindowsFormsIntegration.dll 的引用 然后:
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
这是 WPF 中 WinForm ToolTip Ballon 的示例:
希望这有帮助!
这个 BalloonDecorator 项目 是我在当前项目中使用的一个用于显示帮助提示和错误通知的项目。 我知道您可以修改错误模板来显示此装饰器,就像您可以显示图标而不是红色边框一样。 使用装饰器的好处是你可以让它看起来像你想要的那样,而不必依赖 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;
}
}
}
只需确保此类的命名空间已加载到 XAML 导入中(我使用名为“Framework”的命名空间),并且使用起来很简单:
<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>
显然,我将可见性绑定到绑定,但您可以将其设置为 true 并将其放入您的 Validation.ErrorTemplate 中。
希望这有帮助!
我一直在寻找比 BalloonDecorator 更好的解决方案,并浏览了 http://www.hardcodet.net/projects/wpf-notifyicon 项目。 它在最低级别使用 WinAPI,这可能会让您在构建自己的解决方案方面领先一步。 乍一看似乎可以解决这个问题,但我没有足够的时间来验证 BalloonTip 是否可以按照您所描述的那样运行。
祝您的项目顺利!
也许您可以使用 WindowsFormsHost 类型在 WPF 中托管 Windows 窗体控件。
MSDN 上有一个关于如何执行此操作的演练:
使用此技术,您也许可以使用 System.Windows.Forms.ToolTip 控件。如果将此控件的 IsBalloon 属性 设置为 true,它将显示为气球窗口。
看看这个:
https://www.nuget.org/packages/Laila.BalloonTip
https://sourceforge.net/projects/laila-balloontip/
只需添加 Nuget 包 Laila.BalloonTip,将控件放入 XAML 中并设置 PlacementTarget、Placement、PopupAnimation、Timeout 和 Text 或 ContentTemplate 以及可选的 HorizontalOffset 属性。您还可以通过 BalloonTemplate 属性重新定义整个气球。
查看示例应用程序和源代码。