Нужно в VS 2010 перехватить клавиатурный ввод мышиные сообщения.
Раньше (в VS 2008) делался сабкласинг окна редактора и перехватывались сообщения вроде WM_KEYDOWN и WM_LBUTTONDOWN. Но в VS 2010 в подменную оконную процедуру попросту не приходит этих сообщений.
Более детальное описание задачи...
Реализуется всплывающая подсказка (Hint). Ее отличие от стандартной заключается, в основном, в том что подсказки могут быть иерархическими.
Подсказка оформлена отдельным всплывающим окном. Нужно закрывать это окно (и все окна для которых оно является владельцем) если пользователь начал клавиатурный ввод (например, сдвинул курсор стрклками) или нажал мышью в редкторе. При этом не хочется завязываться на редактор и т.п. Хочется чтобы контрол (хинт) сам перехватывал сообщения и закрывался.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: [WPF, VS 2010] Глобальный перехват сообщений мыши и клав
Здравствуйте, VladD2, Вы писали:
VD>Реализуется всплывающая подсказка (Hint). Ее отличие от стандартной заключается, в основном, в том что подсказки могут быть иерархическими. VD>Подсказка оформлена отдельным всплывающим окном. Нужно закрывать это окно (и все окна для которых оно является владельцем) если пользователь начал клавиатурный ввод (например, сдвинул курсор стрклками) или нажал мышью в редкторе. При этом не хочется завязываться на редактор и т.п. Хочется чтобы контрол (хинт) сам перехватывал сообщения и закрывался.
Я не в курсе архитектуры VS, но если все это крутится в WPF окружении, для перехвата событий ввода можно использовать события от InputManager.
Re: [WPF, VS 2010] Глобальный перехват сообщений мыши и клав
Здравствуйте, VladD2, Вы писали: VD>…Хочется чтобы контрол (хинт) сам перехватывал сообщения и закрывался.
Сколько я такого добра не понаделал, при скурпулёзном и дотошном тестировании так или иначе удавалось добиться того, что бы такой попап не закрывался бы когда нужно. Тем более когда в попап ещё и хочется добавить какого-то инпута/интерактива. Порыв сделать так очень хочется назвать "пионерским".
С другой стороны способ, когда попап закрывается кем-то "повыше", получался действительно железобетонным. Программировать такое конечно не так удобно, зато смешных ситуаций, когда попап вдруг ведёт себя не так, как ожидается, не возникает.
P.S.
Уже за одно "Topmost = true;" хочется дать по рукам Ничто так не раздражает, как окна, которые вдруг в случае чего оказываются поверх всех. Например тултип с путём к файлу солюшена на стартовой странице MSVS 2010 не убирается при открытии большого солюшена (смотри под кат) — тоже попап с топмостом, будь не ладен тот разработчик.
передовики технологий удружили. Это конечно не попап, но с топмостом надо быть очень и очень осторожным, ибо мешающие таким образом окошки раздражают сильно. Я не говорю уже про чайников, которые делают с помощью топмоста сплешскрин. Для них, надеюсь, заготовлен проклятиями "благодарных" пользователей специальный котёл в местах не столь отдалённых
Help will always be given at Hogwarts to those who ask for it.
Re[2]: [WPF, VS 2010] Глобальный перехват сообщений мыши и к
Здравствуйте, MxMsk, Вы писали:
MM>Я не в курсе архитектуры VS, но если все это крутится в WPF окружении, для перехвата событий ввода можно использовать события от InputManager.
И как с его помощью можно перехватить клавиатурные сообщения?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: [WPF, VS 2010] Глобальный перехват сообщений мыши и к
Здравствуйте, _FRED_, Вы писали:
_FR>Сколько я такого добра не понаделал, при скурпулёзном и дотошном тестировании так или иначе удавалось добиться того, что бы такой попап не закрывался бы когда нужно. Тем более когда в попап ещё и хочется добавить какого-то инпута/интерактива. Порыв сделать так очень хочется назвать "пионерским".
Редактирование нам не нужно. Только переходы по нажатию мыши (url-ы).
Собственно в VS 2008 у нас все работает как надо. Хинт в любом случае закрывается по таймеру. Тупо анализируется где сейчас мышь и если не в определенных областях (над одним из хинтов или в области текста для которого и показывается хинт), то хинт закрывается. Так что это не проблема.
Проблема только в перехвате сообщений нажатия кнопок мыши и клавиатуры для окна студии (ну или редактора).
_FR>С другой стороны способ, когда попап закрывается кем-то "повыше", получался действительно железобетонным.
Что значит повыше? Пониже, наверно. Это криво. К тому же COM API студии настолько кривой, что получить внятное событие нажатия мыши и клавы не так то просто.
_FR>Программировать такое конечно не так удобно, зато смешных ситуаций, когда попап вдруг ведёт себя не так, как ожидается, не возникает.
Я не люблю кривые решения. Или уж нужно пользоваться API студии (новым), но с этим огромные проблемы (во-первых, до нового API я пока что даже не смог добраться из COM-оберток, а во-вторых, API студии рассчитан на отображение текстовых хинтов, а у нас более продвинутый случай который требует передачи структурированных данных).
_FR> _FR>P.S...
Прошу прощения, но мне нужна конкретная помощь, а не беллетристика.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: [WPF, VS 2010] Глобальный перехват сообщений мыши и к
Здравствуйте, MxMsk, Вы писали:
MM>Я не в курсе архитектуры VS, но если все это крутится в WPF окружении, для перехвата событий ввода можно использовать события от InputManager.
Взято отсюда, должно работать под .Net 4 тоже, не проверял.
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using swf = System.Windows.Forms;
using System.Windows.Interop;
namespace WpfMnemonicsTest
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
ComponentDispatcher.ThreadPreprocessMessage += new ThreadMessageEventHandler(ComponentDispatcher_ThreadPreprocessMessage);
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
//Setup some content in the FlowLayoutPanels in the two WindowsFormsHost controls
//1st panel content
swf.Button button1 = new swf.Button();
button1.Text = "Button&1";
this.WinFormPanel.Controls.Add(button1);
swf.Button button1a = new swf.Button();
button1a.Text = "Button&1a";
this.WinFormPanel.Controls.Add(button1a);
//2nd panel content
swf.Button button2 = new swf.Button();
button2.Text = "Button2&1";
this.WinFormPanel2.Controls.Add(button2);
}
bool _suppressKeyboardGotFocus;
void ComponentDispatcher_ThreadPreprocessMessage(ref MSG msg, ref bool handled)
{
const int WM_KEYDOWN = 0x100;
const int WM_SYSKEYDOWN = 0x104;
const int VK_MENU = 0x12; //VK_MENU is the virtual Alt key (independent of left/right Alt key)
//Intercept all mnemonics handling. Try to predict where keyboard focus would go, had the
//TAB key been pressed. if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN)
{
swf.Keys keyData = ((swf.Keys)((int)((long)msg.wParam))) | swf.Control.ModifierKeys;
if ((keyData & swf.Keys.Alt) > 0 && msg.wParam.ToInt32() != VK_MENU) //mnemonic activated
{
handled = true; //stop mnemonic default handling
_suppressKeyboardGotFocus = true; //we don't want to actually navigate
UIElement focusedElement = Keyboard.FocusedElement as UIElement; //find currently focused element if (focusedElement != null)
focusedElement.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next)); //attempt to move focus to next element
}
}
}
protected override void OnPreviewGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
base.OnPreviewGotKeyboardFocus(e);
if (_suppressKeyboardGotFocus)
{
e.Handled = true; //supress movement
System.Diagnostics.Trace.WriteLine("New element would be: " + e.NewFocus.ToString()); //display preferred new focus element
_suppressKeyboardGotFocus = false;
}
}
}
}
Re[2]: [WPF, VS 2010] Глобальный перехват сообщений мыши и к
Здравствуйте, MxMsk, Вы писали:
MM>Я не в курсе архитектуры VS, но если все это крутится в WPF окружении, для перехвата событий ввода можно использовать события от InputManager.
В итоге, решил проблему следующим образом. С помощью InputManager.Current перехватил события PreProcessInput и EnterMenuMode и в их обработчиках закрываю хинт.
.
Во-первых не ясно было как добывать информацию о сообщениях. Решил проблему получив имя сообщения через e.StagingItem.Input.RoutedEvent.Name (т.е. в строковом виде). Возможно есть более удобные и/или правильные пути. По этому поводу приветствуются предложения.
Во-вторых, в этих сообщениях приходится вручную разбираться находится ли курсор мыши над окнами хинта или нет. Решил проблему тупой проверкой не находится ли курсор над окном (путем перебора).
Попутно не понял как в WPF узнать текущую позицию курсора мыши. В RoutedEvent этой информацию не обнаружилось. Пришлось пользоваться WinAPI-функцией GetCursorPos().
А так же то, что не один я обращаю внимание на подобные косяки.
Это косяки алгоритма используемого в ДжетБрэйнс. Как я уже сказал, мы используем таймер, так что если окно переключится, хинт по любому пропадет через пару секунд. Плюс заложена железная возможность закрыть хинт принудительно, нажав на нем правую кнопку мыши).
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: [WPF, VS 2010] Глобальный перехват сообщений мыши и к
А так же то, что не один я обращаю внимание на подобные косяки.
VD>Это косяки алгоритма используемого в ДжетБрэйнс. Как я уже сказал, мы используем таймер, так что если окно переключится, хинт по любому пропадет через пару секунд. Плюс заложена железная возможность закрыть хинт принудительно, нажав на нем правую кнопку мыши).
Да, уже попробовал. Ничего так, очень даже ничего с таймером. Правда нажатия по non-client всё-таки не обрабатыватся и при тягании окошка студии хинт пропадает по таймеру. Но пропадает.
Варианты во второй версии не хотите переделать, что бы в них можно было бы свойсва использовать вместо полей? Ну и ещё некоторые вещи в них кажутся странными.
Help will always be given at Hogwarts to those who ask for it.
Re[3]: [WPF, VS 2010] Глобальный перехват сообщений мыши и к
. VD>Во-первых не ясно было как добывать информацию о сообщениях. Решил проблему получив имя сообщения через e.StagingItem.Input.RoutedEvent.Name (т.е. в строковом виде). Возможно есть более удобные и/или правильные пути. По этому поводу приветствуются предложения.
Примерно так и придется. Только сверять не имя, а идентификатор события. Они доступны в виде статический полей, аналогично DependencyProperty. Правда не всегда ясно, где их искать, но можно воспользоватся Object Browser-ом. Имя поля образуется сочетанием <имя_события>Event. Например для события PreviewKeyDown можно проверить так:
if (inputArgs.StagingItem.Input.RoutedEvent == Keyboard.PreviewKeyDownEvent)
{
return keyPredicate((KeyEventArgs)inputArgs.StagingItem.Input);
}
VD>Во-вторых, в этих сообщениях приходится вручную разбираться находится ли курсор мыши над окнами хинта или нет. Решил проблему тупой проверкой не находится ли курсор над окном (путем перебора). VD>Попутно не понял как в WPF узнать текущую позицию курсора мыши. В RoutedEvent этой информацию не обнаружилось. Пришлось пользоваться WinAPI-функцией GetCursorPos().
Если речь идет сугубо о "мышинных" событиях, то надо привести inputArgs.StagingItem.Input к MouseEventArgs и воспользоваться MouseEventArgs.GetPosition. Для проверки попадания есть VisualTreeHelper.HitTest.
Re[4]: [WPF, VS 2010] Глобальный перехват сообщений мыши и к
Здравствуйте, MxMsk, Вы писали:
MM>Примерно так и придется. Только сверять не имя, а идентификатор события. Они доступны в виде статический полей, аналогично DependencyProperty. Правда не всегда ясно, где их искать, но можно воспользоватся Object Browser-ом. Имя поля образуется сочетанием <имя_события>Event. Например для события PreviewKeyDown можно проверить так: MM>
Строки могут измениться? Если нет, то проще оставить строки. А то с объектами в C# (а хинт, к сожалению на нем) switch не поиспользуешь и придется городить кучу if-ов.
MM>Если речь идет сугубо о "мышинных" событиях, то надо привести inputArgs.StagingItem.Input к MouseEventArgs и воспользоваться MouseEventArgs.GetPosition.
Если не ошибаюсь, там 0;0 получается. Вообще как-то с координатами мутно все.
MM>Для проверки попадания есть VisualTreeHelper.HitTest.
В принципе текущий код работате, но надо попробовавший этот вариант. А то может я где-то накосячил с мониторами или еще что...
ЗЫ
Собственно, могу дать права на SVN-репозиторий (если их еще нет). Попробуй сам по колдовать. Для раздачи прав нужен гуглевый экаунт (или просто гуламыло).
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: [WPF, VS 2010] Глобальный перехват сообщений мыши и к
Здравствуйте, _FRED_, Вы писали:
_FR>Да, уже попробовал. Ничего так, очень даже ничего с таймером. Правда нажатия по non-client всё-таки не обрабатыватся и при тягании окошка студии хинт пропадает по таймеру. Но пропадает.
Это ж каким шустрым нужно быть, чтобы успеть хватануть за рамку окна за секунду .
Можно конечно перехватить много разной фигни, но не хочется. Сейчас хинт живет без хаков на урове WinAPI. А перехват
_FR>Варианты во второй версии не хотите переделать, что бы в них можно было бы свойсва использовать вместо полей? Ну и ещё некоторые вещи в них кажутся странными.
А зачем для этого что-то делать в языке? Это и так можно делать.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: [WPF, VS 2010] Глобальный перехват сообщений мыши и к
Здравствуйте, VladD2, Вы писали:
_FR>>Да, уже попробовал. Ничего так, очень даже ничего с таймером. Правда нажатия по non-client всё-таки не обрабатыватся и при тягании окошка студии хинт пропадает по таймеру. Но пропадает. VD>Это ж каким шустрым нужно быть, чтобы успеть хватануть за рамку окна за секунду .
Ещё довольно легко, уже без очень быстрых движений, удаётся добиться одновременного появления на экране двух хинтов.
Кстати, хинты студийные, когда мышь не непосредственно на хинте, делаются прозрачными. Очень удобно. Так же было бы здорово иметь возможность в хинте выделить весь или только часть текста.
Картинки в хинтах очень сильно нужны?
Help will always be given at Hogwarts to those who ask for it.
Re[8]: [WPF, VS 2010] Глобальный перехват сообщений мыши и к
Здравствуйте, _FRED_, Вы писали:
_FR>Ещё довольно легко, уже без очень быстрых движений, удаётся добиться одновременного появления на экране двух хинтов.
Да, ну? Как?
Ты уверен, что это не вложенный хинт? Там при наведении мыши на описания типов в хинте вылетает вложенный хинт описывающий этот тип. А при открытии хинта для длругого места первый автоматически закрывается. Так что штатно получить два хинта нельзя.
_FR>Кстати, хинты студийные, когда мышь не непосредственно на хинте, делаются прозрачными. Очень удобно.
Не вижу в этом ничего удобного. Читаемость от этого ухудшается, а разглядеть, что под ними все равно нельзя. Так что лучше их уж тогда закрывать.
Да и не замечал чтобы в студии хинты прозрачными становились.
_FR>Так же было бы здорово иметь возможность в хинте выделить весь или только часть текста.
Это уже не хинт, а редактор какой-то. Можно (и нужно) сделать копирование содержимого хинта. А уж если нужно, то можно и в каком-нибудь редакторе вставить текст и взять нужное.
_FR>Картинки в хинтах очень сильно нужны?
А они там есть? Насколько я помню — нет.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[9]: [WPF, VS 2010] Глобальный перехват сообщений мыши и к
Здравствуйте, VladD2, Вы писали:
_FR>>Ещё довольно легко, уже без очень быстрых движений, удаётся добиться одновременного появления на экране двух хинтов. VD>Да, ну? Как? VD>Ты уверен, что это не вложенный хинт? Там при наведении мыши на описания типов в хинте вылетает вложенный хинт описывающий этот тип. А при открытии хинта для длругого места первый автоматически закрывается. Так что штатно получить два хинта нельзя.
А, на этот счёт не уверен — не знал, что хинты могут быть и вложенными ещё.
_FR>>Кстати, хинты студийные, когда мышь не непосредственно на хинте, делаются прозрачными. Очень удобно. VD>Не вижу в этом ничего удобного. Читаемость от этого ухудшается, а разглядеть, что под ними все равно нельзя. Так что лучше их уж тогда закрывать. VD>Да и не замечал чтобы в студии хинты прозрачными становились.
А тут не читаемость улучшается, тут видимость появляетcя — когда немного видно — это в миллионы раз лучше, чем когда не видно совсем.
_FR>>Так же было бы здорово иметь возможность в хинте выделить весь или только часть текста. VD>Это уже не хинт, а редактор какой-то. Можно (и нужно) сделать копирование содержимого хинта. А уж если нужно, то можно и в каком-нибудь редакторе вставить текст и взять нужное.
Копирование тоже неплохо.
_FR>>Картинки в хинтах очень сильно нужны? VD>А они там есть? Насколько я помню — нет.
Я про картинку с зелёным жуком/пауком, например.
Help will always be given at Hogwarts to those who ask for it.
Re[10]: [WPF, VS 2010] Глобальный перехват сообщений мыши и
Здравствуйте, _FRED_, Вы писали:
_FR>А, на этот счёт не уверен — не знал, что хинты могут быть и вложенными ещё.
Ну, так взглянул бы на содержимое.
Вообще-то вложенность — это главная фишка. Весь смысл в том, что в хинтах используются короткие имена типов, а полные и другую информацию можно узнать подведя мышку к имени типа.
_FR>А тут не читаемость улучшается, тут видимость появляетcя — когда немного видно — это в миллионы раз лучше, чем когда не видно совсем.
Если человек открыл хинт, то он видимо сделал это чтобы прочесть его содержимое. Прозрачность этому мешает.
Первая версия хинта была с прозрачностью Но потом это убрали. Мешало.
_FR>Копирование тоже неплохо.
Чем плохо? Потребность в копировании есть, но редко. Так что простое и понятное решение сделать банальное копирование по нажатии кнопки или клавиатурного сочетания.
_FR>>>Картинки в хинтах очень сильно нужны? VD>>А они там есть? Насколько я помню — нет.
_FR>Я про картинку с зелёным жуком/пауком, например.
Это символ из какого-то шрифта.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: [WPF, VS 2010] Глобальный перехват сообщений мыши и к
Здравствуйте, VladD2, Вы писали:
VD>Если не ошибаюсь, там 0;0 получается. Вообще как-то с координатами мутно все.
Я тут по-экспериментировал. Нормально работает и с координатами из аргументов событий. Плюс, я все-таки перевел проверку event-ов на сравнение RoutedEvent, а то сравнивать строки как-то не комильфо. Но мне не хватает что-то вроде списка требований, чтобы полноценно изучить код и занятся допиливанием. А так, вполне готов. Да и вообще могу по возможности подключиться ко всему, связанному с WPF.
И потом такой вопрос: где вообще ведется обсуждением деталей разработки?
Re[6]: [WPF, VS 2010] Глобальный перехват сообщений мыши и к
Здравствуйте, MxMsk, Вы писали:
MM>Я тут по-экспериментировал. Нормально работает и с координатами из аргументов событий. Плюс, я все-таки перевел проверку event-ов на сравнение RoutedEvent, а то сравнивать строки как-то не комильфо. Но мне не хватает что-то вроде списка требований, чтобы полноценно изучить код и занятся допиливанием. А так, вполне готов.
Список требований таков:
1. На сегодня хинт ведет себя некорректно, если в нем содержится много текста. Он начинает уезжать за пределы окна (вверх). Это хорошо видно при попытке посмотреть код "большого" макроса. Хорошо бы сделать так чтобы хинт обрезался по размерам экара (имел высоту между областью для которой он отображается и верхом или низом экрана (в зависимости от того где больше места). При обрезании текста надо показывать нечто вроде стрелки-скроллера. При нажатии на нее должен происходить сдвиг на один экран вверх и появляться вторая (верхняя) стрелка-скроллер.
2. Нужно сделать кнопку копирования содержимого хинта в клипборд. Кнопка должна быть еле заметна и проявляться при наведении мыши. Так же нужно сделать копирование содержимого верхнего хинта в клипборд при нажатии стандартных клавиатуртных сочетаний Ctrl+C и Ctrl+Ins. При этом текст должен копироваться без форматирования (без тегов).
4. Надо сделать поддержку ссылок. Скажем, если блок текста выделен тегом <A href="значение">...</A>, то у хинта (контрола) должно вызываться событие GoTo и туда должна передаваться строка "значение", а хинт закрываться. По умолчанию такие ссылки не должны как-то отличаться от остального текста, но при наведении на них мыши курсор должен становиться рукой с пальцем.
MM>Да и вообще могу по возможности подключиться ко всему, связанному с WPF.
С WPF у нас не так много связано.
MM>И потом такой вопрос: где вообще ведется обсуждением деталей разработки?
Вообще, для этого есть форум http://www.rsdn.ru/forum/prj.nemerle. Но не страшно, если и здесь вопрос будет. Честно говоря второй форум был излишен.
ЗЫ
Собственно, автор хинта соездал вторую версию в которой ссылки были реализованы. Но при этом он очень сильно изменил хинт. Это привело к тому, что на то чтобы смерджить его с кодом интеграции у меня не хватило сил и времени. Однако имеет смысл найти последнюю версию и поглядеть на изменения. Поиск должен помочь .
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.