using System;
using System.IO;
using System.Windows;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.Win32;
using System.Diagnostics;
using Application = System.Windows.Application;
using MessageBox = System.Windows.MessageBox;
namespace VoiceAssistantBall
{
public partial class App : Application
{
private NotifyIcon _notifyIcon;
private bool _isExiting = false;
private ToolStripMenuItem _startupMenuItem;
private void Application_Startup(object sender, StartupEventArgs e)
{
CreateTrayIcon();
UpdateStartupMenuItemState();
}
private void CreateTrayIcon()
{
try
{
_notifyIcon = new NotifyIcon
{
Icon = GetTrayIcon(),
Text = "语音助手 - 双击显示/隐藏,右键菜单",
Visible = true
};
_notifyIcon.DoubleClick += (sender, e) =>
{
ToggleMainWindowVisibility();
};
CreateTrayContextMenu();
}
catch (Exception ex)
{
MessageBox.Show($"创建系统托盘失败: {ex.Message}", "错误",
MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
private Icon GetTrayIcon()
{
try
{
string appPath = System.Reflection.Assembly.GetEntryAssembly().Location;
string iconPath = Path.Combine(Path.GetDirectoryName(appPath), "Resources", "app.ico");
if (File.Exists(iconPath))
{
return new Icon(iconPath);
}
using (Bitmap bmp = new Bitmap(16, 16))
{
using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bmp))
{
g.Clear(System.Drawing.Color.Transparent);
g.FillEllipse(System.Drawing.Brushes.DodgerBlue, 0, 0, 15, 15);
g.DrawEllipse(new System.Drawing.Pen(System.Drawing.Color.White, 2), 0, 0, 15, 15);
}
return Icon.FromHandle(bmp.GetHicon());
}
}
catch
{
return System.Drawing.SystemIcons.Application;
}
}
private void CreateTrayContextMenu()
{
if (_notifyIcon == null) return;
ContextMenuStrip contextMenu = new ContextMenuStrip();
ToolStripMenuItem showHideItem = new ToolStripMenuItem("显示/隐藏主窗口");
showHideItem.Click += (sender, e) => ToggleMainWindowVisibility();
contextMenu.Items.Add(showHideItem);
contextMenu.Items.Add(new ToolStripSeparator());
_startupMenuItem = new ToolStripMenuItem("开机自启");
_startupMenuItem.CheckOnClick = true;
_startupMenuItem.Click += (sender, e) => ToggleStartup();
contextMenu.Items.Add(_startupMenuItem);
contextMenu.Items.Add(new ToolStripSeparator());
ToolStripMenuItem exitItem = new ToolStripMenuItem("退出");
exitItem.Click += (sender, e) => ShutdownApplication();
contextMenu.Items.Add(exitItem);
_notifyIcon.ContextMenuStrip = contextMenu;
}
private void ToggleMainWindowVisibility()
{
MainWindow mainWindow = Application.Current.MainWindow as MainWindow;
if (mainWindow == null) return;
if (mainWindow.Visibility == Visibility.Visible)
{
mainWindow.Hide();
ShowNotification("语音助手已隐藏到托盘", "提示", ToolTipIcon.Info);
}
else
{
mainWindow.Show();
mainWindow.WindowState = WindowState.Normal;
mainWindow.Activate();
}
}
private void ToggleStartup()
{
try
{
if (_startupMenuItem == null) return;
bool currentState = IsStartupEnabled();
bool newState = !currentState;
SetStartup(newState);
_startupMenuItem.Checked = newState;
string message = newState ? "已启用开机自启" : "已禁用开机自启";
ShowNotification(message, "设置", ToolTipIcon.Info);
}
catch (Exception ex)
{
MessageBox.Show($"设置开机自启失败: {ex.Message}\n\n请尝试以管理员权限运行程序。",
"错误", MessageBoxButton.OK, MessageBoxImage.Error);
if (_startupMenuItem != null)
{
_startupMenuItem.Checked = IsStartupEnabled();
}
}
}
private bool IsStartupEnabled()
{
try
{
string appName = "VoiceAssistantBall";
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", false))
{
if (key == null) return false;
return key.GetValue(appName) != null;
}
}
catch (Exception ex)
{
Debug.WriteLine($"检查开机自启状态失败: {ex.Message}");
return false;
}
}
private void SetStartup(bool enable)
{
try
{
string appName = "VoiceAssistantBall";
string appPath = Process.GetCurrentProcess().MainModule.FileName;
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true))
{
if (key == null)
{
throw new Exception("无法访问注册表,请以管理员权限运行");
}
if (enable)
{
key.SetValue(appName, "\"" + appPath + "\"");
}
else
{
key.DeleteValue(appName, false);
}
}
}
catch (UnauthorizedAccessException)
{
throw new Exception("权限不足,请以管理员身份运行程序");
}
catch (Exception ex)
{
throw new Exception($"注册表操作失败: {ex.Message}");
}
}
private void UpdateStartupMenuItemState()
{
if (_startupMenuItem != null)
{
bool isEnabled = IsStartupEnabled();
_startupMenuItem.Checked = isEnabled;
}
}
public void OnMainWindowClosing(object sender, System.ComponentModel.CancelEventArgs e)
{
if (!_isExiting && sender is Window window)
{
e.Cancel = true;
window.Hide();
if (_notifyIcon != null)
{
_notifyIcon.ShowBalloonTip(1000, "语音助手", "已最小化到系统托盘", ToolTipIcon.Info);
}
}
}
private void ShowNotification(string message, string title, ToolTipIcon icon)
{
if (_notifyIcon != null)
{
_notifyIcon.ShowBalloonTip(3000, title, message, icon);
}
}
private void ShutdownApplication()
{
_isExiting = true;
if (_notifyIcon != null)
{
_notifyIcon.Visible = false;
_notifyIcon.Dispose();
}
Application.Current.Shutdown();
}
private void Application_Exit(object sender, ExitEventArgs e)
{
if (_notifyIcon != null)
{
_notifyIcon.Visible = false;
_notifyIcon.Dispose();
}
}
}
}
<Window x:Class="VoiceAssistantBall.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="VoiceAssistantBall"
Height="60"
Width="60"
WindowStyle="None"
AllowsTransparency="True"
Background="Transparent"
Topmost="True"
ShowInTaskbar="False"
Top="100"
Left="100"
MouseDown="Window_MouseDown"
MouseMove="Window_MouseMove"
MouseUp="Window_MouseUp"
MouseDoubleClick="Window_MouseDoubleClick"
Closing="Window_Closing">
<Window.Resources>
<Storyboard x:Key="ListeningAnimation" RepeatBehavior="Forever">
<DoubleAnimation
Storyboard.TargetName="ListeningRing"
Storyboard.TargetProperty="Opacity"
From="0.8" To="0" Duration="0:0:1"
AutoReverse="True"/>
<DoubleAnimation
Storyboard.TargetName="ListeningRing"
Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)"
From="1" To="1.5" Duration="0:0:1"
AutoReverse="True"/>
<DoubleAnimation
Storyboard.TargetName="ListeningRing"
Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)"
From="1" To="1.5" Duration="0:0:1"
AutoReverse="True"/>
</Storyboard>
</Window.Resources>
<Grid>
<Ellipse Name="MainEllipse"
Width="50"
Height="50"
Stroke="#FF0066CC"
StrokeThickness="2"
ToolTip="语音助手悬浮球 - 拖动可移动,双击开始监听"
RenderTransformOrigin="0.5,0.5">
<Ellipse.RenderTransform>
<ScaleTransform ScaleX="1" ScaleY="1"/>
</Ellipse.RenderTransform>
<Ellipse.Fill>
<RadialGradientBrush GradientOrigin="0.3,0.3" Center="0.5,0.5" RadiusX="0.5" RadiusY="0.5">
<GradientStop Color="#FF66B3FF" Offset="0"/>
<GradientStop Color="#FF0066CC" Offset="1"/>
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Viewbox Width="24" Height="24" VerticalAlignment="Center" HorizontalAlignment="Center">
<Canvas>
<Path Data="M12,2A3,3 0 0,1 15,5V11A3,3 0 0,1 12,14A3,3 0 0,1 9,11V5A3,3 0 0,1 12,2M19,11C19,14.53 16.39,17.44 13,17.93V21H11V17.93C7.61,17.44 5,14.53 5,11H7A5,5 0 0,0 12,16A5,5 0 0,0 17,11H19Z"
Fill="White"
Stretch="Uniform"/>
</Canvas>
</Viewbox>
<Ellipse Name="ListeningRing"
Width="60"
Height="60"
Stroke="#FFFF6B6B"
StrokeThickness="3"
Opacity="0"
Visibility="Collapsed"
RenderTransformOrigin="0.5,0.5">
<Ellipse.RenderTransform>
<ScaleTransform ScaleX="1" ScaleY="1"/>
</Ellipse.RenderTransform>
</Ellipse>
<TextBlock Name="StatusText"
Text="就绪"
Foreground="White"
FontSize="9"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
Margin="0,0,0,2"
Visibility="Hidden"/>
</Grid>
</Window>
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Runtime.InteropServices;
using System.Windows.Interop;
using System.Diagnostics;
using System.Windows.Threading;
namespace VoiceAssistantBall
{
public partial class MainWindow : Window
{
private const int WM_NCLBUTTONDOWN = 0xA1;
private const int HT_CAPTION = 0x2;
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);
[DllImport("user32.dll")]
private static extern bool ReleaseCapture();
private Point _dragStartPoint;
private bool _isDragging = false;
private bool _isListening = false;
private DateTime _lastClickTime = DateTime.MinValue;
private const int DoubleClickThreshold = 300;
public MainWindow()
{
InitializeComponent();
Loaded += (s, e) => InitializeWindow();
}
private void InitializeWindow()
{
Topmost = true;
UpdateStatus("就绪");
}
private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
{
TimeSpan timeSinceLastClick = DateTime.Now - _lastClickTime;
if (timeSinceLastClick.TotalMilliseconds < DoubleClickThreshold)
{
HandleDoubleClick();
_lastClickTime = DateTime.MinValue;
return;
}
_lastClickTime = DateTime.Now;
_dragStartPoint = e.GetPosition(this);
_isDragging = true;
CaptureMouse();
ReleaseCapture();
SendMessage(new WindowInteropHelper(this).Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
}
else if (e.ChangedButton == MouseButton.Right)
{
ShowRightClickMenu(e.GetPosition(this));
}
else if (e.ChangedButton == MouseButton.Middle)
{
TestOpenNotepad();
}
}
private void Window_MouseMove(object sender, MouseEventArgs e)
{
if (_isDragging && e.LeftButton == MouseButtonState.Pressed)
{
Point currentPosition = e.GetPosition(this);
Vector offset = currentPosition - _dragStartPoint;
Left += offset.X;
Top += offset.Y;
}
}
private void Window_MouseUp(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left && _isDragging)
{
_isDragging = false;
ReleaseMouseCapture();
UpdateStatus("位置已更新");
ClearStatusAfterDelay(2000);
}
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
if (Application.Current is App app)
{
app.OnMainWindowClosing(this, e);
}
}
private void UpdateStatus(string status)
{
Dispatcher.Invoke(() =>
{
StatusText.Text = status;
StatusText.Visibility = string.IsNullOrEmpty(status) ?
Visibility.Hidden : Visibility.Visible;
});
}
private void ClearStatusAfterDelay(int milliseconds)
{
DispatcherTimer timer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(milliseconds)
};
timer.Tick += (s, e) =>
{
UpdateStatus("");
timer.Stop();
};
timer.Start();
}
private void ShowRightClickMenu(Point position)
{
UpdateStatus("右键点击");
ClearStatusAfterDelay(1500);
}
private void TestOpenNotepad()
{
try
{
Process.Start("notepad.exe");
UpdateStatus("已打开记事本");
ClearStatusAfterDelay(2000);
ChangeBallColor(Colors.LightGreen);
DispatcherTimer timer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(1)
};
timer.Tick += (s, e) =>
{
ResetBallColor();
timer.Stop();
};
timer.Start();
}
catch (Exception ex)
{
UpdateStatus($"错误: {ex.Message}");
ClearStatusAfterDelay(3000);
}
}
private void ChangeBallColor(Color color)
{
Dispatcher.Invoke(() =>
{
RadialGradientBrush brush = new RadialGradientBrush
{
GradientOrigin = new Point(0.3, 0.3),
Center = new Point(0.5, 0.5),
RadiusX = 0.5,
RadiusY = 0.5
};
brush.GradientStops.Add(new GradientStop(color, 0));
brush.GradientStops.Add(new GradientStop(
Color.FromArgb(color.A,
(byte)(color.R * 0.7),
(byte)(color.G * 0.7),
(byte)(color.B * 0.7)), 1));
MainEllipse.Fill = brush;
});
}
private void ResetBallColor()
{
Dispatcher.Invoke(() =>
{
RadialGradientBrush brush = new RadialGradientBrush
{
GradientOrigin = new Point(0.3, 0.3),
Center = new Point(0.5, 0.5),
RadiusX = 0.5,
RadiusY = 0.5
};
brush.GradientStops.Add(new GradientStop(Color.FromRgb(102, 179, 255), 0));
brush.GradientStops.Add(new GradientStop(Color.FromRgb(0, 102, 204), 1));
MainEllipse.Fill = brush;
});
}
private void Window_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
{
HandleDoubleClick();
}
}
private void HandleDoubleClick()
{
if (!_isListening)
{
StartListening();
}
else
{
StopListening();
}
}
private void StartListening()
{
_isListening = true;
UpdateStatus("监听中...");
StartListeningAnimation();
ChangeBallColor(Colors.Orange);
}
private void StopListening()
{
_isListening = false;
UpdateStatus("处理中...");
StopListeningAnimation();
ResetBallColor();
DispatcherTimer timer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(2)
};
timer.Tick += (s, e) =>
{
UpdateStatus("就绪");
timer.Stop();
};
timer.Start();
}
private void StartListeningAnimation()
{
Dispatcher.Invoke(() =>
{
ListeningRing.Visibility = Visibility.Visible;
Storyboard listeningAnimation = FindResource("ListeningAnimation") as Storyboard;
if (listeningAnimation != null)
{
listeningAnimation.Begin(ListeningRing, true);
}
});
}
private void StopListeningAnimation()
{
Dispatcher.Invoke(() =>
{
Storyboard listeningAnimation = FindResource("ListeningAnimation") as Storyboard;
if (listeningAnimation != null)
{
listeningAnimation.Stop(ListeningRing);
}
ListeningRing.Visibility = Visibility.Collapsed;
ListeningRing.Opacity = 0;
ListeningRing.RenderTransform = new ScaleTransform(1, 1);
});
}
}
}