Professional UI Solutions
Site Map   /  Register
 
 

Forum

Please Log In to post a new message or reply to an existing one. If you are not registered, please register.

NOTE: Some forums may be read-only if you are not currently subscribed to our technical support services.

Forums » Prof-UIS Tech Support » Cell needs to redraw itself Collapse All
Subject Author Date
Offer Har Apr 28, 2009 - 2:18 PM

Dear Support,


We have a CExtGridCell derived class (actually CExtGridCellComboBox derived)


The design we have is very object-oriented, so that the cell observes changes in the data, and if, for example the string to display in the cell changes, it does it by itself. The only problem is that the cell cannot draw itself, nor can it ask somebody to do it for him, which breaks the whole design.


Is there any chance we are missing something? if not, how can this be done, this realy breaks oue whole design.


Thanks,


Ron.

Technical Support May 4, 2009 - 2:08 PM

The details in your last message are important. So, we have new ideas. You have very powerful grid cells and they are the only object which can do updating. It’s OK. There are no problems. Now we need to clarify what is updating? You can save the new information from the network into any of your grid cells. This is simple. The next thing is to repaint the renewed/updated grid cell. But the grid cell does not know it’s location inside the grid window and it does not know whether it’s currently displayed in the grid’s client area. The fastest way to redraw the grid cell in such situation is to do the following:
1) Use the data provider which knows the grid window reference. We described you how to do this with the ready to use source code.
2) Detect whether the grid cell is displayed in the grid’s client area. Solution for this sub task is hidden inside our previous answer. You can ask the grid window for displayed row/column ranges (CExtScrollItemWnd::OnSiwGetVisibleRange()), walk through displayed range, get grid cell pointers using the CExtGridWnd::GridCellGet() method and compare returned pointers with the this pointer of grid cell which is renewed/updated. So, this should enough quickly compute row/column indexes of the grid cell if it’s visible inside the grid’s client area. We suspect your grid cells may know column index. If yes, then the search algorithm is even faster. You can invoke the CExtGridWnd::GridCellRectsGet() method to get the rectangular area of the grid cell in the client coordinates of the grid window for using as parameter of the InvalidateRect() API.

Offer Har May 4, 2009 - 6:23 PM

Dear Support,


Thanks for all you details answers, I think we are ready now to go and implement this solution.


Ron.

Technical Support Apr 29, 2009 - 11:43 AM

Please let us know what you need to repaint in your grid cell class? There are lot of virtual methods in the CExtGridCell class for painting it and its parts. The entire grid cell is painted in two steps using the CExtGridCell::OnPaintBackground() and CExtGridCell::OnPaintForeground() virtual methods. Last one invokes many other CExtGridCell::OnPaint***() virtual methods for painting cell parts.

Offer Har Apr 29, 2009 - 11:49 AM

I new the cell to redraw itself when the data in it changes, all its parts (buttons, text, colors, bitmaps... everything)


So, if I’m in a cell’s scope this will be enough?:


 


void CMyCell::UpdateMe()

{

// Do some changes to cell’s string & color etc.

...

// Now re-paint the cell:

OnPaintBackground();

OnPaintForeground();

}


Thanks,


Ron.

Technical Support Apr 30, 2009 - 6:39 AM

Your method should look like:

void CMyCell::UpdateMe( CExtGridWnd & wndGrid, LONG nColNo, LONG nRowNo, INT nColType = 0, INT nRowType = 0, bool bUpdateNow = false )
{
	// Do some changes to cell’s string & color etc.
	. . .
	If( wndGrid.GetSafeHwnd() == NULL )
		return;
CRect _rcRedraw;
	if( wndGrid.GridCellRectsGet( nColNo, nRowNo, nColType, nRowType, NULL, & rcRedraw ) )
		return; // cell is outside visible range
	wndGrid.InvalidateRect( &rcRedraw );
	if( bUpdateNow )
		wndGrid.UpdateWindow();
}


Offer Har May 3, 2009 - 6:24 AM

Dear Support,


I saw your mail about the steps I need to take to make a cell paint itself.


1. It’s a little hard for me to understand why thei partition between cells and the grid is needed - can give an example?


2. I still don’t understand how your solution to pass the grid wnd class to the cell will do - from your answer, I need also the row, col, ro type, col type tec etc.


3. Why isn’t calling OnPaintBackgound() & OnPaintForground() from the cell enough, as you suggested at the beginning?


Ron.

Technical Support May 3, 2009 - 2:20 PM

As we said, the grid and data provider in Prof-UIS are similar to the view and document in MFC. If you understand the MFC’s document view architecture design, you should understand the design of grids in Prof-UIS. What you need is your grid and data provider classes. We will declare both of them preliminary:

class CYourDataProvider;
class CYourGrid;

Here is the data provider:
// in .h file
class CYourDataProvider : public CExtMDP < CExtGridDataProviderMemory >
{
public:
            DECLARE_DYNCREATE( CYourDataProvider );
            CYourGrid & m_wndGrid;
            CYourDataProvider( CYourGrid & wndGrid )
                        : m_wndGrid( wndGrid )
            {
            }
};

// in .cpp file
IMPLEMENT_DYNCREATE( CYourDataProvider, CExtGridDataProviderMemory );

Here is the grid:
// in .h file
class CYourGrid : public CExtGridWnd
{
public:
            DECLARE_DYNCREATE( CYourGrid );
            virtual CExtGridDataProvider & OnGridQueryDataProvider()
            {
                        ASSERT_VALID( this );
                        if( m_pDataProvider != NULL )
                        {
                                    ASSERT_VALID( m_pDataProvider );
                                    return (*m_pDataProvider);
                        }
                        m_pDataProvider = new CYourDataProvider( *this );
                        ASSERT_VALID( m_pDataProvider );
                        return (*m_pDataProvider);
            }
};

// in .cpp file
IMPLEMENT_DYNCREATE( CYourGrid, CExtGridWnd );

That is enough to let any method of your grid cell classes to invoke the following code:
void CSomeYourGridCell::SomeMethod()
{
. . .
            CYourDataProvider * pDP = STATIC_DOWNCAST( CYourDataProvider, DataProviderGet() );
            CYourGridWnd & wndGrid = pDP->m_wndGrid;
. . .
}
Now your grid cells know the grid window object reference. But we think this approach is not good and you should pass the grid window as parameter. The grid cells does not know their location in the grid window and you may need to repaint entire grid. The grid cells cannot know their location because the row/column insertion and sorting routines will require resetting of location information in many cell objects what should make the grid window million times slower.


Offer Har May 4, 2009 - 7:51 AM

OK, I understand that you do not encourage me to use this solution. Maybe you can help me find other solution for the problem I am facing:


1. I have a control-bar with a grid.


2. Some cells in the grid are drop-list cells or text cells , which preset objects in our system.


3. Objects can be added, removed or renamed.


4. The system is distributed over LAN - the objects can be added/removed/renamed in any station.


This is the problem:


A. In the grid the user selected object X


B. In a remote station somebody renamed object X or removed object X.


C. The cell needs to immediately display the name name of object X, or remove the selection if object X is deleted from the system.


I have observers to assist me, or I can run in a loop and update all cells:


1. If I use observers, then the cell is the one that gets the notification, and this is the problem I am currently facing.


2. If I run in a loop over all cells, I need some virtrual function added to the all base cell class like virtual bool Update(), that will check the cell for changes, and will return true if the grid needs to be refreshed (change in data of the cell), this approach will involoved changing the code in Prof-UIS library which I rather not do.


What do you suggest?


Thanks,


Ron.

Technical Support May 4, 2009 - 11:34 AM

The first thing to check before updating the grid on particular computer is whether the grid is running some in place cell editor using the CExtGridBaseWnd::GetSafeInplaceActiveHwnd() method. If this method returns non-NULL window handle, then you should send the WM_CANCELMODE message to it. Now there is no cell editing and you can update the grid. The row removing will repaint the grid. You can check whether the row to be removed is focused and move focus to the next row using the CExtGridBaseWnd::FocusGet() / CExtGridBaseWnd::FocusSet(). This will keep some row focused in the grid and this is the good style. You don’t need to check whether the row selected. The selection is synchronized automatically. Next task is updating the renamed and/or somehow modified rows. This task should be performed in the different way, not like you described. Please define some message like:

#define WM_SYNCHRONIZE_MY_GRID (WM_USER+123)

Then please add the message handler for this message. The handler method will do the following things:
1) It will invoke the grid’s CExtScrollItemWnd::OnSiwGetVisibleRange() method which returns the CRect object describing ranges of rows and columns currently displayed in grid’s client area. The grid is displaying rows with indices from CRect::top to CRect::bottom (including first and last).
2) Next thing is walk through the rows in range [ CRect::top . . . CRect::bottom ] and update object names and/or their values.
3) Additionally, you can immediately return from the handler method if the grid window has no the WS_VISIBLE standard window style. We will explain you why this is the good idea closer to the bottom of this message.
That’s all what the WM_SYNCHRONIZE_MY_GRID message handler should do. It’s work should always be finished very fast because the number of rows displayed in the grid’s client area is always not large. The next thing is to send the WM_SYNCHRONIZE_MY_GRID message to the grid window everywhere it’s needed. This message should be sent when the grid is scrolled vertically, resized, displayed and when some other computer in your application notified this computer about changes. So, you should handle the WM_VSCROLL, WM_SIZE and WM_SHOWWINDOW messages. Each message handler should invoke the parent class method and then just send the WM_SYNCHRONIZE_MY_GRID message to the grid window. That’s finally all.
But, of course, there are some space for optimizations is left. The good idea is to keep the list of some objects describing the grid modification actions to do in the WM_SYNCHRONIZE_MY_GRID message handler. This message handler method should not do anything if there are no modification commands present in the list.
In conclusion, this design does not need to keep row/column numbers in your grid cell classes. You don’t need to keep the map from row indices to grid objects/rows and this means your grid can be quickly sorted. If you need to bind some additional data to each row, then you can add some outer header column at left or right, make it zero pixels widths and keep grid cell objects of your type there. These header grid cells can describe anything additional related to each grid row. For instance, you may have some Object Name column in your grid but the text strings in this column are just user friendly object names and they are not the internal technical names used in data exchange between computer names in your network. This means you may need to keep internal technical names somewhere and additional zero width header column with your cell objects is the very good place for such technical information. Besides, the row removing and renaming commands handled by the WM_SYNCHRONIZE_MY_GRID message handler may have technical object names and specifications - not the user friendly names. So, when the WM_SYNCHRONIZE_MY_GRID message handler walks through the displayed names, it should compare technical object names in the renaming command description object with the technical object name in you cell object stored in the zero width header column. Each renaming command description object should be removed from the list of commands after handling. This means the list of commands will be empty only when the user will scroll vertically through entire grid. But this is optimal solution because you don’t need to walk through all the grid each time when some object is renamed. Please note, when adding such renaming command into the commands list you should first walk through the command list and remove previous renaming command of the same object. This will allow you to keep the list of commands more compact.

Offer Har May 4, 2009 - 11:47 AM

Dear Support,


I have a problem with the modification of cell’s data as you descripbed it. The grid is a container, it does not know what kinds of cells there are in it - we have a lot of customized cells... they read data from different locations, display different color, images and names. The only way for the grid to update the content of the cells is by asking the cells to do it, that’s why I need some virtual Update function that the cells will invoke.


It’s impossible for anybody to send this message - think of it as a database that have many views - the database does not know who is looking at what piece of data - only the viewers can do that - that’s why we use the subject/observer pattern, so that data will be left alone...


Also, the data is not plain text - there are images, colors, drop lists elemnts that are removed (not a row removed, but an item in a drop-list that was the selected item)


And fianly, this database have a lot of different tables. so each cell type knows how to look at a specif table to decide row to display a row in this table (again, name, color, image, tool-tip and so on). One grid can display data in cell from location table and in another cell data from a user table.


I still think there is no way your design will work in this complex scenario...


Thanks,


Ron.