Колёсико мышки

Программирование на Visual Basic, главный форум. Обсуждение тем программирования на VB 1—6.
Даже если вы плохо разбираетесь в VB и программировании вообще — тут вам помогут. В разумных пределах, конечно.
Правила форума
Темы, в которых будет сначала написано «что нужно сделать», а затем просьба «помогите», будут закрыты.
Читайте требования к создаваемым темам.
David
Обычный пользователь
Обычный пользователь
 
Сообщения: 81
Зарегистрирован: 10.03.2004 (Ср) 20:41
Откуда: FRANCE

Колёсико мышки

Сообщение David » 20.11.2021 (Сб) 21:50

Добрый вечер
Нашёл на форуме как работать со средней кнопкой мышки. Кое как разобрался и прокрутка работает
Ну у меня проблема, если я прокручиваю медленно, ни каких ошибок, но стоит мне ускорить «скроллинг» и программа зависает
Для форума я написал маленький фрагмент и как на зло никаких ошибок. Не знаю как найти ошибку
Прилагаю фрагмент программы и картинку моей программы
1 – Блок который повторяется многократно по решению пользователя
2 – PictureBox
А весь юлок находится в другом PictureBox в котором я пробую решить проблему прокрутки колесика мышки
Я понимаю, что ответить мне не просто, ну если у кого-то уже было что-то подобное. Но если надо я могу и всю программу послать, правда она читает данные с SQL сервера
Ну ладно, надеюсь на вашу помощь, спасибо
Вложения
Molette souris teste 2 (Rus).zip
Фрагмент
(3.72 Кб) Скачиваний: 93
Capture d’écran.jpg
Картика моей программы
Capture d’écran.jpg (137.38 Кб) Просмотров: 3064

Teranas
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 224
Зарегистрирован: 13.12.2008 (Сб) 4:26
Откуда: Новосибирск

Re: Колёсико мышки

Сообщение Teranas » 20.11.2021 (Сб) 23:23

Ничего не зависает при любой скорости прокрутки, наверно проблема в другом
Изображение
С уважением, Андрей.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Колёсико мышки

Сообщение Хакер » 21.11.2021 (Вс) 10:56

David писал(а):Ну у меня проблема, если я прокручиваю медленно, ни каких ошибок, но стоит мне ускорить «скроллинг» и программа зависает


Очень грязный код, особенно что касается перехвата WM_MOUSEWHEEL и WindowProc. Это просто хождение по лезвию ножа и игра на грани провала.

Что здесь не так? WindowProc нужно писать или так, чтобы там гарантированно не возникало никаких ошибок, или так, чтобы возникновение ошибок внутри WindowProc не вызывало катастрофы. То есть хотя бы отлавливать ошибки.

Как здесь может развиться катастрофа? Да здесь все написано так, чтобы WindowProc был бомбой замедленного действия. Начиная от использования позднего связывание (вызов fFrm.MouseWheel) и заканчивая отсутствием обработки ошибок.

Что если ошибка возникнет в самом WindowProc или в одной из процедур, вызываемых из WindowProc, например внутри MouseWheel? Её никто не обрабатывает на этом уровне, а выше WindowProc обработчиков нет и не может быть. Это значит, что VB попытается показать сообщение об ошибке, но показ сообщения об ошибке сопряжён с тем, что окна (оконная процедура которых переопределена), будут получать какие-то сообщения (о потери фокуса, например) — а значит это повторный вход в WindowProc.

Вот уже и повод для неконтроируемой рекурсии.

По крайней мере в режиме отладки (не в режиме скомпилированного проекта) ошибка в WindowProc приведёт к попытке остановиться в WindowProc, что переводит весь код проекта в «условно неработоспособное» состояние, а вот обработка оконные сообщений при этом не прекращается и сабклассинг с окон не снимается. Это прямо гарантия плохих последствий.

WindowProc должна в любом случае написана так, чтобы перехватывать любые ошибки, которые происходят непосредственно в ней или в одной из вызываемы из неё процедур — и ни в коем случае не пропускать их уровнем выше. Обрабатывать их тоже надо умно: показывать сообщения из WindowProc, естественно, ни при каких обстоятельствах нельзя.

Потом использование раннего связывания с WindowProc: это чуть-чуть медленно, но самое главное что это очень ошибко-опасно, и никакой обработки возможной ошибки (отсутствие в найденном экземпляре формы метода MouseWheel по какой-то причине) нет — случись так, что метода не окажется, это гарантированный вылет программы.

Вот здесь:
Код: Выделить всё
      Set fFrm = GetForm(Lwnd)
      If fFrm Is Nothing Then

хотя бы была мысль о том, что GetForm может вернуть Nothing, а уже здесь:
Код: Выделить всё
GetForm(GetParent(Lwnd)).MouseWheel MouseKeys, Rotation, Xpos, Ypos

почему-то нет.

Вот здесь:
Код: Выделить всё
WindowProc = CallWindowProc(GetProp(Lwnd, "PrevWndProc"), Lwnd, Lmsg, wParam, lParam)

что если GetProp по какой-то причине вернёт 0? Кто-то по ошибке удалил prop с именем «PrevWndProc». Будет осуществлён вызов по адресу 0 — это гарантированный крах всей программы.

А ведь можно проверять на корректность возврат GetProp() и вызывать DefWindowProc() — тогда банальное мелкое вредительство не сможет сразу насмерть убить всё приложение.

Почему PictureBoxZoom называется PictureBoxZoom, если там нет никакого зуммирования, а есть только скроллинг?
Почему у PictureBoxZoom первый параметр ByRef а не ByVal?

Почему в качестве контейнера используется PictureBox, а не Frame, если Frame дал бы все те же возможности, но ресурсов потребляет меньше?

Почему мы двигаем блоки каждый индивидуально в цикле массивом, если можно положить все блоки в один контейнер-холст внутри контейнера-рамки и двигать холст внутри рамки одним вызовом?

Почему коллекция контролов перебирается с помощью For i = 1 To Picture2.UBound, если для коллекции (в отличие от массива) не гарантируется непрерывность последовательности индексов-ключей? Почему не используется For Each, который бы гарантированно правильно работал?

Почему в VScroll1_Change переменная dist объявлена As Integer если она легко может переполниться, что приведёт к ошибке.

Почему мы сабклассим родительское окно, а потом мучительно ищем, над каким же контролом была мышка, вместо того, чтобы сразу сабклассить нужный контрол и без разбирательств реагировать на событие прокрутки колеса прямо над ним?

Почему внутри Isover после вызова GetWindowRect не проверяется, сработала ли вообще функция? Ведь если она не сработает, а мышка будет находиться в координатах (0; 0), получится, что мы будем считать, что любое окно находится под мышкой. И вообще: IsOver — это изобратение велосипеда, правда без одной ноги. То, что делает IsOver операционная система и так делает, и это процесс называетс hit-testing. Его частью, кстати, является отправка окну сообщения WM_NCHITTEST. Этот hit-testing и так делается при любом мышном событии, чтобы понять, кому предназначается сообщение. Уже делается, и никак этого не избежать, а раз делается, значит надо этим пользоваться, а не изобретать своё. Только вот уже имеющийся в Windows механизм дружит с окнами непрямоугольной формы (круглыми окнами, окнами со скруглёнными углами и т.п.), а наш самодельный — нет.

В общем, в коде очень много спорных и некачественных мест. Всё это можно было сделать гораздо изящнее и элегантнее, да и кода было бы меньше. При этом, демонстрационный пример работает и не зависает:
Для форума я написал маленький фрагмент и как на зло никаких ошибок. Не знаю как найти ошибку


У нас тоже. Видимо несмотря на все грязные места в коде, их недостаточно, чтобы испортить в жизнь, а в реальном проекте есть свои грязные места, которых уже оказывается достаточным для того, чтобы вызвать проблемы.

David писал(а):Я понимаю, что ответить мне не просто, ну если у кого-то уже было что-то подобное. Но если надо я могу и всю программу послать, правда она читает данные с SQL сервера

Значит нужно демонстрационный пример делать из реального проекта, полностью заменив получение данных от SQL-сервера на получение данных из фиктивного/фальшивого/загрушечного источника данных. Остальное оставить как есть.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

David
Обычный пользователь
Обычный пользователь
 
Сообщения: 81
Зарегистрирован: 10.03.2004 (Ср) 20:41
Откуда: FRANCE

Re: Колёсико мышки

Сообщение David » 22.11.2021 (Пн) 10:28

Привет Хаккер,
Очень грязный код, особенно ...
WindowProc нужно писать или так, чтобы там гарантированно не возникало никаких ошибок, или так, чтобы возникновение ошибок внутри WindowProc не вызывало катастрофы. То есть хотя бы отлавливать ошибки.


Критика принята, ну я и не претендую на звание "великий программист". Этот код я списал с интернета и по правде сказать не всё понял. Вообще-то, по профессии я архитектор и программирование это больше хобби, чем способ заработать деньги
В основном "краду" программы выложенные в сети, пробую подделать под мои нужды и использую.
Хаккер, может подскажешь где могу подсмотреть "чистый код" ? Спасибо

Значит нужно демонстрационный пример делать из реального проекта, полностью заменив получение данных от SQL-сервера на получение данных из фиктивного/фальшивого/загрушечного источника данных. Остальное оставить как есть.

А на счёт реального проекта, ну попробовал создать версии без SQL, вроде работает и ошибка прокрутки появляется. Извини, но некоторые комментарии на французском, в моей версии VB6 я их на русском не могу писать
Ну если сможешь помочь, спасибо а нет, не ломай голову без колесика программа и так работает
Ещё раз спасибо

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Колёсико мышки

Сообщение Хакер » 22.11.2021 (Пн) 18:43

David писал(а):Хаккер, может подскажешь где могу подсмотреть "чистый код" ? Спасибо

Не знаю. Его можно и не подсматривать, чтобы писать, а просто чувствовать и думать, что делаешь.
Подсмотреть можно в хороших программах.
На форуме (нашем), хотя бы, в хороших примерах.

Ну если сможешь помочь, спасибо а нет, не ломай голову без колесика программа и так работает

Так проект выложен не полный. Половина модулей в архив не включена:
  • ..\ReyOrientLabel.bas
  • ..\Glob_Transparent.bas
  • ..\Glob_ModMolette(R).bas
  • ..\UserControl1.ctl
  • Ещё и какой-то gif89.dll ей нужен
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

David
Обычный пользователь
Обычный пользователь
 
Сообщения: 81
Зарегистрирован: 10.03.2004 (Ср) 20:41
Откуда: FRANCE

Re: Колёсико мышки

Сообщение David » 23.11.2021 (Вт) 11:57

Так проект выложен не полный. Половина модулей в архив не включена:

Извините, совсем забыл об этих модулях

Прочитал твои комментарии к теме Kак ограничить область действия MouseWheel? от 07/05/2020, ну что сказать, чтобы всё понять надо было не архитектурный заканчивать а математический
Извини, но я правда ничего (ну почти ничего) не понял

Его можно и не подсматривать, чтобы писать, а просто чувствовать и думать, что делаешь.

К этому списку надо ещё добавить : иметь соответствующее образование
Программки я пишу для внутреннего пользования и в принципе для "программиста" самоучки мои программки нас устраивают.
Поэтому пойми меня правильно, если в программке я смогу выделить текст только красным цветом, а хотелось бы зеленным, то не очень ломая голову, так и оставлю, т.е. будет красным.
Тоже самое с колёсиком, списал программку, попробовал на тестовом примере - работает. Адоптировал в основную – работает ... и вдруг зависает, ну и подумал может кто поможет.

А архивный файл я поменял, так что если поможешь спасибо, а если нет всё равно спасибо, так как ты уже мне несколько раз помогал
Вложения
GR-TGBT.zip
(409.18 Кб) Скачиваний: 93

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Колёсико мышки

Сообщение Хакер » 26.11.2021 (Пт) 1:26

Так, ну и как этим пользоваться?
При старте нас встречает вот это окно:
scrsht_wnd1.png
scrsht_wnd1.png (11.13 Кб) Просмотров: 2998


Что делать дальше? Какой проект выбирать? И выбирать ли вообще, или что-то другое надо делать?

Я выбрал первый (хотя разницы нет), дальше появляется вот такое окно:
scrshot_wnd2.png
scrshot_wnd2.png (29.9 Кб) Просмотров: 2998


Что здесь прокручивать колесом, если тут прокручивать по сути нечего?
Если попытаться покрутить мышкой над единственной строкой, то возникает ошибка:
scrshot_wnd2_error.png
scrshot_wnd2_error.png (7.06 Кб) Просмотров: 2998


Потому что код кривейший, и элемента с ключом «1» в коллекции контролов действительно в этот момент нет:
Код: Выделить всё
If (frmCadreLigne(1).Top = 0 And Naprav = 1) Or (Not butGestGrup(4).Enabled And Naprav = -1) Then Exit Sub


Мало того, что код кривейший, в нём ещё и обработки ошибок нет, поэтому когда эта ошибка возникает, по нормальному с ней что-то сделать через среду нельзя — мешает сабклассинг, который на время остановки проекта в режим отладки нужно снимать (но и этого нет в проекте нет).

Но я так понимаю, жалоба не на это (хотя само по себе это уже ни в какие ворота не лезет).

Ладно, понадобавлял я с помощью «плюсиков» всякого фарша в окно:
scrshot_wnd2_dummies.png
scrshot_wnd2_dummies.png (28.31 Кб) Просмотров: 2998


Прокручиваю колесом мышки — никакого пресловутого «зависания» проекта нет. Как ни крути, а ничего не зависает.

Другой разговор, что при добавлении «плюсиком» всё окно омерзительно моргает, а при прокрутке колесом сама прокрутка ЧУДОВИЩНО тормозит (как будто я работаю на компьютере образца 1981-го года с тактовой частотой 1 МГц) и совершенно неадекватно себя ведёт.

Причины тормозов мне очевидны сходу — это комбинация убойного набора ошибок при проектировании/реализации всего, что стоит из эти окном:
  • Сама по себе идея делать какие-то таблицы в динамическим числом элементов путём тиражирования контролов — это мёртворождённая идея. Это может быть худо-бедно ещё применимо, если речь идёт о тиражировании 10—20 контролов, ну пусть 50-контролов. Но у тебя же на одну только «строчку» создаётся с десяток контролов. Это так не делается. Попытка создать в своей программе классную таблицу, которую можно было бы прокручивать, путём размножению контролов по количеству строк обречена на провал. Это будет работать на 20 строках, но когда будет 100 строк, 1000 строк, 10 000 строк — это значит ты своим кодом будет пытаться подвинуть тысячу или десять тысяч контролов? Это будет тормоза, каких свет не видовал. Так не пишут программы. Так не делают пользовательские интерфейсы. Excel представляет собой таблицу из ячеек, и строк и столбцов может быть очень много, но внутри Excel ячейка не представляна каким-то контролом типа PictureBox или TextBox. Оно бы просто не могло так работать учитывая, что в Excel одних только строк могут быть миллионы, не говоря уже о столбцах. Контролы очень тяжеловесно (по крайней мере некоторых из них), и делать таблицу на 1000 строк из тысячи контролов это большая глупость, особенно учитывая, что на экране всё равно будет одновременно видимо только очень малая часть строк (например 10—20), а значит и ничтожно малая часть контролов.
  • Попытка при прокрутке таблицы двигать каждый контрол отдельно — это ещё одна огромная ошибка, наслоенная на предыдущую огромную ошибку (саму идею делать всё это на тиражируемых контролах). Ладно бы всё это лежало в одном большом контроле-контейнере, и при прокрутке ты бы одной строчкой кода двигал большой контрол-контейнер. Это было бы идеальным решением на фоне неидеальной архитектуры. Ну хорошо, если не так, то можно было бы хотя бы для одного блока (или группы, как оно у тебя называется) сделать свой UserControl, и тогда для 20 групп ты хотя бы двигал бы в цикле 20 UserControl-ов, а не 200 отдельных элементиков, из которых собраны группы и вся их начинка. А лучше бы и те 20 UserControl-ов лежали в родительском контрол-контейнере и ты двигал бы его внутри viewport-контрола.

    Но нет, ты выбрал худшее из худшего: ты просто перебираешь все контролы, какие только есть, из всех тех, что образуют твою иерархическую таблицу (набор групп с динамическим подэлементами-строками в каждой группе) и просто двигаешь каждый контрол по отдельности. Учитывая общую тяжеловесность контролов и некоторые особенности внутреннего устройства именно VB-шных контролов, это супер-неэффективно.

    И ведь главное, что такое UserControl ты знаешь, у тебя он в проекте используется, но не для дела, а для вертикальных надписей. Нет бы для дела использовать. И в целом, в программе куча свистелок и перделок украшательства и нестандартного дизайна, но прежде чем делать нестандартные дизайны вопреки мудрому взгляду UI-дизайнеров Windows, нужно сначала просто научиться писать нормальные, стабильные и со стройной архитектурой внутри программы, пусть они будут и в обычном стандартном оформлении Windows.
  • Дальше причина неадекватного поведения — это пара глупых строк внутри xDecaler:
    Код: Выделить всё
      frmCadreGauche.Refresh
      DoEvents

    Зачем вот это вот всё? Такое впечатление, что эти две строчки вставлены не потому, что было понимание того, что они делают, не для решения какой-то конкретной задачи, а как волшебное заклинание, которое пришло к нам «от предков», и надо на всякий случай его вставить, чтобы не обидеть «дух предков».

    Если первая из этих дву строк просто ещё больше замедляет процесс прокрутки, то вторая — это настоящая бомба, которая имеет потенциал умопромрачительным образом ломать ход программы, потому что DoEvents может подобрать в очереди сообщений следующий WM_MOUSEWHEEL, и передать его в DispatchMessage, а оттуда будет вызвана ещё раз WindowProc (хотя мы ещё с предыдущего раза оттуда не вышли), оттуда запустится очередной цикл обхода элементов массива элементов (точнее коллекции), и процесс может повториться неивзестно сколько раз.

    Т.е. у нас 10 строк, мы мы подвинули 3 строки, после чего DoEvents наткнулась на ещё один WM_MOUSEWHEEL, произойдёт цепочка DispatchMessage->WindowProc->MouseWheel->xPictureBoxZoom->xDecaler->DoEvents — и так несколько раз. В итоге у нас строки при получении двух WM_MOUSEWHEEL будут двигаться не в порядке 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 — а в совершенно поломанном порядке 1, 2, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 4, 5, 6, 7, 8, 9, 10. А если уж прямо хорошо покрутить колёсиком туда сюда, то в очередь прилетит целая куча WM_MOUSEWHEEL, а поскольку код написан настолько топорно, что программа не успевает обрабатывать приходящие WM_MOUSEWHEEL, а она может погрузиться в глубочайшую рекурсию, которая в теории может вызвать и переполнение стека.

    И ведь главное, что не только WM_MOUSEWHEEL может обрабатываться обработчиком, вызванном из работающего и ещё не завершившегося обработчика предыдущего WM_MOUSEWHEEL, но и все остальные оконные сообщения — они тоже лежут в эту неконтролируемую (и абсолютно ненужную, порочную) рекурсию.

    В итоге если прямо хорошо, интенсивно и долго крутить колёсико, мне удалётся легко вызвать крах не только проекта, но и среды разработки:
    fatal.png
    fatal.png (15.11 Кб) Просмотров: 2998

У меня нет никакого «зависания» в исходном смысле этого слова, а есть только жуткие тормоза и неадекватное поведение, но я уверен, что и в зависании виноват этот чёртов DoEvents, точнее полных хаос, который происходит в коде, на фоне которого DoEvents это не досадное исключение, а логичное поддержание общей картины.

И там помимо этой реализации прокрутки колесом мыши огромное число серьёзных нареканий — я могу перечислить если не все, то хотя бы большинство отдельным постом. Так программы не делают. Коль уж пошли отсылки к архитектуре и строительству:
David писал(а):Прочитал твои комментарии к теме Kак ограничить область действия MouseWheel? от 07/05/2020, ну что сказать, чтобы всё понять надо было не архитектурный заканчивать а математический

то всё, что я вижу в коде, это как попытка построить здание, строя несущие стены не из бетона, не из бетонных блоков, не из кирпичей, а из старых телевизоров, склеивая их мебельным клеем между собой:
tvwall.jpg
tvwall.jpg (64.49 Кб) Просмотров: 2998

А если необходимо сделать круглые колонны в помещениях — делать из пропановых баллонов (они ведь тоже круглые), наставляя один поверх другого и заделывая получающиеся разрывы в колоннах монтажной пеной:
columns_from_balloons.jpg
columns_from_balloons.jpg (100.47 Кб) Просмотров: 2998


Даже если нет образования строителя, даже если строительство это хобби, то даже для хоббиста-строителя должно быть понятно, что так дома не строят. Да, можно сложить стенку из телевизоров, поставив их в 3—4 яруса, но попытка построить 10-этажное здание из старых телевизоров неминуемо приведёт к тому, что нижние «блоки» под весом всего столба, опёртого на них сверху, схлопнутся, и что всё здание, построенное из таких элементов, рухнет, а до того как рухнет — будет шататься от ветра, потому что едва различимая гибкость отдельно взятого телевизора, помноженная на огромное число ящиков-элементов, сделают стену очень нежёсткой, как бы хорошо элементы не были сцеплены между собой.

Так что оправдание про образование не применяется. Тут полно людей, которые на правах хобби пишут отличный код.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

David
Обычный пользователь
Обычный пользователь
 
Сообщения: 81
Зарегистрирован: 10.03.2004 (Ср) 20:41
Откуда: FRANCE

Re: Колёсико мышки

Сообщение David » 28.11.2021 (Вс) 20:25

Спасибо, постараюсь что-то улучшить по твоим замечаниям, если удаться может даже похвастаюсь
А вообще-то не нервничай, ну не получается у меня писать хорошие программы
Ну всё, ещё раз спасибо

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Колёсико мышки

Сообщение Хакер » 28.11.2021 (Вс) 20:40

Да я вообще не нервничаю. Только расстраиваюсь, что люди сами себе «в ногу стреляют».

Я вот предлагаю, что будет, если в этом окне завести 10 000 групп с 10 пунктами в каждом. Насколько тормозной станет прокрутка (в реальности, я думаю, приложение не переварит и близко такого количество контролов, которое будет при таких условиях). Там думаю один шаг прокрутки будет занимать минуты.

Я в соседней теме упоминал логгинг-контрол (точнее окно), написанное с нуля. Оно во-первых легко может вобрать в себя хоть сто тысяч, хоть пятьсот тысяч, хоть миллион(ы) строк. Во-вторых, прокрутка текста в этом окне работает одинаково быстро вообще вне зависимости от того, сколько строк в него добавлено: сто, тысяча, десяток тысяч, сотня тысяч, миллион

Я могу выложить исходники такого окна, если это поможет правильным образом переосмыслить взгляд на вещи (но боюсь, что не поможет).
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Колёсико мышки

Сообщение Хакер » 28.11.2021 (Вс) 22:14

Вот пример, подчёркивающий мои замечания на практике.
David_scrolling.zip
(4.89 Кб) Скачиваний: 103

В примере три формы: красная, желтая, и зелёная. Плохая, почти плохая и хорошая.
Каждая форма при загрузке создаёт 25×25 = 625 контролов, в нашем случае кнопок. На каждой форме также есть кнопки «Вверх»/«Вниз», перемещающие контролы, и производит замер времени, требуемого на осуществление одного шага скроллинга.

В первой форме (красная, самая плохая) я сделал в точности так, как у тебя — перемещение каждого контрола в индивидуальном порядке.
Во второй форме (жёлтая, всё равно плохая) я убрал глупую пару вызовов Refresh+DoEvents, которую критиковал выше.
В зелёной форме (зелёная, хорошая) я сделал подход с перемещаемых контейнером-холстом (canvas), как советовал делать.

Результат сравнения:
david_benchmarks.png
david_benchmarks.png (10.15 Кб) Просмотров: 2978


  • Как видишь, твой подход с 625 контроллами требует аж полторы секунды на один шаг прокрутки. С прокруткой мышкой это будет сущий ад. Да и без него, прямо глазами видно «волну» передвижения отдельных контролов. Совершенно неприемлемый результат.
  • Убирание глупой пары Refresh+DoEvents ускоряет этот подход в целых три раза! Но всё равно полсекунду на то, что должно делаться мгновенно.
  • Мой подход даёт скорость выполнения в 1000 раз быстрее, чем средний, или в 3000 раз быстрее, чем изначальный. Вот так уже можно делать, это не будет тормозить.

Хотя, как я писал выше, сам по себе подход с «тиражированием контролов» подходит для небольшого числа элементов (как раз в пределах нескольких сотен). Сделать какую-нибудь таблицу, список с тысячами, десятками тысяч, сотнями тысяч элементов на тиражируемых контролах нельзя — надо делать совершенно по другому. В простейшем случае решение заключается в том, чтобы использовать контролы только для той части элементов, которые пользователь сейчас видит на экране. Если у нас список из миллиона элементов, пользователь же не видит весь миллион элементов на экране сразу? Он видит десяток элементов — крохотную часть из всего миллиона. Вот для этого десятка видимых элементов мы используем настоящие контролы. А более сложный подход состоит в том, что мы не используем контролы вообще, а отрисовываем из всего миллиона малую или крохотную часть тех, которые сейчас видны (видимы) пользователю.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

David
Обычный пользователь
Обычный пользователь
 
Сообщения: 81
Зарегистрирован: 10.03.2004 (Ср) 20:41
Откуда: FRANCE

Re: Колёсико мышки

Сообщение David » 05.12.2021 (Вс) 14:17

Здравствуй Хакер
Честно говоря не думал что ты мне ответишь. Зашёл на форум по другому поводу и увидел твой ответ.
Так что буду разбираться
Спасибо тебе

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Колёсико мышки

Сообщение Хакер » 05.12.2021 (Вс) 14:28

Могу к своему приемеру ещё прокрутку колесом прикрутить, и показать наглядно, почему твой код работает неадекватно, сэмулировав ошибки, допущенные в твоём коде.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

kibernetics
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 945
Зарегистрирован: 03.05.2006 (Ср) 13:31
Откуда: Minsk

Re: Колёсико мышки

Сообщение kibernetics » 11.12.2021 (Сб) 14:16

Хакер писал(а):Могу к своему приемеру ещё прокрутку колесом прикрутить, и показать наглядно, почему твой код работает неадекватно, сэмулировав ошибки, допущенные в твоём коде.

Хакер, дорогой, привет!
Пожалуйста, лучше к моему коду, если можно попросить тебя, прикрути колёсико мышки пожалуйста.
Есть приложушка одна, и так раздражает отсутствие скрола...
Как-то решал эту проблему, не смог решить и бросил.
А тут как раз тема про колёсико.
Плиз, если не затруднит.

В частности, не поддаётся вот этот фрейм в центре формы
not_scroll.JPG
not_scroll.JPG (30.57 Кб) Просмотров: 2883


Сорц:
Tovarscope.zip
(131.01 Кб) Скачиваний: 91

RNG21
Начинающий
Начинающий
 
Сообщения: 8
Зарегистрирован: 08.12.2021 (Ср) 23:06

Re: Колёсико мышки

Сообщение RNG21 » 12.12.2021 (Вс) 14:26

kibernetics
Принципиально прокрутка колесиком к твоему фрейму прикручивается примерно так:
Вложения
Tovarscope_scroll.zip
(126.64 Кб) Скачиваний: 91

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Колёсико мышки

Сообщение Хакер » 12.12.2021 (Вс) 17:01

RNG21 писал(а):Принципиально прокрутка колесиком к твоему фрейму прикручивается примерно так:

Не дай боже так делать.

Шаг прокрутки вообще игнорируется, хоть чуть-чуть крути мышку, хоть со всей дури:
Код: Выделить всё
    If wParam < 0 Then Wheel = -250 'скорость прокрутки вниз (отрицательное значение - колесико на себя)
    If wParam > 0 Then Wheel = 250 'скорость прокрутки вверх


При создании множества экземпляров формы frmPriceExporter1 кручение колеса над любой из них будет оказывать эффект на «дефолтный» экземпляр (если он вообще существует, ведь может, что и нет). Должна быть логика трансляции hwnd в ссылку на объект.

Почему-то используется позднее связывание. Почему Contr As Object, а не Contr As HSB?

Обработка ошибок в WindowProc хоть и есть, но она ничего не делает — если какое-то сообщение будет настойчиво отправляться контролу, пока не обрабаотает, и именно оно вызывает ошибку, всё это зафризится.

Для хранения оригинального адреса WindowProc используется глобальная переменная модуля: это значит невозможно установить перехват сообщений для более чем одного окна (то есть даже для более чем одного контрола в пределах одной формы). Причём это ограничение никак не очевидно для гипотетического пользователя функций MouseHook/MouseUnHook: он поймёт, что перехватывать больше одного окна нельзя только тогда, когда среда с проектом просто рухнет, потому что окнам назначена неправильная оконная процедура, когда будет вызываться MouseUnHook.

Даже если предыдущий пункт исправить, если кому-то надо перехватить несколько несколько разных оконных сообщений, надо запоминать порядок, в котором устанавливаются перехваты, и снимать перехваты в точности в том порядке, в каком они устанавливались. Если мы собираем проект из модулей, написанных разными людьми, которые создали каждый по своему собственному механизму перехвата, разные механизмы нельзя подружить между собой.

Кроме того, в добавок к предыдщему пункту, не используется преимущество подсчёта ссылок и автоматического уничтожения объектов, на которые не осталось ссылок, хотя оно могло бы использоваться для того, чтобы снимать перехват тогда, когда он никому не нужен, например тогда, когда экземпляр формы выгружается, и он спас бы проект от падения, если сабклассинг снять забыли, просто забыв разместить вызов «MouseUnHook» в нужном месте. Деструктор (Class_Terminate) промежуточного объекта-перехвата вызывался бы в любом случае, даже если о перехвате забыли, кроме случае с нажатием кнопки «Стоп». Но от нажатия кнопки «Стоп» вообще спасения нет, кроме использования объектов из сторонних библиотек. Кроме того, использование прокси-объекта для перехвата позволяет использовать событийный механизм, а на одно событие одного объекта, как известно, могут подписаться сразу множество подписчиков. Это означает, что если несколько «сторон» заинтересованы в одном сообщение, или даже в кучи разных сообщений, не нужно для каждой отдельной цели устанавливать свой сабклассинг, а потом беспокоиться о снятии целого каскада сабклассингов в правильном порядке. Сабклассинг устанавливается один раз (и один перехват неразрывно связан со своим прокси-объектом), а прокси-объект транслирует получение оконных сообщений в срабатывание события объекта. На событие объекта может быть вообще никто не подписан (тогда наш сабклассинг никому не мешает и никого не дёргает), может быть подписан один подписчик, а может быть сразу куча. Таким образом, можно независимо и изолированно друг от друга (грубо говоря — в разных процедурах) иметь обработчики для разных оконных сообщений, и эти обработчики не будут ни зависеть друг от друга, ни мешать друг другу.

Именно так и сделано и все эти недостатки устранены в моём классе для перехвата оконных сообщений, который я хотел сюда выложить, но я думаю, что его надо как следует оформить и поместить в «Кирпичный завод», а это значит, что нужно снабдить его документацией, примерами использования, отполировать до идеального состояния. Поэтому я не могу выложить его мгновенно.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

The trick
Постоялец
Постоялец
 
Сообщения: 781
Зарегистрирован: 26.06.2010 (Сб) 23:08

Re: Колёсико мышки

Сообщение The trick » 12.12.2021 (Вс) 22:55

Мой класс позволяет сабклассить + не боится ни End, ни кнопки Stop.
UA6527P

RNG21
Начинающий
Начинающий
 
Сообщения: 8
Зарегистрирован: 08.12.2021 (Ср) 23:06

Re: Колёсико мышки

Сообщение RNG21 » 13.12.2021 (Пн) 0:01

Код в моих правках исходников kibernetics конечно, не лучший, но я к этому и не стремился, просто хотел на коротком примере показать, как это сделать. Во всяком случае, я не заметил в проекте признаков того, что форма frmPriceExporter1 может быть запущена в нескольких экземплярах (правда, в это сильно не вникал), а действия при обработке ошибок человек сам напишет, какие ему нужно. Вот то, что для хранения адреса WindowProc используется единая глобальная переменная модуля - да, грубая ошибка, даже для короткого примера, просмотрел.

kibernetics
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 945
Зарегистрирован: 03.05.2006 (Ср) 13:31
Откуда: Minsk

Re: Колёсико мышки

Сообщение kibernetics » 14.12.2021 (Вт) 16:04

RNG21 писал(а):Код в моих правках исходников kibernetics конечно, не лучший, но я к этому и не стремился, просто хотел на коротком примере показать, как это сделать. Во всяком случае, я не заметил в проекте признаков того, что форма frmPriceExporter1 может быть запущена в нескольких экземплярах (правда, в это сильно не вникал), а действия при обработке ошибок человек сам напишет, какие ему нужно. Вот то, что для хранения адреса WindowProc используется единая глобальная переменная модуля - да, грубая ошибка, даже для короткого примера, просмотрел.


Друзья, спасибо, за то что обратили внимание на мою проблему.
А сделать так, чтобы скрол срабатывал по событию mouseover над зоной фрейма?
По клику это не интересно, т.к. если уже кликаешь по фрейму, то ничто не мешает кликнуть сразу и по самому скролу и его переместить, т.е. теряется необходимость вообще в скроле.

Я надеюсь добиться такого эффекта, как вот если посмотреть на работу всех других контролов, где находится курсор - там работает скролинг именного того контрола над которым этот самый курсор находится.
ListView, Listbox, TreeView. И вот бы сделать и для Frame аналогичное поведение.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Колёсико мышки

Сообщение Хакер » 16.12.2021 (Чт) 0:06

Я надеюсь добиться такого эффекта, как вот если посмотреть на работу всех других контролов, где находится курсор - там работает скролинг именного того контрола над которым этот самый курсор находится.
ListView, Listbox, TreeView. И вот бы сделать и для Frame аналогичное поведение.


Не понял? Ты хочешь, чтобы вращение колеса оказывало влияние на тот контрол, над которым сейчас находится указатель мышки, а не на тот, который в данный момент обладает фокусом? Непонятно, про работу каких «всех других контролов» ты говоришь, потому что так никакие контролы не работают. Ни ListView, ни ListBox, ни TreeView. Ни, если взять какой-нибудь стандартный диалог, типа такого:
font_comdlg.png
font_comdlg.png (13.97 Кб) Просмотров: 2820

Нигде скроллинг не влияет на то окно, над которым мышка — везде скроллинг влияет на то окно, которое в данный момент обладает фокусом. Так задумано в Windows, это стандартный подход, его во-первых не стоит ломать, а во-вторых, дорого обойдётся его ломать.

Ломать это поведение достаточно непросто и некомфортно. В Windows в каждой сессии есть специальный поток, названный RIT (Raw Input Thread), который получает сырые данные о клавиатурных и мышиных событиях от драйверов соответствующих устройств, и занимается тем, что конвертирует сырые данные от драйверов в понятный приложениям формат и рассылкой оконным сообщений тому, кому нужно. В логику работы этого кода заложено, что WM_MOSUEWHEEL посылается окну, имеющему фокус, а не окну, над которым стоит мышка. Это системно-глобальная логика, её нельзя переопределить для одного приложения, и вообще её не так-то просто переопределить, потому что это происходит в режиме ядра.

Окно, получающее WM_MOUSEWHEEL, может не знать ничего о прокрутке, и не обрабатывать это сообщение, но тогда оконная процедура вызовет DefWindowProc, а стандартной логикой действия DefWindowProc для сообщений WM_MOUSEWHEEL является отправка сообщения родительскому окну того окна, для которого вызвана DefWindowProc.

Поэтому сообщение WM_MOUSEWHEEL изначально прилетает самому вложенному окну, самому дочернему в иерархии окон, а затем, если его не обрабатывают, начинает всплывать наверх, пока не дойдёт до самого верха, и если и там никто его не обрабатал, оно бесследно исчезает.

На заре появления мышек с прокруткой использовался другой подход: поскольку мышки с колесом только-только появились, а в системе не было никакой поддержки WM_MOUSEWHEEL, но зато была поддержка пользовательских регистрируемых оконных сообщений, предполагалось, что проприетарный драйвер мышки и «прогрессивное приложение», которое хочет работать с колёсиком, оба регистрируюют и обмениваются сообщение MSH_MOUSEWHEEL. Вот MSH_MOUSEWHEEL как раз отправлялось родительскому окну, и оконная процедура родительского окна играла роль диспетчера и сама решала, какому дочернему окну перенаправить это сообщение и по какому принципу выбрать это дочернее окно. Там можно было на уровне кода оконной процедуры родительского окна сделать так, чтобы таковым дочерним окном было то, над которым стоит мышка, а не то, которое обладает фокусом. Но не с WM_MOUSEWHEEL.

Что можно придумать с WM_MOUSEWHEEL?

Если использовать windowless-контролы, то проблема сведётся к отлову сообщения WM_MOUSEWHEEL только у родительского окна.

Если не ограничивать себя, то по видимому нужно фильтровать WM_MOUSEWHEEL и каким-то образом переопределять параметр hwnd у сообщений. По какому принципу переопределять? Ну, учитывая, что координаты в сообщении WM_MOUSEWHEEL идут в абсолютной (экранной) системе координат, можно вычислять окно получатель с помощью WindowFromPoint. Но здесь есть момент: окно, находящееся под указателем на момент отправки сообщения, и окно, находящееся под точкой по указанным координатам на момент обработки сообщения — не всегда одно и то же окно, потому что в промежутке между отправкой и обработкой оконного сообщения что-то могло подвинуться или перестроиться в иерархии окон.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

kibernetics
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 945
Зарегистрирован: 03.05.2006 (Ср) 13:31
Откуда: Minsk

Re: Колёсико мышки

Сообщение kibernetics » 16.12.2021 (Чт) 0:32

Я искренне надеюсь, что это только потому, что у тебя на скрине форма из WinXP.

На Win10 это давно априори. Навёл мышь - получи скролл. Не нужно клацать.
Изображение
kiber-16-12-2021-00-19-09.gif
(520.7 Кб) Скачиваний: 84

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Колёсико мышки

Сообщение Хакер » 16.12.2021 (Чт) 1:24

Так это экселевский диалог, а ты покажи общесистемный, коммондиалоговский, например из Блокнота, если там до сих пор используют CommonDialog-овский диалог выбора шрифта.

Если стандартную логику работу колеса мыши так поменяли в этих ваших новомодных версиях Windows на общесистемном уровне, то это говорит о том, что: (а) у современных разработчиков Windows кисель вместо головы, чтобы ломать такую стандартную и укоренившуюся вещь и правильно, что я не перехожу на эти ваши новомодные версии, (б) значит и VB-шных проектах должна начать работать новая логика, если это не один из тех случаев, когда для активации нового поведения нужен манифест.

Если стандартную логику не поменяли, а это именно офисовский диалог так работает, то это толко подтверждает мои слова о windowless-контролах.

Документация по WM_MOUSEWHEEL до сих пор говорит:
Sent to the focus window when the mouse wheel is rotated. The DefWindowProc function propagates the message to the window's parent. There should be no internal forwarding of the message, since DefWindowProc propagates it up the parent chain until it finds a window that processes it.

...

It is up to the application to forward MSH_MOUSEWHEEL to any embedded objects or controls. The application is required to send the message to an active embedded OLE application. It is optional that the application sends it to a wheel-enabled control with focus. If the application does send the message to a control, it can check the return value to see if the message was processed. Controls are required to return a value of TRUE if they process the message.


Никакой замены «focused» на «hovered» и особых пометок про новое поведение в новых версиях в документации нет.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

kibernetics
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 945
Зарегистрирован: 03.05.2006 (Ср) 13:31
Откуда: Minsk

Re: Колёсико мышки

Сообщение kibernetics » 16.12.2021 (Чт) 13:30

Хакер писал(а):Так это экселевский диалог, а ты покажи общесистемный, коммондиалоговский, например из Блокнота, если там до сих пор используют CommonDialog-овский диалог выбора шрифта.

Это повсеместно в системе. Блокнот в том числе. Очень удобно. Не надо прицеливаться в скрол, и совершать действие клика.
Изображение

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Колёсико мышки

Сообщение Хакер » 16.12.2021 (Чт) 15:22

Ну тогда и в VB-шных программмах это должно работать так же, если это общесистемное поведение?

Сделай форму с 3 листбоксами, заполненными айтемами и посмотри, как оно работает для VB-шных программ. Если не так, как надо, вызови из VB-шной программы фонтовый коммондиалог, и посмотри, что там.

По результатам будет ясно, откуда ноги растут у нового поведения, но в любом случае, то, что это никак не отражено в документации (и то, что поведение, на которое рассчитывали программы, сломали), говорит о печальном положении дел.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

kibernetics
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 945
Зарегистрирован: 03.05.2006 (Ср) 13:31
Откуда: Minsk

Re: Колёсико мышки

Сообщение kibernetics » 16.12.2021 (Чт) 16:11

Хакер писал(а):Ну тогда и в VB-шных программмах это должно работать так же, если это общесистемное поведение?

Сделай форму с 3 листбоксами, заполненными айтемами и посмотри, как оно работает для VB-шных программ. Если не так, как надо, вызови из VB-шной программы фонтовый коммондиалог, и посмотри, что там.

По результатам будет ясно, откуда ноги растут у нового поведения, но в любом случае, то, что это никак не отражено в документации (и то, что поведение, на которое рассчитывали программы, сломали), говорит о печальном положении дел.


Так на VB-шных программах работает.
Даже, на том варианте, что я прикреплял выше.
На листбоксе и листвьюшке.

Не работает, в частности, для фрейма, т.е. его я могу двигать только кликом по скролу
not_scroll.JPG
not_scroll.JPG (30.57 Кб) Просмотров: 2804

bon818
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 267
Зарегистрирован: 29.08.2009 (Сб) 4:49
Откуда: Ташкент

Re: Колёсико мышки

Сообщение bon818 » 18.12.2021 (Сб) 1:23

kibernetics писал(а):
RNG21 писал(а):А сделать так, чтобы скрол срабатывал по событию mouseover над зоной фрейма?

Код: Выделить всё
CASE WM_SETCURSOR
    SetFocus(wParam) ' wParam = hWnd


Вернуться в Visual Basic 1–6

Кто сейчас на конференции

Сейчас этот форум просматривают: Yandex-бот и гости: 49

    TopList