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