View previous topic :: View next topic |
Author |
Message |
MHB
Joined: 04 Apr 2005 Posts: 69
|
Posted: Tue Jul 26, 2005 5:17 pm Post subject: Context sensitive columns not only for FocusRow |
|
|
I didn't realize that context sensitive columns only worked on the FocusRow until I tried it. This really doesn't seem very logical. I expected a context sensitive column to be shown whenever a visible row (not just the FocusRow) supported the given column. Would it be possible to get this behaviour, perhaps configurable?
I've implemented the behaviour myself by overriding ColumnInContext and looping through the rows from toprowindex to bottomrowindex and doing a costum check, but for some reason it doesn't work perfectly. When the tree is first shown, the context sensitive column is not displayed until I resize the tree window or expand a node, even though ColumnInContext returns true from the beginning. |
|
Back to top |
|
|
Infralution
Joined: 28 Feb 2005 Posts: 5027
|
Posted: Tue Jul 26, 2005 11:30 pm Post subject: |
|
|
The reason it is not designed the way you suggest is that you would tend to end up with an unworkably large number of columns displayed once you start expanding items. However you can make it this way if you would prefer that.
Try the following code:
Code: |
class CustomVT : Infralution.Controls.VirtualTree.VirtualTree
{
protected override bool ColumnInContext(Column column)
{
Hashtable rows = new Hashtable();
GetRows(TopRowIndex, BottomRowIndex, rows);
for (int i=TopRowIndex; i <= BottomRowIndex; i++)
{
Row row = (Row)rows[i];
RowBinding binding = GetBindingForRow(row);
if (binding != null)
{
if (binding.SupportsColumn(column))
return true;
}
}
return false;
}
protected override void OnLayout(System.Windows.Forms.LayoutEventArgs levent)
{
this.UpdateColumnsContext();
base.OnLayout (levent);
}
}
|
Currently the tree only calls the UpdateColumnsContext method when the focus is changed - which is probably why you couldn't get it to work. By forcing this to be called each time OnLayout is called you get the desired behaviour. We will change this in the next release so that UpdataColumnsContext is called from OnLayout by default. _________________ Infralution Support |
|
Back to top |
|
|
MHB
Joined: 04 Apr 2005 Posts: 69
|
Posted: Wed Jul 27, 2005 9:59 am Post subject: |
|
|
Infralution wrote: | The reason it is not designed the way you suggest is that you would tend to end up with an unworkably large number of columns displayed once you start expanding items. However you can make it this way if you would prefer that. |
I'm only talking about the visible rows, not all expanded rows. At least in my case, most of the time this wouldn't result in too many columns. Otherwise, one could change the behaviour accordingly...
The reason I'm not satisfied using my own implementation is that you could implement a better solution by taking advantage of the fact that you are notified when rows enter and leave the view. Columns can only be added when rows enter the view, and removed when rows leave the view. I have to loop through every visible row each time ColumnInContext is called, just like you exemplified. And I can't really guess how often that is...
Infralution wrote: | Code: |
protected override void OnLayout(System.Windows.Forms.LayoutEventArgs levent)
{
this.UpdateColumnsContext();
base.OnLayout (levent);
}
|
|
Thanks for the suggestion, but this doesn't seem to make any difference! Still, the column is not shown until I expand an item or resize the window. Not even selecting an item that supports the column is enough. |
|
Back to top |
|
|
Infralution
Joined: 28 Feb 2005 Posts: 5027
|
Posted: Wed Jul 27, 2005 10:51 am Post subject: |
|
|
I understand you were only talking about visible rows - however if you have a number of different row types which each have a number of independent columns then you can quickly end up with a huge number of columns displayed if you expand a few items.
We actually implemented the code I posted by modifying the Dataset Browser Sample. It works fine - although it does illustrate the problem I mentioned above of managing large numbers of columns.
If we were to add this as an option in VirtualTree we would have to implement it in pretty much the same way as I posted here - as there is no internal notification of new rows being displayed. As it turns out this seems to perform OK anyway. Obviously however it is not as efficient as the default algorithm which just checks the current FocusRow.
I'm not sure why you were unable to get it to work. I will email you a copy of the modified DataSet Browser sample to play with. _________________ Infralution Support
Last edited by Infralution on Wed Jul 27, 2005 10:43 pm; edited 1 time in total |
|
Back to top |
|
|
MHB
Joined: 04 Apr 2005 Posts: 69
|
Posted: Wed Jul 27, 2005 3:34 pm Post subject: |
|
|
Infralution wrote: | If we were to add this as an option in VirtualTree we would have to implement it in pretty much the same way as I posted here - as there is no internal notification of new rows being displayed. As it turns out this seems to perform OK anyway. Obviously however it is not as efficient as the default algorithm which just checks the current FocusRow. |
Ok, of course it was my assumption there was an internal notification. How else would you update the TopRowIndex, for instance?
Infralution wrote: | I'm not sure why you were unable to get it to work. I will email you a copy of the modified DataSet Browser sample to play with. |
Thanks. I can see it works under those circumstances. I also modified the filebrowser sample to verify that it works using object binding. However, I still sometimes get the problem with my own project. I'll try to narrow it down and make a sample project... Trouble is, my project is getting rather complicated due to the very dynamic nature of the data source! |
|
Back to top |
|
|
MHB
Joined: 04 Apr 2005 Posts: 69
|
Posted: Wed Jul 27, 2005 3:47 pm Post subject: |
|
|
It seems the problem may have been not ending ColumnInContext() by calling base.ColumnInContext()! Apparently it works when I do that, even though base.ColumnInContext() never returns true. This is because I'm using some level of programmatic data binding. |
|
Back to top |
|
|
Infralution
Joined: 28 Feb 2005 Posts: 5027
|
Posted: Wed Jul 27, 2005 10:42 pm Post subject: |
|
|
MHB wrote: | Ok, of course it was my assumption there was an internal notification. How else would you update the TopRowIndex, for instance?
|
Well obviously we know when the TopRowIndex changes - but there are a whole range of other possible causes for new rows to be displayed or previously displayed rows to disappear. For instance:
a. User resizes the window causing more or less items to be displayed
b. User expands or collapses a node
c. Items are added or removed from data bound collections
For this reason we don't calculate upfront what new rows are to be added and which removed - we just calculate in OnLayout what rows should be visible given the current state. _________________ Infralution Support |
|
Back to top |
|
|
MHB
Joined: 04 Apr 2005 Posts: 69
|
Posted: Thu Oct 06, 2005 4:35 pm Post subject: |
|
|
I'm revisiting this subject because I'm having trouble getting something like the above ColumnInContext example to work right. Don't know whether this has been introduced in a recent version... I'm at 2.1.0 now and only just discovered the issue.
Problem is, when ColumnInContext gets called on expanding a row, the rows are not updated yet, and BottomRowIndex reflects the tree before the row is expanded. It's like ColumnInContext gets called a bit early... When collapsing a row, BottomRowIndex seems correct. |
|
Back to top |
|
|
Infralution
Joined: 28 Feb 2005 Posts: 5027
|
Posted: Thu Oct 06, 2005 11:11 pm Post subject: |
|
|
I've just modified the Dataset Browser sample to use the code discussed earlier in this thread by adding the following:
Code: |
public class MyTree : VirtualTree
{
protected override bool ColumnInContext(Column column)
{
Hashtable rows = new Hashtable();
GetRows(TopRowIndex, BottomRowIndex, rows);
for (int i=TopRowIndex; i <= BottomRowIndex; i++)
{
Row row = (Row)rows[i];
RowBinding binding = GetBindingForRow(row);
if (binding != null)
{
if (binding.SupportsColumn(column))
return true;
}
}
return false;
}
}
|
And then changed the designer generated code to use the derived MyTree class. Note that you don't need to override OnLayout anymore because UpdateColumnContext is called by the base method now.
This all works without any problems with the latest version - Maybe try this on your machine. Note that BottomRowIndex is the index of the last displayed row. If you haven't moved the top row (by scrolling) and the size of the window is unchanged then the BottomRowIndex shouldn't change. So the code above only considers displays columns that are in context for the currently displayed rows. If you wanted it to be for all rows then you could change BottomRowIndex to LastRowIndex and TopRowIndex to FirstRowIndex - although this will be much more expensive. _________________ Infralution Support |
|
Back to top |
|
|
MHB
Joined: 04 Apr 2005 Posts: 69
|
Posted: Fri Oct 07, 2005 10:26 am Post subject: |
|
|
I can see that the problem is not exposed by this example, probably because there are a lot of rows in the data source. The problem only seems to occur when the rows of the tree do not cover the entire display area, and an expanded row introduces more rows (and hence BottomRowIndex should be increased), and one of these is the only one supporting some column. I will mail you a sample project exhibiting this behaviour. |
|
Back to top |
|
|
Infralution
Joined: 28 Feb 2005 Posts: 5027
|
Posted: Mon Oct 10, 2005 12:44 am Post subject: |
|
|
Thanks for the sample project. The problem is a "chicken and egg" type thing. When we introduced variable and auto height rows we needed to change the algorithm to calculate the NumVisibleRows property to take into account individual row heights - so that the vertical scrollbar is correct. This is done in OnLayout after the Widgets for each of the rows have been created. So after OnLayout is called NumVisibleRows is correct and so therefore is BottomRowIndex (which depends on this).
The problem is that ColumnInContext (which you are overriding) is called at the beginning of OnLayout and so NumVisibleRows has not yet been recalculated. ColumnInContext has to be called at the beginning of OnLayout because the layout of widgets depends on which columns are currently in context.
The solution is to calculate the bottom row index yourself based on the minimum row height. eg
Code: |
int numVisibleRows = DisplayHeight / MinRowHeight;
int topIndex = TopRowIndex;
int bottomIndex = Math.Min(LastRowIndex, topIndex + numVisibleRows - 1);
|
This will calculate the maximum number of rows that could possibly be visible - which will in most cases be an overestimate - so you may potentially be showing columns for rows that are not yet visible - but in practice I don't think this is likely to be an issue. _________________ Infralution Support |
|
Back to top |
|
|
MHB
Joined: 04 Apr 2005 Posts: 69
|
Posted: Tue Oct 11, 2005 2:11 pm Post subject: |
|
|
Nice explanation
I can't think of a simple better solution either, so I'll stick to the overestimation for the time being, and hope nobody experiences it as a problem. Looks like my demands are approaching the limit of VirtualTree
In theory, shouldn't it be possible to solve this? Perhaps by getting rid of the high level ColumnInContext() that works on all rows at the same time, and instead dynamically determine the supported columns for each row/widget as they are created, generating a list of columns in context in the process? Of course, this will depend on exactly how the widgets are set up, and whether it's possible to add (unsupported) columns to a widget along the way... |
|
Back to top |
|
|
Infralution
Joined: 28 Feb 2005 Posts: 5027
|
Posted: Tue Oct 11, 2005 10:16 pm Post subject: |
|
|
The problem is that the Active "InContext" columns can affect the height of the rows displayed (if AutoFitHeight is true) and thus the number of visible rows. So you have to do this before calculating the NumVisibleRows - but your algorithm for calculating the displayed rows depends on the NumVisibleRows.
The only way you could handle this rigourously would be to override OnLayout and call the base.OnLayout in a loop until the NumVisibleRows does not change - but this seems like overkill and would have an adverse affect on performance. _________________ Infralution Support |
|
Back to top |
|
|
|