using System; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Input; using System.Windows.Interop; using System.Windows.Threading; using Excel = Microsoft.Office.Interop.Excel; namespace excelDalir { public partial class ThisAddIn { private FloatingWindow _floatWindow; private Excel.Application _app; private DispatcherTimer _focusTimer; private const int WM_KEYDOWN = 0x0100; private void ThisAddIn_Startup(object sender, EventArgs e) { _app = this.Application; _floatWindow = new FloatingWindow(); _floatWindow.TextAccepted += (s, text) => PushToExcel(text); _focusTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(50) }; _focusTimer.Tick += FocusTimer_Tick; _app.SheetSelectionChange += App_SheetSelectionChange; // Hook کیبورد System.Windows.Interop.ComponentDispatcher.ThreadPreprocessMessage += ThreadPreprocessMessageMethod; } private void App_SheetSelectionChange(object Sh, Excel.Range Target) { try { Excel.Window window = _app.ActiveWindow; if (window == null) return; dynamic pane = window.ActivePane; int x1 = pane.PointsToScreenPixelsX((int)Target.Left); int y1 = pane.PointsToScreenPixelsY((int)Target.Top); int w = pane.PointsToScreenPixelsX((int)(Target.Left + Target.Width)) - x1; int h = pane.PointsToScreenPixelsY((int)(Target.Top + Target.Height)) - y1; w = Math.Max(w, 50); h = Math.Max(h, 25); _floatWindow.Left = x1; _floatWindow.Top = y1; _floatWindow.Width = w; _floatWindow.Height = h; string cellText = Convert.ToString(Target.Value2) ?? ""; _floatWindow.Setup(cellText, (double)Target.Font.Size, Target.Font.Name.ToString()); if (_floatWindow.Visibility != Visibility.Visible) _floatWindow.Show(); _focusTimer.Stop(); _focusTimer.Start(); } catch { } } private void FocusTimer_Tick(object sender, EventArgs e) { _focusTimer.Stop(); if (_floatWindow.Visibility == Visibility.Visible) _floatWindow.ForceFocus(); } private void PushToExcel(string text) { try { if (_app.ActiveCell != null) { _app.EnableEvents = false; _app.ActiveCell.Value2 = text; } } catch { } finally { _app.EnableEvents = true; } } private void ThisAddIn_Shutdown(object sender, EventArgs e) { _floatWindow?.Close(); } private void InternalStartup() { this.Startup += new EventHandler(ThisAddIn_Startup); this.Shutdown += new EventHandler(ThisAddIn_Shutdown); } private const int WM_CHAR = 0x0102; private void ThreadPreprocessMessageMethod(ref MSG msg, ref bool handled) { if (_floatWindow == null || _floatWindow.Visibility != Visibility.Visible) return; // Handle printable character input if (msg.message == WM_CHAR) { char ch = (char)msg.wParam; _floatWindow.Dispatcher.BeginInvoke(new Action(() => { var tb = _floatWindow.EditorBox; if (tb == null || !tb.IsVisible) return; if (!tb.IsKeyboardFocused) tb.Focus(); // Insert character at caret (یا جایگزینی متن انتخاب شده) int selStart = tb.SelectionStart; int selLength = tb.SelectionLength; string before = tb.Text.Substring(0, selStart); string after = tb.Text.Substring(selStart + selLength); tb.Text = before + ch + after; tb.SelectionStart = selStart + 1; tb.SelectionLength = 0; })); handled = true; return; } // Handle control keys (Backspace, Enter, Escape, arrows) if (msg.message == WM_KEYDOWN) { int vk = (int)msg.wParam; // Allow system combos (Alt+Tab etc.) if ((System.Windows.Input.Keyboard.Modifiers & System.Windows.Input.ModifierKeys.Alt) != 0) return; var key = System.Windows.Input.KeyInterop.KeyFromVirtualKey(vk); if (key == System.Windows.Input.Key.Back || key == System.Windows.Input.Key.Enter || key == System.Windows.Input.Key.Escape || key == System.Windows.Input.Key.Left || key == System.Windows.Input.Key.Right || key == System.Windows.Input.Key.Up || key == System.Windows.Input.Key.Down) { _floatWindow.Dispatcher.BeginInvoke(new Action(() => { var tb = _floatWindow.EditorBox; if (tb == null || !tb.IsVisible) return; if (!tb.IsKeyboardFocused) tb.Focus(); switch (key) { case System.Windows.Input.Key.Back: if (tb.SelectionLength > 0) { int s = tb.SelectionStart; tb.Text = tb.Text.Remove(s, tb.SelectionLength); tb.SelectionStart = s; } else if (tb.SelectionStart > 0) { int pos = tb.SelectionStart - 1; tb.Text = tb.Text.Remove(pos, 1); tb.SelectionStart = pos; } break; case System.Windows.Input.Key.Left: tb.SelectionStart = Math.Max(0, tb.SelectionStart - 1); tb.SelectionLength = 0; break; case System.Windows.Input.Key.Right: tb.SelectionStart = Math.Min(tb.Text.Length, tb.SelectionStart + 1); tb.SelectionLength = 0; break; case System.Windows.Input.Key.Enter: // اگر می‌خواهی Enter سلول را ثبت کند، می‌توانی PushToExcel یا رفتار دلخواه را اینجا فراخوانی کنی PushToExcel(tb.Text); break; case System.Windows.Input.Key.Escape: // اگر می‌خواهی پنجره را ببندی یا مقدار را نپذیری، اینجا قرار ده break; // سایر کلیدها را بنا به نیاز پیاده کن } })); handled = true; } } } } } _________________________________________________________ using System; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Input; using System.Windows.Interop; namespace excelDalir { public partial class FloatingWindow : Window { public event EventHandler TextAccepted; [DllImport("user32.dll")] private static extern bool SetForegroundWindow(IntPtr hWnd); public FloatingWindow() { InitializeComponent(); } public void Setup(string text, double fontSize, string fontName) { EditorBox.Text = text ?? ""; EditorBox.FontSize = fontSize > 0 ? fontSize : 11; if (!string.IsNullOrEmpty(fontName)) EditorBox.FontFamily = new System.Windows.Media.FontFamily(fontName); EditorBox.SelectAll(); } public void ForceFocus() { this.Activate(); EditorBox.Focus(); Keyboard.Focus(EditorBox); // بیاور جلو var helper = new WindowInteropHelper(this); SetForegroundWindow(helper.Handle); } private void EditorBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e) { // اگر لازم دارید متن را realtime پردازش کنید // در غیر این صورت می‌توانید خالی بگذارید } private void EditorBox_KeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Enter) { TextAccepted?.Invoke(this, EditorBox.Text); e.Handled = true; this.Hide(); } else if (e.Key == Key.Escape) { this.Hide(); e.Handled = true; } } } }