如何在 WPF 应用程序中实现气球消息

2024-05-14

我们想使用气球消息,如UX Guide http://msdn.microsoft.com/en-us/library/aa511451.aspx来自微软。我发现一些示例使用 Windows 窗体中的本机代码,但本机代码需要组件的句柄,这对于 WPF 应用程序来说有点困难,因为它不遵循相同的概念。

我找到了一些示例代码 http://blog.pixelingene.com/?p=18它使用 WPF 的装饰器机制,但我仍然不相信这是 WPF 应用程序最简单的方法。可能的实现是围绕工具提示实现装饰器吗?

我的具体情况是一个带有几个文本框的表单,需要输入验证并通知可能的错误输入值 - 这似乎适合气球消息。

我应该注意 WPF 下是否有为此用例构建的商业或开源控件?


我继续为此创建了一个 CodePlex 站点,其中包括“Toast Popups”和控件“Help Balloons”。这些版本具有比下面描述的更多的功能。代码丛项目 https://toastspopuphelpballoon.codeplex.com/.

这是链接到努吉特包 https://www.nuget.org/packages/Mantin.Controls.Wpf.Notification

这是我的气球标题解决方案。我希望它做一些不同的事情:

  • 当鼠标进入时淡入。
  • 鼠标离开时淡出,不透明度达到 0 时关闭窗口。
  • 如果鼠标悬停在窗口上方,不透明度将为 100% 并且不会关闭。
  • 气球窗口的高度是动态的。
  • 使用事件触发器而不是计时器。
  • 将气球放置在控件的左侧或右侧。

这是我使用的帮助图像。

我创建了一个带有简单“帮助”图标的用户控件。

<UserControl x:Class="Foundation.FundRaising.DataRequest.Windows.Controls.HelpBalloon"
         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" 
         mc:Ignorable="d" 
         Name="HelpBalloonControl"
         d:DesignHeight="20" d:DesignWidth="20" Background="Transparent">
    <Image Width="20" Height="20" 
           MouseEnter="ImageMouseEnter" 
           Cursor="Hand"
           IsManipulationEnabled="True" 
           Source="/Foundation.FundRaising.DataRequest.Windows;component/Resources/help20.png" />

并将其添加到后面的代码中。

public partial class HelpBalloon : UserControl
{
    private Balloon balloon = null;

    public HelpBalloon()
    {
        InitializeComponent();
    }

    public string Caption { get; set; }

    public Balloon.Position Position { get; set; }

    private void ImageMouseEnter(object sender, MouseEventArgs e)
    {
        if (balloon == null)
        {
            balloon = new Balloon(this, this.Caption);
            balloon.Closed += BalloonClosed;
            balloon.Show();
        }
    }

    private void BalloonClosed(object sender, EventArgs e)
    {
        this.balloon = null;
    }
}

以下是 UserControl 打开的气球窗口的 XAML 代码。

<Window x:Class="Foundation.FundRaising.DataRequest.Windows.Balloon"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="90" Width="250" WindowStyle="None" 
    ResizeMode="NoResize" ShowInTaskbar="False"
    Topmost="True" IsTabStop="False" 
    OverridesDefaultStyle="False" 
    SizeToContent="Height"
    AllowsTransparency="True" 
    Background="Transparent" >
   <Grid RenderTransformOrigin="0,1" >        
    <StackPanel Orientation="Vertical">
        <StackPanel Orientation="Horizontal">
            <StackPanel.Resources>
                <Style TargetType="Path">
                    <Setter Property="Fill" Value="#fdfdfd"/>
                    <Setter Property="Stretch" Value="Fill"/>
                    <Setter Property="Width" Value="22"/>
                    <Setter Property="Height" Value="31"/>
                    <Setter Property="Panel.ZIndex" Value="99"/>
                    <Setter Property="VerticalAlignment" Value="Top"/>
                    <Setter Property="Effect">
                        <Setter.Value>
                            <DropShadowEffect Color="#FF757575" Opacity=".7"/>
                        </Setter.Value>
                    </Setter>
                </Style>
            </StackPanel.Resources>
            <Path  
              HorizontalAlignment="Left"  
              Margin="15,3,0,0" 
                Data="M10402.99154,55.5381L10.9919,0.64 0.7,54.9"
              x:Name="PathPointLeft"/>
            <Path  
                HorizontalAlignment="Right"  
                Margin="175,3,0,0"
                Data="M10402.992,55.5381 L10284.783,3.2963597 0.7,54.9"
                x:Name="PathPointRight">
            </Path>
        </StackPanel>

        <Border Margin="5,-3,5,5" 
                CornerRadius="7" Panel.ZIndex="100"
                VerticalAlignment="Top">
            <Border.Background>
                <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
                    <LinearGradientBrush.RelativeTransform>
                        <RotateTransform Angle="90" CenterX="0.7" CenterY="0.7" />
                    </LinearGradientBrush.RelativeTransform>
                    <GradientStop Color="#FFFDFDFD" Offset=".2"/>
                    <GradientStop Color="#FFB6FB88" Offset=".8"/>
                </LinearGradientBrush>
            </Border.Background>
            <Border.Effect>
                <DropShadowEffect Color="#FF757575" Opacity=".7"/>
            </Border.Effect>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>

                <Image Grid.Column="0" 
                       Width="35" 
                       Margin="5"
                       VerticalAlignment="Top" Height="35" 
                       Source="Resources/help.png" />

                <TextBlock Grid.Column="1" 
                           TextWrapping="Wrap"
                           Margin="0,10,10,10" 
                           TextOptions.TextFormattingMode="Display"
                           x:Name="textBlockCaption"
                           Text="This is the caption"/>
            </Grid>
        </Border>
    </StackPanel>

    <!-- Animation -->
    <Grid.Triggers>
        <EventTrigger RoutedEvent="FrameworkElement.Loaded">
            <BeginStoryboard x:Name="StoryboardLoad">
                <Storyboard>
                    <DoubleAnimation Storyboard.TargetProperty="(UIElement.Opacity)" From="0.0" To="1.0" Duration="0:0:2" />
                    <DoubleAnimation Storyboard.TargetProperty="(UIElement.Opacity)" From="1.0" To="0.0" Duration="0:0:3" BeginTime="0:0:3" Completed="DoubleAnimationCompleted"/>
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>

        <EventTrigger RoutedEvent="Mouse.MouseEnter">
            <EventTrigger.Actions>
                <RemoveStoryboard BeginStoryboardName="StoryboardLoad"/>
                <RemoveStoryboard BeginStoryboardName="StoryboardFade"/>
            </EventTrigger.Actions>
        </EventTrigger>

        <EventTrigger RoutedEvent="Mouse.MouseLeave">
            <BeginStoryboard x:Name="StoryboardFade">
                <Storyboard>
                    <DoubleAnimation Storyboard.TargetProperty="(UIElement.Opacity)" From="1.0" To="0.0" Duration="0:0:2" BeginTime="0:0:1" Completed="DoubleAnimationCompleted"/>
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </Grid.Triggers>

    <Grid.RenderTransform>
        <ScaleTransform ScaleY="1" />
    </Grid.RenderTransform>
</Grid>

以及气球窗口后面的代码。

public partial class Balloon : Window
{
    public enum Position
    {
        Left,

        Right
    }

    public Balloon(Control control, string caption, Position position)
    {
        InitializeComponent();

        this.textBlockCaption.Text = caption;

        // Compensate for the bubble point
        double captionPointMargin = this.PathPointLeft.Margin.Left;

        Point location = GetControlPosition(control);

        if (position == Position.Left)
        {
            this.PathPointRight.Visibility = Visibility.Hidden;
            this.Left = location.X + (control.ActualWidth / 2) - captionPointMargin;
        }
        else
        {
            this.PathPointLeft.Visibility = Visibility.Hidden;
            this.Left = location.X - this.Width + control.ActualWidth + (captionPointMargin / 2);
        }

        this.Top = location.Y + (control.ActualHeight / 2);
    }

    private static Point GetControlPosition(Control control)
    {
        Point locationToScreen = control.PointToScreen(new Point(0, 0)); 
        var source = PresentationSource.FromVisual(control);
        return source.CompositionTarget.TransformFromDevice.Transform(locationToScreen);
    }

    private void DoubleAnimationCompleted(object sender, EventArgs e)
    {
        if (!this.IsMouseOver)
        {
            this.Close();
        }
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 WPF 应用程序中实现气球消息 的相关文章

  • .NET Framework 中的语言互操作性(基本概念)是什么?

    我正在阅读 NET框架 https en wikipedia org wiki NET Framework维基百科的文章 第一段描述了这个框架的一般概念 它说 它包括一个大型库 并提供跨多种编程语言的语言互操作性 每种语言可以使用用其他语言
  • 使用AOP技术拦截ADO.Net

    我有相当大的代码库 使用各种不同的 ADO 技术 即一些 EF 在某些情况下直接使用 ADO Net 我想知道是否有任何方法可以全局拦截任何 ADO Net 调用 以便我可以开始审核信息 例如执行的确切 SQL 语句 花费的时间 返回的结果
  • 如何使用故事板更改wpf中网格行的高度

    我有一个Grid有 2 行
  • 如何使用样式表删除 QWizard 中的水平线?

    我正在研究一个样式表QWizard我想删除按钮上方的水平线 我已经发布了一个最小的例子here https stackoverflow com q 52538669 8570451 这个问题是由scopchanov从最小的例子中解决的 但是
  • 在 XAML 中设置依赖项属性时,为什么会在运行时绕过 .NET 属性包装器?

    我正在阅读 Adam Nathan 的书 WPF 4 Unleashed 第 82 页有以下警告 在 XAML 中设置依赖属性时 NET 属性包装器会在运行时被绕过 虽然 XAML 编译器依赖于 编译时的属性包装器 WPF 调用底层 直接在
  • 等待运算符错误

    我的代码有问题 我怎么解决这个问题 这个问题出现在await操作符中 public MyModel HttpClient client new HttpClient HttpResponseMessage response await cl
  • 更改实体的可访问性

    我想建立一个内部实体 我已将实体 其标量属性和导航属性更改为内部 当我尝试构建它时出现此错误 错误 6036 EntityType 文件 具有 内部 可访问性 EntitySet 文件 具有具有 公共 可访问性的 get 属性 Entity
  • 从 AuthorizeAttribute 继承的属性不起作用

    我目前正在尝试根据用户角色在新的 ASP MVC 5 应用程序中实现安全性 目标是防止用户在没有特定角色 或更高角色 的情况下访问某些控制器或控制器方法 根据到目前为止我所读到的问题 我创建了一个继承 AuthorizeAttribute
  • 从 RichTextBox 复制文本及其格式

    如何将 RichTextBox 中的文本及其格式复制到写字板或网络浏览器 就像复制纯文本一样 您可以使用Clipboard SetText method http msdn microsoft com en us library 6eahs
  • Swing - 是否可以在 JTable 单元格中设置“特定”文本的字体颜色?

    我有一个 JTable 其中一列按以下格式显示值 423545 50 7568787 50 53654656 2021947 50 021947 2021947 50 8021947 50 我想知道是否可以用红色显示方括号内的值 在过去的几
  • 从 .NET 文本中提取关键字

    我需要计算每个关键字在字符串中重复出现的次数 并按最高次数排序 NET 代码中用于此目的的最快算法是什么 编辑 下面的代码将唯一标记与计数分组 string target src Split new char var results tar
  • .Net 6 控制台应用程序:WebApplication.CreateBuilder 与 Host.CreateDefaultBuilder

    我正在研究 NET 6 并希望构建一个简单的控制台应用程序 并进行一些依赖项注入 据我所知 为了使启动 现在只是程序 文件更具可读性 已经做了很多工作 让我有点困惑的是 所有改进似乎都是针对 API 项目中使用的 WebApplicatio
  • 无法转换类型为“NHibernate.Collection.Generic.PersistentGenericBag”的对象

    public List
  • .NET PInvoke 可以从用户指定的目录动态加载本机 dll 吗?

    我有一个 NET 应用程序 需要加载一个本机库 其位置由用户指定 PInvoke 看起来只会从全局搜索路径 或编译时指定的路径 加载 最好的方法是创建一个在运行时调用 LoadLibrary 的 C CLI 程序集吗 C CLI 会比 C
  • 带图像的简单 GUI [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我试图在简单的 GUI 上显示一些卡
  • .net 中的计时器合并

    Windows 7 引入了计时器合并 提高了能源效率 哪些托管 API 公开了计时器容差 似乎利用此功能的唯一方法是 p invoke设置等待定时器 http msdn microsoft com en us library windows
  • WPF PRISM 事件订阅不会消亡

    我有一个 PRISM RegionManager 其中有几个区域 顶部的功能区区域和其下方用于我的视图的主要内容区域 相当基本 该应用程序以主要内容区域中的 主页 视图开始 当我单击功能区上的按钮时 我将第二个视图注入内容区域并在区域管理器
  • 如何在我的应用程序中使用 Windows Key

    Like Windows Key E Opens a new Explorer Window And Windows Key R Displays the Run command 如何在应用程序的 KeyDown 事件中使用 Windows
  • 线程、进程和 Application.Exit()

    我的应用程序由主消息循环 GUI 和线程 Task Factory 组成 在线程中我调用一些第三方应用程序var p new Process 但是当我调用Application Exit 在消息循环中 我可以看到在线程中启动的进程仍在内存中
  • 将 System.Windows.Forms.Keys 序列转换为 Char

    有没有办法转换由 Keys 枚举表示的击键序列 即System Windows Forms Keys 在一个字符中 例如 Keys Oem4进而Keys A产生 char 它一定存在于 WinAPI 中的某个地方 因为当我在文本框中按下按键

随机推荐

  • 查找 postgres 提供的列表中不存在的值

    我试图找到一个查询来告诉我数据库中没有的值 例如 select seqID segment from flu where seqID IN 1 2 3 4 5 6 7 8 9 现在 如果我的数据库没有 seqID 3 8 9 我将如何查找
  • LINQ:从类型 T 的列表中,仅检索某个子类 S 的对象

    给定一个简单的继承层次结构 人 gt 学生 教师 职员 假设我有一个人员列表 L 该列表中有一些学生 教师和工作人员 使用 LINQ 和 C 有没有办法编写一个只能检索特定类型人员的方法 我知道我可以做类似的事情 var peopleIWa
  • 如何使 Rails 3 资源预编译速度更快?

    我有一个正在运行的 Rails 3 2 1 应用程序 我正在通过 Capistrano 进行部署deploy assets它运行deploy assets precompile task 一切工作都很好 除了编译本身非常慢 我没有那么多 C
  • 发布到 npm 时出现问题

    所以我尝试使用 React 构建一个开源项目并将其推送到 npm 问题是 我的组件虽然在测试环境中运行良好 安装到其他组件 但当我将其发布到 npm 并下载包并尝试访问它时 它给了我一个错误 这是代码的一个小示例 import React
  • 在 C# 中生成随机浮点数的最佳方法[关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 在 C 中生成随机浮点数的最佳方法是什么 更新 我想要从 float Minvalue 到 float Maxvalue 的随机浮点数 我在一些
  • 发布数据时维护 ViewBag 值

    我有一个逻辑问题需要回答 这是一个场景 在控制器中 ViewBag Name aaaa 在视野中 ViewBag Name 在我的控制器中 我为 ViewBag 设置了值 并从 VIew 中的 ViewBag 检索了值 现在在 View 中
  • ASP.NET Core 过滤器内的 ViewBag

    基于这篇文章制作一个全局ViewBag https stackoverflow com questions 23381295 make a global viewbag 我正在尝试在 ASP NET Core 2 2 中实现以下内容 我意识
  • 为什么在 10.5 上我必须在 NSWindowController 上调用 showWindow 两次?

    我有一个 NSWindowController 的子类 我用它从笔尖加载窗口并将其显示在屏幕上 下面是当我想显示窗口时调用的代码 在 10 6 上 当调用 showCustomWindow 时 会显示窗口 但在 10 5 上 必须调用此方法
  • 以编程方式在指令内添加指令

    我想将指令的另一个实例附加到父指令中 但我无法使用 apply 重新编译我的指令 我想我在某个地方错过了一些东西 我的 HTML 代码 div div div div
  • 关闭文件后未保存展开/折叠状态

    在 Visual Studio 中 您可以在不使用区域的情况下展开和折叠代码 例如在代码隐藏页面中您可以折叠方法等 而在 ASPX 页面中您可以折叠标签 标签等 当您的页面很长并且您希望将开发重点放在特定部分时 它非常有用 它的一个很酷的地
  • 如何在 Angular 单元测试中模拟/触发 $routeChangeSuccess?

    给定一个附加到 routeChangeSuccess 事件的处理程序来更新 rootScope 上的 title 属性 rootScope on routeChangeSuccess function event current previ
  • 将密钥对添加到现有 EC2 实例

    我被给予AWS控制台访问正在运行 2 个实例的帐户 但我无法关闭 在生产中 但是 我想获得对这些实例的 SSH 访问权限 是否可以创建一个新的密钥对并将其应用到实例 以便我可以通过 SSH 访问 获取现有的pem当前无法选择创建实例所用的密
  • 在 XSD 中哪里放置版权信息?

    将版权信息放入 XML 架构定义 XSD 时 是否有官方 或半官方 普遍接受的 位置 基于在哪里将版本添加到 XSD 架构 https stackoverflow com questions 2138705 where to add a v
  • setImageCompressionQuality 与 setCompressionQuality 之间有什么区别 - Imagick

    我在Imagick中找到了两种设置图像压缩质量的方法 A 设置图像压缩质量 B 设置压缩质量 所以我想知道哪一个是最好的以及为什么在以下条件下 我读到了setCompressionQuality方法仅适用于新图像 我正在尝试压缩文件 jpe
  • 自动测试无限循环

    我在 Rails 项目中使用自动测试时遇到了一个问题 即当测试失败时 即 我修改了测试文件并且自动测试自动运行测试 自动测试会不断尝试运行测试 当然每次都会失败 因为文件尚未被修改 而不是等到文件再次保存 当咆哮通知打开时 当我试图修复代码
  • 无法在我的程序中使用 matplotlib 函数

    我正在 Windows 10 中运行 Anaconda 安装 conda 版本 4 3 8 这是我尝试在 python 命令行中运行的代码 import matplotlib pyplot as plt x 1 2 3 4 y 5 6 7
  • JAXB - 忽略元素

    有什么方法可以忽略 Jaxb 解析中的元素吗 我有一个很大的 XML 文件 如果我可以忽略其中一个大而复杂的元素 那么它的解析速度可能会快很多 如果它根本无法验证元素内容并解析文档的其余部分 即使该元素不正确 那就更好了 例如 这应该只生成
  • 为什么 C++11 的移动构造函数/赋值运算符没有按预期运行

    include
  • X86 预取优化:“计算 goto”线程代码

    我有一个相当重要的问题 我的计算图有循环和多个 计算路径 我没有制作一个调度程序循环 其中每个顶点将被一一调用 而是将所有预先分配的 框架对象 放置在堆中 代码 数据 这有点类似于线程代码 甚至更好 CPS 只是在堆中跳转 执行代码 每个代
  • 如何在 WPF 应用程序中实现气球消息

    我们想使用气球消息 如UX Guide http msdn microsoft com en us library aa511451 aspx来自微软 我发现一些示例使用 Windows 窗体中的本机代码 但本机代码需要组件的句柄 这对于