Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[C++] [WinApi] Odświeżanie elementów w ListView

Ostatnio zmodyfikowano 2021-10-26 17:08
Autor Wiadomość
idepozapalki
Temat założony przez niniejszego użytkownika
[C++] [WinApi] Odświeżanie elementów w ListView
» 2021-06-01 11:50:06
Ten temat powiązany jest z tematem:
https://cpp0x.pl/forum/temat/?id=28512&p=1

Problemem jest odświeżanie progresbaru w ListView służące do pokazania postępu wczytywania pliku.

Cały program z dołączonym CustomPaint().
Program nie działa, ze względu na braki w CustomPaint().

C/C++
//-----------------------------------------------
//
// Program napisany w Microsoft Visual C++ 2019
// Aplikacja napisana w C++ z uzyciem WinApi
//
//-----------------------------------------------
#include <cmath> // do ceil
#include <windows.h>
#include <commctrl.h>
#include <string>
#include <thread>
#include <process.h>  
// wymagane ze względu na _beginthread
#include <vector>
#include <mutex>
#include <condition_variable>

#pragma comment(lib,"comctl32.lib")
// użycie stylu z XP do grafiki do ListView
#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version = '6.0.0.0' processorArchitecture = '*' publicKeyToken = '6595b64144ccf1df' language = '*'\"")

void AddNewItemOnListView( int i );

std::mutex m; // mutex dla sekcji krytycznych
std::condition_variable cv;
std::string data;
int ElementDoPrzetworzenia;
bool ready = false;
bool processed = false;
bool przerwij = false;

// deklaracje globalnych uchwytow i zmiennych
MSG msg;
HWND hWnd;
HINSTANCE hInst;
static HWND hwndListView;
#define ID_LISTVIEW                     1000
LVCOLUMN lvc;
LVITEM lvi;
LPCWSTR ApplicationTitle = L"Aplikacja";
std::wstring fileName = L"C:\1\Testy.txt"; // testowy plik

struct Task {
   
int ElementNr = 0; // numer elementu
   
std::wstring path; // pełna sciezka w formacie wstring
   
std::wstring Produkt; // nazwa produktu
   
int iProgress = 0; // postep
   
bool iStatus = 0; // 1 - jezeli plik został przetworzony
};
static std::vector < Task > StructureInVector; // w wektorze beda przechowywane struktury z zadaniami

// klasa przetwarzająca plik tekstowy
class ProcesModul {
public:
   
std::wstring pathToFile;
   
   
ProcesModul( std::wstring _pathToFile ) {
       
this->pathToFile = _pathToFile;
   
}
   
~ProcesModul() { }
   
   
// metoda wczytujaca plik
   
int start() {
       
RECT rcClient; // Client area of parent window.
       
HANDLE hFile; // Handle of file.
       
DWORD cb; // Size of file and count of bytes read.
       
LPCH pch; // Address of data read from file.
       
LPCH pchTmp; // Temporary pointer.
       
unsigned int wczytanychbajtow = 0;
       
unsigned int filesize;
       
unsigned int progress; // do przechowywania postępu jako liczby całkowitej
        // Ensure that the common control DLL is loaded
       
GetClientRect( hWnd, & rcClient );
       
hFile = CreateFile( L"C:\\1\\Testy2.txt", GENERIC_READ, FILE_SHARE_READ,
       
( LPSECURITY_ATTRIBUTES ) NULL, OPEN_EXISTING,
       
FILE_ATTRIBUTE_NORMAL,( HANDLE ) NULL );
       
if( hFile ==( HANDLE ) INVALID_HANDLE_VALUE )
           
 return FALSE;
       
       
cb = GetFileSize( hFile,( LPDWORD ) NULL ); // rozmiar pliku w bajtach
       
filesize = cb; // zapamiętuje rozmiar pliku
        // Parse the file.
       
pch =( LPCH ) LocalAlloc( LPTR, sizeof( char ) * 2048 );
       
pchTmp = pch;
       
do {
           
ReadFile( hFile, pchTmp, sizeof( char ) * 2048, & cb,( LPOVERLAPPED ) NULL );
           
wczytanychbajtow += cb; // zapamiętaj wczytaną liczbę bajtów
           
progress = std::ceil((( double ) wczytanychbajtow /( double ) filesize ) * 100 ); // ceil żeby nie było ewentualnych małych skoków paska do tyłu
           
StructureInVector.at( 0 ).iProgress = progress; // wpisz w strukturę
       
} while( cb );
       
       
CloseHandle(( HANDLE ) hFile ); // zamknij plik
       
return TRUE;
       
//        MessageBox(NULL, L"Koniec przetwarzania", L"Info", MB_ICONINFORMATION | MB_OK);
   
}
}
;

void worker_thread() {
   
for(;; ) {
       
std::unique_lock < std::mutex > lk( m ); // Poczekaj, az main () wysle ??dane
        //  cv.wait(lk, [] {return ready; }); // przeładwanie wait - to jest to samo co na dole
       
while( !ready ) {
           
cv.wait( lk ); // wait zatrzymuje watek do czasu az cos dostanie
       
}
       
if( przerwij )
           
 break;
       
       
ProcesModul process( StructureInVector.at( ElementDoPrzetworzenia ).path );
       
process.start();
       
data += " po przetworzeniu";
       
processed = true; // Send data back to main()
       
StructureInVector.at( ElementDoPrzetworzenia ).iStatus = true; // element zostal przetworzony
       
lk.unlock();
       
cv.notify_one(); // odblokowuje jeden z wątków znajdujących się w stanie oczekiwania po uprzednim wywołaniu na obiekcie zmiennej warunkowej metody wait()
   
}
}

void startproces() {
    {
       
std::lock_guard < std::mutex > lk( m );
       
ready = true; // dane przygotowane do przetworzenia
   
}
   
// przygotowanie danych dla wątku
   
if( StructureInVector.size() > 0 ) { // jezeli w vectorze jest cos to przetwarzaj    
       
for( int i = 0; i < StructureInVector.size(); i++ ) { // przetwarzaj wszystkie elementy z vectora      
           
if( !StructureInVector.at( i ).iStatus ) { // sprawdz czy plik w strukturze został już przetworzony
               
AddNewItemOnListView( i ); // wyswietl pozycje na liście
               
cv.notify_one(); // odblokowuj dodatkowy wątek
               
std::unique_lock < std::mutex > lk( m );
               
//      cv.wait(lk, [] {return processed; }); // przeładowanie wait - to jest to samo co na dole
               
while( !processed ) {
                   
cv.wait( lk );
               
}
            }
        }
    }
   
// przestawiam flagi pod kolejne zadanie
   
{
       
ready = false;
       
//        processed = false;
   
}
}

//---------------------------------------------------------
//
// Wyciaga rozszerzenie ze sciezki
//
//---------------------------------------------------------
std::wstring GetExtension( const std::wstring & fileName ) {
   
auto pos = fileName.rfind( L"." );
   
if( pos == std::wstring::npos )
       
 pos = - 1;
   
   
return std::wstring( fileName.begin() + pos + 1, fileName.end() );
}

void AddNewItemOnListView( int i ) {
   
TCHAR pszBuffer[ 16 ]; // bufor na cyferke na progresbarze
    // sprawdzam czy w wektorze ze strukturami cos jest
   
if( !StructureInVector.empty() ) { // sprawdzam czy w wektorze ze strukturami cos jest
        // pierwsza kolumna (oznaczona jako 0)
       
lvi.mask = LVIF_PARAM | LVIF_TEXT;
       
lvi.iItem = i;
       
lvi.iSubItem = 0;
       
// tu robię wskaźnik na fileName
       
lvi.pszText = LPTSTR(( LPCTSTR )( StructureInVector.at( i ).path ).c_str() );
       
lvi.lParam = StructureInVector.at( i ).iProgress; // tutaj ustawia sie dlugosc progresbaru wizualnie
       
SendMessageW( hwndListView, LVM_INSERTITEM, 0,( LPARAM ) & lvi );
       
       
// druga kolumna (oznaczona jako 1)
       
lvi.mask = LVIF_TEXT;
       
lvi.iSubItem = 1;
       
lvi.pszText = LPTSTR(( LPCTSTR )( StructureInVector.at( i ).Produkt ).c_str() );
       
SendMessageW( hwndListView, LVM_SETITEM, 0,( LPARAM ) & lvi );
       
       
// pierwsza kolumna (oznaczona jako 2)
        // pasek progresbaru, ale tutaj wpisuje sie tylko jego procentowa wartosc jako cyfre
       
lvi.iSubItem = 2;
       
lvi.pszText = pszBuffer;
       
swprintf_s( pszBuffer, L"%d %%", StructureInVector.at( i ).iProgress );
       
SendMessageW( hwndListView, LVM_SETITEM, 0,( LPARAM ) & lvi );
   
}
}

//---------------------------------------------------------
//
// Funkcja robi opisy na belce nad ListView
//
//---------------------------------------------------------
static void CreateListViewLabels() {
   
//    LVCOLUMN lvc;
    // tutaj są etykietki na belce nad wrzuconymi stringami
   
lvc.mask = LVCF_WIDTH | LVCF_TEXT;
   
// piersza pozycja na pasku
   
lvc.pszText =( LPWSTR ) L"Przetwarzany plik";
   
lvc.cx = 265;
   
SendMessageW( hwndListView, LVM_INSERTCOLUMN, 0,( LPARAM ) & lvc );
   
// druga pozycja na pasku
   
lvc.pszText =( LPWSTR ) L"Produkt";
   
lvc.cx = 60;
   
SendMessageW( hwndListView, LVM_INSERTCOLUMN, 1,( LPARAM ) & lvc );
   
// trzecia pozycja na pasku
   
lvc.pszText =( LPWSTR ) L"Postęp";
   
lvc.cx = 80;
   
SendMessageW( hwndListView, LVM_INSERTCOLUMN, 2,( LPARAM ) & lvc );
}

void AddControls( HWND hWnd ) {
   
hwndListView = CreateWindowEx( 0, WC_LISTVIEW, NULL, LVS_REPORT | WS_TABSTOP | WS_BORDER | WS_CHILD | WS_VISIBLE,
   
10, 10, 600, 250, hWnd,( HMENU ) ID_LISTVIEW, hInst, 0 );
}

//---------------------------------------------------------
//
// Obsluga rzuconych plikow
//
//---------------------------------------------------------
void DropFile( WPARAM wParam ) {
   
TCHAR lpszFile[ MAX_PATH ] = { 0 };
   
UINT uFile = 0;
   
HDROP hDrop =( HDROP ) wParam;
   
int index, count, length;
   
count = DragQueryFile( hDrop, 0xFFFFFFFF, NULL, 0 ); // ilosc rzuconych plikow
    // petla bedzie czytac rzucone pliki
   
for( index = 0; index < count; ++index ) {
       
length = DragQueryFile( hDrop, index, NULL, 0 );
       
if( length > 0 ) {
           
TCHAR * fname = new TCHAR[ static_cast < size_t >( length ) + 1 ]; // rzutuje
           
DragQueryFile( hDrop, index, fname, length + 1 );
           
// konwertuje tchar do wstringa
           
std::wstring sciezka( & fname[ 0 ] );
           
std::wstring rozszerzenie;
           
rozszerzenie = GetExtension( sciezka );
           
// filtruje pliki i do przetwarzania puszczam tylko aptsource
           
if( rozszerzenie == L"txt" ) {
               
               
Task t { 0, sciezka, L"Produkt3", 2 }; // testowe ustawienie 8% na progressbarze i opisy
               
StructureInVector.push_back( t ); // dodaje strukture do vectora
               
startproces();
           
}
           
delete[ ] fname;
       
}
    }
   
DragFinish( hDrop );
}

static LRESULT HandleCustomDraw( NMLVCUSTOMDRAW * pcd ) {
   
TCHAR buffer[ 40 ];
   
switch( pcd->nmcd.dwDrawStage ) {
   
case CDDS_PREPAINT:
       
// Tell the control we are interested in per-item notifications.
        // (We need it just to tell the control we want per-subitem notifications.)
       
return CDRF_DODEFAULT | CDRF_NOTIFYITEMDRAW;
       
       
case( CDDS_ITEM | CDDS_PREPAINT )
            :
// Tell the control we are interested in per-subitem notifications.
           
return CDRF_DODEFAULT | CDRF_NOTIFYSUBITEMDRAW;
       
       
case( CDDS_ITEM | CDDS_SUBITEM | CDDS_PREPAINT )
            :
switch( pcd->iSubItem )
       
{
       
case 1:
           
// obsluga drugiej kolumny danych
            // Customize "priority" column by marking some priority levels with appropriate color.
           
lvi.iSubItem = pcd->iSubItem;
           
lvi.pszText = buffer;
           
lvi.cchTextMax = sizeof( buffer ) / sizeof( buffer[ 0 ] );
           
SendMessage( hwndListView, LVM_GETITEMTEXT, pcd->nmcd.dwItemSpec,( LPARAM ) & lvi ); // LVM_GETITEMTEXT Retrieves the text of a list-view item or subitem.
            // Let the control do the painting itself with the new color.
           
return CDRF_DODEFAULT;
           
       
case 2:
           
// Customize "progress" column. We paint simple progress indicator.
           
int iProgress = pcd->nmcd.lItemlParam;
           
int cx;
           
HDC hdc = pcd->nmcd.hdc;
           
COLORREF clrBack;
           
HBRUSH hBackBrush;
           
HBRUSH hProgressBrush;
           
HBRUSH hOldBrush;
           
HPEN hPen;
           
HPEN hOldPen;
           
RECT rc;
           
           
clrBack = pcd->clrTextBk;
           
if( clrBack == CLR_NONE || clrBack == CLR_DEFAULT )
               
 clrBack = RGB( 255, 255, 255 ); // tło pod progresbarami
           
           
hBackBrush = CreateSolidBrush( clrBack );
           
hProgressBrush = CreateSolidBrush( RGB( 190, 190, 255 ) ); // kolory pasków
           
hPen = CreatePen( PS_SOLID, 0, RGB( 190, 190, 255 ) ); // obwódka progresbaru
           
           
hOldBrush =( HBRUSH ) SelectObject( hdc, hBackBrush );
           
FillRect( hdc, & pcd->nmcd.rc, hBackBrush );
           
           
cx = pcd->nmcd.rc.right - pcd->nmcd.rc.left - 6;
           
if( cx < 0 )
               
 cx = 0;
           
           
rc.left = pcd->nmcd.rc.left + 3; // ustawinie lewego brzegu progressbaru
           
rc.top = pcd->nmcd.rc.top + 2;
           
rc.right = rc.left + cx * iProgress / 100;
           
rc.bottom = pcd->nmcd.rc.bottom - 2;
           
SelectObject( hdc, hProgressBrush );
           
FillRect( hdc, & rc, hProgressBrush ); // wypełnienie progressbaru kolorem
           
           
rc.right = pcd->nmcd.rc.right - 3;
           
SelectObject( hdc, GetStockObject( HOLLOW_BRUSH ) );
           
hOldPen =( HPEN ) SelectObject( hdc, hPen );
           
Rectangle( hdc, rc.left, rc.top, rc.right, rc.bottom ); // rysuje obwódkę na około progressbaru
           
           
lvi.iSubItem = pcd->iSubItem;
           
lvi.pszText = buffer;
           
lvi.cchTextMax = sizeof( buffer ) / sizeof( buffer[ 0 ] );
           
SendMessage( hwndListView, LVM_GETITEMTEXT, pcd->nmcd.dwItemSpec,( LPARAM ) & lvi ); // wrzucenie tekstu
           
DrawText( hdc, buffer, - 1, & rc, DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE | DT_END_ELLIPSIS );
           
           
SelectObject( hdc, hOldBrush );
           
DeleteObject( hProgressBrush );
           
DeleteObject( hBackBrush );
           
SelectObject( hdc, hOldPen );
           
DeleteObject( hPen );
           
           
// Tell the control to not paint as we did so.
           
return CDRF_SKIPDEFAULT; // brak tego nie narysuje nic
       
}
       
break;
   
}
   
// For all unhandled cases, we let the control do the default.
   
return CDRF_DODEFAULT;
}

static void
CustomPaint( HWND hWnd, HDC hDC, RECT * rcDirty, BOOL bErase )
{
   
NMCUSTOMDRAW nmcd; // The custom draw structure
   
LRESULT cdControlMode; // Return value of NM_CUSTOMDRAW for CDDS_PREPAINT
   
LRESULT cdItemMode; // Return value of NM_CUSTOMDRAW for CDDS_ITEM | CDDS_PREPAINT
   
    // Initialize members of the custom draw structure used for all the stages below:
   
nmcd.hdr.hwndFrom = hWnd;
   
nmcd.hdr.idFrom = GetWindowLong( hWnd, GWL_ID );
   
nmcd.hdr.code = NM_CUSTOMDRAW;
   
nmcd.hdc = hDC;
   
   
if( bErase ) {
       
LRESULT cdEraseMode;
       
       
// Send control pre-erase notification:
       
nmcd.dwDrawStage = CDDS_PREERASE;
       
cdEraseMode = SendMessage( GetParent( hWnd ), WM_NOTIFY, nmcd.hdr.code,( LPARAM ) & nmcd );
       
       
if( !( cdEraseMode & CDRF_SKIPDEFAULT ) ) {
           
// Do the erasing:
            // Send control post-erase notification:
           
if( cdEraseMode & CDRF_NOTIFYPOSTERASE ) {
               
nmcd.dwDrawStage = CDDS_POSTERASE;
               
SendMessage( GetParent( hWnd ), WM_NOTIFY, nmcd.hdr.code,( LPARAM ) & nmcd );
           
}
        }
    }
   
   
// Send control pre-paint notification:
   
nmcd.dwDrawStage = CDDS_PREPAINT;
   
GetClientRect( hWnd, & nmcd.rc );
   
cdControlMode = SendMessage( GetParent( hWnd ), WM_NOTIFY, nmcd.hdr.code,( LPARAM ) & nmcd );
   
   
if( !( cdControlMode &( CDRF_SKIPDEFAULT | CDRF_DOERASE ) ) ) {
       
// Do the control (as a whole) painting (e.g. some kind of background or frame)
        // Iterate through all control items.
        // (If the control does not support any items, just omit all this for-loop.)
        //        for (int i = 0; i < lvi.iSubItem ; i++)
       
{
           
// Send item pre-paint notification (if desired by the app):
           
if( cdControlMode & CDRF_NOTIFYITEMDRAW ) {
               
nmcd.dwDrawStage = CDDS_ITEM | CDDS_PREPAINT;
               
// Set some attributes describing the items. The app. can change
                // them to augment painting of the item:
               
nmcd.rc =;
               
               
// Identify the item (in per control specific way) so app. knows what item is augmenting.
               
nmcd.dwItemSpec =;
               
               
// Tell app. if selection box, focus highlight etc. should be painted for the item.
               
nmcd.uItemState =;
               
               
// Fill in also any data the app. may have associated with the
                // item so it can use them for augmenting of the control.
               
nmcd.lItemlParam =;
               
               
// Send the notification.
               
cdItemMode = SendMessage( GetParent( hWnd ), WM_NOTIFY, nmcd.hdr.code,( LPARAM ) & nmcd );
           
}
           
else {
               
cdItemMode = CDRF_DODEFAULT;
           
}
           
           
// Do the item painting (unlesse suppressed by the app.)
           
if( !( cdItemMode & CDRF_SKIPDEFAULT ) ) {
               
// Note you should be ready for a case hdc may have different font selected
                // if (cdItemMode & CDRF_NEWFONT)). In such case you should reset it to
                // the default control font after this particular item is painted:
               
               
                // Do the item (as a whole) painting
                // (e.g. some kind of item background or frame)
               
               
                // If the item is composed from a set of subitems, another similar
                // nested loop would be here to handle them. You would also need
                // yet another variable (cdSubitemMode) and handle the subitems
                // in similar way. nmcd.dwDrawStage would just set CDDS_SUBITEM
                // instead of CDDS_ITEM.
               
               
                // Do item "post-painting"
                // (e.g. paint a focus rectangle if the item is selected and
                // control has a focus).
               
               
                // Send item post-paint notification:
               
if( cdItemMode & CDRF_NOTIFYPOSTPAINT ) {
                   
nmcd.dwDrawStage = CDDS_ITEM | CDDS_POSTERASE;
                   
SendMessage( GetParent( hWnd ), WM_NOTIFY, nmcd.hdr.code,( LPARAM ) & nmcd );
               
}
            }
        }
       
       
// Do control "post-painting":
       
if( !( cdControlMode & CDRF_SKIPPOSTPAINT ) ) {
           
        }
       
       
// Send control post-paint notification:
       
if( cdControlMode & CDRF_NOTIFYPOSTPAINT ) {
           
nmcd.dwDrawStage = CDDS_POSTERASE;
           
SendMessage( GetParent( hWnd ), WM_NOTIFY, nmcd.hdr.code,( LPARAM ) & nmcd );
       
}
    }
}


//---------------------------------------------------------
//
// Procedura obsługująca wiadomości dla okna, wywoływana przez
// MS-Windows przy okazji różnych zdarzeń, które należy obsłużyć
//
//--------------------------------------------------------------
static LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
   
   
   
switch( msg )
   
{
       
   
case WM_ERASEBKGND:
       
return FALSE; // Defer erasing into WM_PAINT
       
   
case WM_PAINT:
       
{
           
PAINTSTRUCT ps;
           
BeginPaint( hWnd, & ps );
           
CustomPaint( hWnd, ps.hdc, & ps.rcPaint, ps.fErase );
           
EndPaint( hWnd, & ps );
           
return 0;
       
}
       
   
case WM_PRINTCLIENT:
       
{
           
RECT rc;
           
GetClientRect( hWnd, & rc );
           
CustomPaint( hWnd,( HDC ) wParam, & rc, TRUE );
           
return 0;
       
}
       
   
case WM_CREATE:
       
AddControls( hWnd );
       
CreateListViewLabels();
       
break;
       
       
// Obsluga drag & drop
   
case WM_DROPFILES: {
           
DropFile( wParam );
           
break;
       
}
       
   
case WM_NOTIFY: {
           
NMHDR * pHdr =( NMHDR * ) lParam;
           
if( pHdr->idFrom == ID_LISTVIEW && pHdr->code == NM_CUSTOMDRAW )
               
 return HandleCustomDraw(( NMLVCUSTOMDRAW * ) pHdr );
           
           
break;
       
}
       
       
// reakcja na zamkniecie okienka
   
case WM_CLOSE:
       
ready = true;
       
przerwij = true;
       
PostQuitMessage( 0 );
       
return 0;
       
       
// komunikat od systemu wyslany po zamknieciu okienka
   
case WM_DESTROY:
       
PostQuitMessage( 0 );
       
return 0;
       
       
default:
       
return DefWindowProc( hWnd, msg, wParam, lParam );
       
break;
   
}
   
return DefWindowProc( hWnd, msg, wParam, lParam );
}

void InitMainWnd() {
   
HINSTANCE hInstance = GetModuleHandle( 0 );
   
WNDCLASSEX wClass = { 0 };
   
ZeroMemory( & wClass, sizeof( WNDCLASSEX ) );
   
wClass.style = CS_HREDRAW | CS_VREDRAW;
   
wClass.cbClsExtra = 0;
   
wClass.cbWndExtra = 0;
   
wClass.cbSize = sizeof( WNDCLASSEX );
   
wClass.hbrBackground =( HBRUSH )( COLOR_BTNFACE + 1 );
   
wClass.hInstance = hInstance;
   
wClass.lpfnWndProc =( WNDPROC ) WndProc;
   
wClass.lpszClassName = L"My Window Class";
   
wClass.hCursor = LoadCursor( 0, IDC_ARROW );
   
wClass.hIcon = LoadIcon( NULL, IDI_APPLICATION );
   
wClass.hIconSm = LoadCursor( NULL, IDC_ARROW );
   
wClass.lpszMenuName = 0;
   
// Rejestrujemy klasę okna w Windows.
   
if( !RegisterClassEx( & wClass ) ) {
       
int nResult = GetLastError();
       
MessageBox( NULL, L"Nie utworzono żadnej klasy!", L"Błąd", MB_ICONERROR );
   
}
}

//---------------------------------------------------------
//
// Funkcja tworząca główne okno
//
//---------------------------------------------------------
HWND CreateMainWnd() {
   
return
   
CreateWindowEx(
   
WS_EX_CLIENTEDGE,
   
L"My Window Class",
   
ApplicationTitle,
   
WS_OVERLAPPED | WS_CAPTION | WS_BORDER | WS_MINIMIZEBOX | WS_SYSMENU,
   
0,
   
0,
   
650,
   
400,
   
NULL,
   
NULL,
   
hInst,
   
NULL );
}

//---------------------------------------------------------
//
// Do obsługi LisView
//
//---------------------------------------------------------
void CreateCommonControls() {
   
// inicjacja klasy pasków chyba sa niepotrzebne narazie
   
INITCOMMONCONTROLSEX cc;
   
cc.dwSize = sizeof( INITCOMMONCONTROLSEX );
   
cc.dwICC = ICC_PROGRESS_CLASS;
   
cc.dwICC = ICC_BAR_CLASSES; // toolbary, statusbary, tooltipy i oczywiście progressbary
   
InitCommonControlsEx( & cc );
}

//---------------------------------------------------------
//
// Główna funkcja w Windows , od której uruchamiany jest program
// kodowanie zależne od ustawień projektu
//
//---------------------------------------------------------
int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int showCmd )
{
   
InitMainWnd(); // startuje głowne okno
   
hWnd = CreateMainWnd(); // create the main window!
   
if( !hWnd ) {
       
int nResult = GetLastError();
       
MessageBox( NULL, L"Nie utworzono żadnego okna!", L"Błąd", MB_ICONERROR );
   
}
   
// wstawiam pierwszy element do listView
   
std::thread worker( worker_thread );
   
data = "Przykladowe dane";
   
{
       
std::lock_guard < std::mutex > lk( m );
   
}
   
CreateCommonControls();
   
DragAcceptFiles( hWnd, true ); // Drag & Drop w starym stylu
    //    Task t{ 0, fileName, L"Produkt2", 8 }; // testowe ustawienie 8% na progressbarze i opisy
    //    StructureInVector.push_back(t); // dodaje strukture do vectora
    //    AddNewItemOnListView(0);
    //    startproces()
   
ShowWindow( hWnd, showCmd ); // Utworzone okno wyświetlamy na ekranie  
   
UpdateWindow( hWnd ); // Uaktualniamy treść okna
   
ZeroMemory( & msg, sizeof( MSG ) );
   
while( msg.message != WM_QUIT ) { // Rozpoczynamy pętlę obsługi wiadomości napływających do naszego okna.
       
if( PeekMessage( & msg, 0, 0, 0, PM_REMOVE ) ) {
           
TranslateMessage( & msg ); // Przekształcamy wiadomość
           
DispatchMessage( & msg ); // Wysyłamy ją do procedury okna
       
}
    }
   
cv.notify_all();
   
worker.join();
   
return 0;
}

W funkcji startproces() dodawana jest nowa pozycja do ListView.
Teoretycznie powinna wyświetlić się zanim wystartuje wątek wczytujący plik.
Praktycznie jednak jest odwrotnie. Podejrzewam, że chodzi o odświeżanie.

Pomysł z progresbar w ListView jest zapożyczony z tego miejsca:

https://www.codeproject.com/Articles/646482/Custom-Controls-in-Win-API-Control-Customization

Mam problem z uruchomieniem funkcji CustomPaint()
Nie wiem jak ją uzupełnić.
P-178688
DejaVu
» 2021-06-01 13:32:33
Podstawowe pytanie brzmi: dlaczego chcesz robić customowe rysowanie w ListView? Z tego co ja kojarzę, to na Internecie jest dużo 'różnych' niby 'działających' kodów, ale żadnego nie udało mi się uruchomić szybko, aby zadziałał poprawnie. Ja walczyłem z 'double buffering', aby lista nie migała przy odświeżaniu/aktualizacji listy. Sądzę więc, że trudno będzie znaleźć Ci działającego gotowca, który dotyka ListView, więc... prawdopodobnie powinieneś po prostu poświęcić tydzień/dwa na czytanie dokumentacji (MSDN/WinAPI/jak działają kontrolki/buforowanie itp), googlanie i eksperymentowanie. Jak zrozumiesz fundamenty to będziesz w stanie coś zaimplementować, co prawdopodobnie zadziała.
P-178689
idepozapalki
Temat założony przez niniejszego użytkownika
» 2021-06-01 15:32:52
Odpowiedź jest dość prosta. Taka forma pokazywania informacji jaka jest w tym przykładzie jest najbardziej odpowiednia do graficznego pokazania efektów pracy programu. Progrebsbar w tym układzie dobrze pokazuje etap na jakim jest analiza pliku, a dodatkowe pola mogą na bieżąco pokazać informacje np o produkcie tak jak zrobiłem to w przykładzie wyżej. Oczywiście dojdzie tam więcej pozycji.

Zgadza się, googlałem trochę i wydaje mi się, że znalazłem najciekawszy przypadek tyle, że nie do końca działający. Coś o podwójnym buforowaniu też tam było.
Rysowanie z tego przykładu przeanalizowałem i wydaje mi się, że rozumiem jak ono działa.
Problem mam z CustomPaint(), bo nie bardzo rozumiem jak to ma praktycznie działać. Do rysowania "progressbaru" (a właściwie to prostokącików) w ListView używałem struktury NMLVCUSTOMDRAW, a przy przerysowywaniu pojawia się NMCUSTOMDRAW i nie bardzo rozumiem na jakiej zasadzie to wszystko ze sobą jest powiązane. Wydawało mi się również, że gdzieś powinien być jakiś timer określający co jaki czas robić to przerysowanie, a jakoś niczego takiego się nie doszukałem i właściwie to nie wiem czy to wszystko i tak nie będzie migać - o czym wspominasz.

Informacje do wyświetlenia - w tym przykładzie akurat procentowy etap wczytywania pliku pojawiają się w strukturze task i to sprawdziłem debugerem. Właściwie to chciałbym uruchomić tylko samo pokazywanie na progressbarze etatu wczytywania pliku.

Z przykładami mam taki problem, że te które znalazłem i działają pokazują właściwie to co chciał autor po kliknięciu w przycisk, ewentaulnie uruchomieniu czegoś. Nie ma płynnego pokazywania. Metoda wczytująca plik to okrojony przykład z MSDN i był w nim użyty standardowy progressbar jednak do ListView on nie pasuje.
P-178690
pekfos
» 2021-06-04 13:56:03
Program nie 'nie działa' tylko się nie kompiluje. Co miało tu być?
C/C++
// Set some attributes describing the items. The app. can change
// them to augment painting of the item:
nmcd.rc =;

// Identify the item (in per control specific way) so app. knows what item is augmenting.
nmcd.dwItemSpec =;

// Tell app. if selection box, focus highlight etc. should be painted for the item.
nmcd.uItemState =;

// Fill in also any data the app. may have associated with the
// item so it can use them for augmenting of the control.
nmcd.lItemlParam =;

Nie widzę w tym kodzie połączenia miedzy progressem ładowania pliku a progressbarem. Jest niby to
C/C++
lvi.lParam = StructureInVector.at( i ).iProgress; // tutaj ustawia sie dlugosc progresbaru wizualnie
ale to się dzieje raz i przed uruchomieniem przetwarzania pliku. Żeby progress bar działał musisz dostarczyć mu aktualne dane i pozwalać na wykonanie kodu rysującego. Jak zmieniłem Twoją zahardkodowaną ścieżkę podaną do CreateFile() na taką do pliku który ma 1.2MB, to aplikacja się zawiesiła na zaskakująco długi czas.
Update: zawieszenie wynika z tego że wątek przetwarza ten sam plik kilkaset razy, bo program zakłada że zależności czasowe między wątkami pójdą po myśli programisty, a tego nie popiera w kodzie żadna synchronizacja. Odpowiednio duży plik i tak zawiesi program, bo zrównoleglenie przetwarzania plików nie działa.
P-178700
idepozapalki
Temat założony przez niniejszego użytkownika
» 2021-06-04 17:26:04
Już się tłumaczę.
Przeczytałem opis na codeproject i doszedłem do błędnego wniosku, że funkcja CustomPaint() i 3 zdarzenia o których pisze autor czyli: case WM_ERASEBKGND, case WM_PAINT, case WM_PRINTCLIENT rozwiążą problem z wyświetlaniem wyników na progressbarze. Okazało się, że byłem w błędzie.

Zakładałem (błędnie), że połączenie pomiędzy progressem który jest wrzucany do struktury podczas ładowania pliku, a progressbarem muszę uzupełnić gdzieś w CustomPaint().

C/C++
lvi.lParam = StructureInVector.at( i ).iProgress; // tutaj ustawia sie dlugosc progresbaru wizualnie

Tak to jest jedyne miejsce gdzie progressbar jest aktywowany i wpisywana jest tam wartość 0. Najprościej byłoby umieścić tą linię w pętli czytania pliku po uaktualnieniu progressu, ale w poprzednim temacie wspominałeś, że wątek jest "zamknięty" na GUI i z tego miejsca graficznie niczego nie pokażę.

Przyznam, że nie sprawdzałem plików większych niż 0,5MB. Na niewielkich plikach liczona była poprawnie procentowa wartość postępu.

Żeby progress bar działał musisz dostarczyć mu aktualne dane i pozwalać na wykonanie kodu rysującego.

W jaki sposób mogę to zrobić?
Czy potrzebuję dodać jakieś nowe zdarzenie, jakaś funkcja ?
Aktualną wartość progressu mogę odczytać ze struktury, ale właściwie nie wiem jak pozwolić mu na rysowanie.
P-178701
pekfos
» 2021-06-04 18:20:16
w poprzednim temacie wspominałeś, że wątek jest "zamknięty" na GUI i z tego miejsca graficznie niczego nie pokażę.
Nie, wspominałem coś całkowicie przeciwnego.

Aktualną wartość progressu mogę odczytać ze struktury, ale właściwie nie wiem jak pozwolić mu na rysowanie.
Musisz nie zawieszać programu. Może nie testuj tego na plikach, na których oczekiwanym wynikiem jest natychmiastowe 100%? Albo w ogóle wywal te pliki, zamiast tego niech zadaniem będzie pętla ze Sleep() i zwiększaniem wyniku o 5% co sekundę. Gdy kod rysujący będzie już mieć możliwość wykonania się, to trzeba jeszcze zadbać żeby w ogóle to zrobił. Przy normalnym progress barze wyzwalaczem do rysowania jest zmiana wartości paska przez SendMessage(), gdy masz własne rysowanie, to pewnie powinieneś przesłać własne zdarzenie, może coś z WM_USER. To raczej lepsze niż wymuszenie przerysowania całego okna.
P-178702
idepozapalki
Temat założony przez niniejszego użytkownika
» 2021-10-26 17:08:11
Zamykam wątek, bo rozwiązałem wszystkie problemy związane z tym tematem.
@DejaVu Dziękuję za rady. Posiedziałem trochę nad dokumentacją i eksperymetnowaniem.
Mam już działające customowe rysowanie w ListView.
Co do 'double buffering' to wystarczy uruchomić wbudowane.
Najprościej zrobić to tak:
C/C++
DWORD exStyle = LVS_EX_DOUBLEBUFFER;
ListView_SetExtendedListViewStyle( hwndListView, exStyle );
Oczywiście można dodać też inne opcje oprócz buforowania o tak:
C/C++
DWORD exStyle = LVS_EX_DOUBLEBUFFER | LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT | LVS_EX_FLATSB;
Sprawdziłem i wszystko działa. Wcześniej robiłem to w inny sposób i działa tylko jedna opcja.

@pekfos
sposób ze sleep zadziałał. Do przerysowywania używałem timera wywoływanego co 50mS i zdarzenia
C/C++
case WM_TIMER: {
   
switch(( UINT ) wParam ) {
   
case IDT_PROGRESS_TIMER: {
           
Update( true ); // przerysowuje ListView
       
}
       
break;
   
}
}
break;
P-179027
« 1 »
  Strona 1 z 1