助手

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);
            });
        }
    }
}

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部