WinApi ScrollBar do ListBoxa

0

Witam. Stworzyłem listBoxa opierającego się na liście dwukierunkowej. Dorobiłem do niego scrollbara ale o tym zaraz.
Na liście jednocześnie może wyświetlać się 14 elementów. Chcę poruszać suwakiem tak, aby na zerowej pozycji wyświetlał się pierwszy element jako pierwszy w listboxie, a jak dojadę do końca suwakiem aby na ostatniej pozycji w listboxie wyświetlał się ostatni element.

suwak porusza się względem osi Y w obszarze: (0, 140) i na jego aktualną pozycję wskazuje liczba "int y".
Ilość elementów w liście określa liczba "int ilosc_elementow".
liczba "double piksele" okresla, co ile pixeli ma nastąpić zmiana elementu.

Tak wygląda wzór określający numer elementu jaki ma zostać wyświetlony jako pierwszy na liście:
Na razie przyjmuję, że liczba elementów (x) jest: (x > 14 && x <= 140)

 
piksele = 140 / (ilosc_elementow - 14);
numer_elementu = int( y / piksele ); // to zostaje standardowo zaokrąglone w dół, co powinno dać dobry winik ale nie daje.

Do 34-tego elementu wszystko wyświetla się wyśmienicie, natomiast przy 35-tym elemencie jak przewinę do końca to numer_elementu = 23, czyli o 2 więcej niż ilość elementów. To jest conajmniej straszne :)

Pokazuję parę losowych wyników:

1a. (dobry wynik)
piksele = 46.66
ilosc = 17
y = 86
pierwszy = 1
ostatni = 15

1b. (dobry wynik)
piksele = 46.66
ilosc = 17
y = 140
pierwszy = 3
ostatni = 17

2a. (zły wynik)
piksele = 6.66
ilosc = 35
y = 30
pierwszy = 5 (wynik na kalkulatorze wyszedł 4.5, wiec zaokraglajac w dol powinno wyjsc 4)

2b. (zły wynik)
piksele = 6.66
ilosc = 35
y = 140
pierwszy = 23 (powinno byc 21, bo wynik wyszedl 21.02)

Jeżeli ma ktoś jakieś uwagi to proszę o pomoc. Jednocześnie wiem, że nigdy nie potrafię podać tych informacji co trzeba, więc jak potrzebujecie czegoś konkretnego piszcie.

0

Na pierwszy rzut oka:

Zrób:

numer_elementu = (int)((double /*taka jak piksele*/ )y / piksele); // bo piksele z tego co widzę, zamienia Ci przed dzieleniem na inta
0

Użyj wyłącznie operacji na liczbach int aby nie tracić precyzji (przy double mogą wystąpić drobne wahania związane z ograniczoną precyzją tych liczb i może się okazać, że raz będzie o 1 piksel za dużo a innym razem o 1 piksel za mało). Trzeba odpowiednio poukładać kolejność działań - najpierw dodawać i mnożyć (nie tracisz precyzji), dzielić tylko raz i to na samym końcu (tracisz precyzję, ale wynik i tak musi być zaokrąglony więc jest ok):

numer_elementu = y * (ilosc_elementow - 14) / 140;

1a. 86 * (17 - 14) / 140 = 258 / 140 = 1
1b. 140 * (17 - 14) / 140 = 420 / 140 = 3
2a. 30 * (35 - 14) / 140 = 630 / 140 = 4
2b. 140 * (35 - 14) / 140 = 2940 / 140 = 21

0

juz sobie poradzilem w inny sposób całkowicie eliminując błędy. Obliczyłem na proporcjach procent przewiniętego miejsca na suwaku. Gdy jest w pozycji 0 wynik = 0%, gdy jest w polowie 50% itp. Następnie mając procent przewiniętego miejsca ustawiam numer elementu na x %.
Wtedy tylko symuluję, że ilośc elementów = ilość elementów - 14 (Aby móc wypełnić 14 elementów w listboxie). Wszystko działa jak należy.

Z ciekawości sprawdziłem wasze uwagi i faktycznie były błędy przy dzieleniu.

0

No i źle zrobiłeś. Będzie działać ale nie zawsze dobrze. Skompiluj sobie taki kod to zrozumiesz o co mi chodzi:

#include <iostream>

int main()
{
    double procent = 1.0 / 3.0 * 100.0;
    int wynik = 3.0 * procent / 100.0;
    std::cout << "3 * " << procent << "% = " << wynik << std::endl;
}
0

hmm... A gdybym napisał własną funkcję dzielącą mógłbym dzielić te liczby double? Chodzi mi o konwertowanie double na char*, wykonywanie dzielenia "pisemnego" i z powrotem konwertowanie na double. Wtedy nie miałbym błędu. Co o tym sądzicie?

0

W komputerze liczby można reprezentować ze skończoną precyzją. Np. liczby 1/3 nie da się zaprezentować za pomocą skończonego ułamka dziesiętnego ani binarnego. Nawet gdybyś swoje dzielenie pisemne wykonywał w nieskończoność to i tak nie wyznaczysz dokładnego wyniku - zawszę będzie się dało dopisać na końcu więcej trójek. Tylko odpowiednia kolejność działań gwarantuje dokładny wynik. I tak np. licząc 3 * (1 / 3) trzeba zmienić kolejność działań i wykonać (3 * 1) / 3. Co prawda mógłbyś reprezentować liczby w postaci ułamka zwykłego nie tracąc w ten sposób precyzji ale sprowadza się to właśnie do zmiany kolejności działań. Wtedy tak na prawdę nie wykonujemy dzielenia a tylko zapamiętujemy dzielnik i dzielną w postaci ułamka czyli odkładamy dzielenie na później. Co ci nie pasi w zamianie kolejności działań? Wystarczy dzielić na samym końcu i po problemie.

0
gswidwa napisał(a):

Witam. Stworzyłem listBoxa opierającego się na liście dwukierunkowej. Dorobiłem do niego scrollbara ale o tym zaraz.
Na liście jednocześnie może wyświetlać się 14 elementów. Chcę poruszać suwakiem tak, aby na zerowej pozycji wyświetlał się pierwszy element jako pierwszy w listboxie, a jak dojadę do końca suwakiem aby na ostatniej pozycji w listboxie wyświetlał się ostatni element.

suwak porusza się względem osi Y w obszarze: (0, 140) i na jego aktualną pozycję wskazuje liczba "int y".
Ilość elementów w liście określa liczba "int ilosc_elementow".
liczba "double piksele" okresla, co ile pixeli ma nastąpić zmiana elementu.

Tak wygląda wzór określający numer elementu jaki ma zostać wyświetlony jako pierwszy na liście:
Na razie przyjmuję, że liczba elementów (x) jest: (x > 14 && x <= 140)

 
piksele = 140 / (ilosc_elementow - 14);
numer_elementu = int( y / piksele ); // to zostaje standardowo zaokrąglone w dół, co powinno dać dobry winik ale nie daje.

Do 34-tego elementu wszystko wyświetla się wyśmienicie, natomiast przy 35-tym elemencie jak przewinę do końca to numer_elementu = 23, czyli o 2 więcej niż ilość elementów. To jest conajmniej straszne :)

Pokazuję parę losowych wyników:

1a. (dobry wynik)
piksele = 46.66
ilosc = 17
y = 86
pierwszy = 1
ostatni = 15

1b. (dobry wynik)
piksele = 46.66
ilosc = 17
y = 140
pierwszy = 3
ostatni = 17

2a. (zły wynik)
piksele = 6.66
ilosc = 35
y = 30
pierwszy = 5 (wynik na kalkulatorze wyszedł 4.5, wiec zaokraglajac w dol powinno wyjsc 4)

2b. (zły wynik)
piksele = 6.66
ilosc = 35
y = 140
pierwszy = 23 (powinno byc 21, bo wynik wyszedl 21.02)

Jeżeli ma ktoś jakieś uwagi to proszę o pomoc. Jednocześnie wiem, że nigdy nie potrafię podać tych informacji co trzeba, więc jak potrzebujecie czegoś konkretnego piszcie.

cóż to za improwizacje?
Numerujesz linie a linia ma ustaloną grubość (albo i niekoniecznie).

Potem np. podczas kliknięcia myszą w punkcie x,y, obliczamy linię:
kliknięta_linia = top + y / Dy;

gdzie: top - nr pierwszej widocznej linii;
dla zróżnicowanych wysokości linii trzeba sumować po kolei aż do y.

jakoś tak:
for(ln = top, dy=0; dy < y && ln < maxLn; dy += hLine[ln++]) ;

kliknięta_linia = ln-1;

0

oks poradzilem sobie z problemem. Dziękuję :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1