Hello!
I have a grid in which I show messages. All messages have a date column and the rows are sorted according to date.
So we have old messages, messages that are for the future and we have "hot" messages which are to be shown today so to speak.
What I want to do is to scroll the grid automatically to get the first message today to appear in the top of the grid.
After a lot of reading in here within this topic, I still couldn't find a solution that worked.
So I am kindly asking for help.
What I have is a grid with many columns but all of them except one is hidden. I have changed the visible cell data using the editor component, and this is done in InitializeRow event.
I am binding the datasource when the form is loaded, and then I use the InitializeLayout for column changes and the InitializeRow for theeditor component work.
I also evaluate which row that should be in the top of the grid in InitializeRow event.
My problems are:
1. I have placed the ScrollIntoViewCode the row after the datasource binding. I get a timing problem the first time I run it since the row to be placed in the top isn't set. Where shall I place the code to functionally all the time. I have used both:
grdNotifications.ActiveRowScrollRegion.FirstRow = ActiveRow_Notifications grdNotifications.ActiveRowScrollRegion.ScrollRowIntoView(ActiveRow_Notifications)
Which one is to prefer?
2. Secondly, when I have a row to scroll for, it looks like the rows are squezed together.
I have set the row height to AutoFixed, but since I have a usercontrol as the editor component, it might be the problem. It looks like the grid returns to standard row height after the scrolling.
I have attached to images that might help out
1) This could very well be a timing issue if you the grid has not yet painted. There are two ways you could get around that.
One would be to wait for the grid to Paint. So use the Paint event of the grid and use a flag so that you only set the FirstRow the first time the event fires. You could even disconnect the event handler after the first Paint if you want.
Another option would be to force the grid to paint earlier, before you set the FirstRow. You could do that by calling grid.Update();
2) It's hard to tell, but it sounds like you are using UltraControlContainerEditor. If that's the case, then when you AutoSize the row height, the grid will call GetPreferredSize on your RenderingControl. So you probably need to override GetPreferredSize and make sure it returns the ideal height for your control.
Thanks Mike!
Just to refer to my first question about the two scrolling functions available. Do you use both firstrow and scrollintoview?
Here is my code:
Private Sub InitGrid_Notifications()
'
Try
' Notifications
Dim Notifications As List(Of NotificationDB) = gManager.GetActiveNotificationsDB(gUserDomain.DomainID, gUser.UserID)
Dim RowIndex As Integer = 0
For i As Integer = 0 To Notifications.Count - 1
Dim Notification As NotificationDB = Notifications.Item(i)
If Notification.TargetDate.ToShortDateString = Date.Now.Date.ToShortDateString Then
RowIndex = i
Exit For
End If
Next
grdNotifications.DataSource = Notifications
Dim argsType As New BeforeRowFilterDropDownEventArgs(grdNotifications.DisplayLayout.Bands(0).Columns("Type"), grdNotifications.Rows, grdNotifications.DisplayLayout.Bands(0).Columns("Type").ValueList)
grdNotifications_BeforeRowFilterDropDown(grdNotifications, argsType)
grdNotifications.ActiveRowScrollRegion.FirstRow = grdNotifications.Rows.Item(RowIndex)
grdNotifications.ActiveRowScrollRegion.ScrollRowIntoView(grdNotifications.Rows.Item(RowIndex))
Catch ex As Exception
MsgBox("InitGrid_Notifications")
End Try
End Sub
This is called when the form is loaded, and once a minute using a timer.
Anyway, it doesn't work as planned...
As you can see, I search for the row to use as first row Before I bind the datasource. When I try to scroll, I guess that I don't have any idea if the InitializeRow events has fired, but I don't know if that affects anything or not.
Ideas?
/Henrik
Hi Henrik,
I'm not quite following the problem with Initialize Row. But there is no reason to both set the FirstRow AND also call ScrollRowIntoView.
Setting FirstRow will scroll so that the row is the first visible row in the ScrollRegion. So it will always be in view. It is possible for FirstRow to fail if your ScrollBounds is set to ScrollToFill, but I think in that case, it scrolls it as close as possible to the top.
ScrollRowIntoView just ensures that the row is somewhere in view - it's a completely arbitrary position, though.
It's not really clear to me exactly what you are trying to do. Getting the index of the row in the data source and then using that index to get the grid row is a bit strange. If your grid is sorted or filtered, then that index will be meaningless, since the order of the rows in the grid will be different from the data source. I think you need to loop through the grid rows, instead, to find the row you want.
Good point there Mike. I missed the filters.
So your suggestion is to loop though the grid in the paint event, find the row that i want to have as the top row, and set the firstrow property?
Yes. Just keep in mind that you only want to do it the first time the grid paints. Unless you want it to happen continuously and not allow the user to scroll at all. Even so, the Paint event is probably not very efficient if you are going to do it continuously. If that's what you need, then I'd say use a timer, but don't start the timer until the first time the grid paints.
OK, it is implemented.
In the paint event, I am looping through the GetFilteredInNonByGroupByRows Collections, since I Believe that it is that one that gives me the visible rows.
I am also using a flag in the pain event, so when I have looped trough the rows of the grid and set which row to be the first one, I am setting the flag to prevent the code from being executed over and over again.
The grid datasource is bound when the form is loaded, or when the timer strikes.
So in the sub routine that binds the datasource, I start that one with setting the flag so that it can enter the paint event again.
It works as far as I can see. The problem is that when I try to set the first row in the grid, in the paint even, the renderingen of the Control fails and I get some sort of undercover error. I get a big red cross over the grid Control.
Do you know why?
Here is my code.
mPaint = True
grdNotifications.DataSource = gManager.GetActiveNotificationsDB(gUserDomain.DomainID, gUser.UserID)
Private Sub grdNotifications_Paint(sender As Object, e As PaintEventArgs) Handles grdNotifications.Paint
If mPaint Then
Dim ActiveRow As UltraGridRow = Nothing
For Each r As UltraGridRow In grdNotifications.Rows.GetFilteredInNonGroupByRows
If CType(r.Cells("TargetDate").Value, Date).ToShortDateString = Date.Now.Date.ToShortDateString Then
ActiveRow = r
grdNotifications.ActiveRowScrollRegion.FirstRow = ActiveRow ' Fails here if not commented out
mPaint = False
MsgBox("grdNotifications_Paint")
Hi,
Okay great sounds good! My intent was to demonstrate that the grid just needed to finish initializing before setting the first row. If you managed to find a simpler solution with this recommendation then by all means stick with it!
If you have any additional questions or if there any parts of your application that you feel are unsafe please send code snippets or your application if possible. Thanks.
Hello Michael!
I implemented your code in my test example and it worked. However, when I implemented the code into my real project I ran into some problems.
The grid that I have in my Project shows one column only, and those column cells are rendered with a usercontrol showing something different. As far as understand, this cell can never get focus and due to that, the afterentereditmode event will not fire. I tried with other approaches like showing more cells but giving them a width of 0, but that doesn't work.
What did solve it was that I replaced the code in your EnterEditModeOnFirstCellInGrid routine with the code that sets the firstrow
That works. . I deleted the event afterentereditmode as well...
So as far as i see it, the begininvoke approach seems to solve this by its own. I am not sure if this is as safe as your complete approach, but it works now.
Please send me some feedback, Before we close this case down.
Good work!
Hello Henrik,
This behavior may very well be a simple timing issue where you are trying to set the first row before the grid has finished initializing. I've worked around this by calling BeginInvoke, placing the first cell in edit mode and then on AfterEnterEditMode I am setting the FirstRow to mimic the click event of your button. There's probably a better way I've just have to regroup with Mike S.
Here is what I added in your sample to make this work. [code]
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
UltraGrid1.BeginInvoke(New MethodInvoker(AddressOf EnterEditModeOnFirstCellInGrid))
Private Sub EnterEditModeOnFirstCellInGrid()
Me.UltraGrid1.Focus()Me.UltraGrid1.ActiveCell = Me.UltraGrid1.Rows(0).Cells(0)Me.UltraGrid1.PerformAction(UltraGridAction.ToggleCellSel, False, False)Me.UltraGrid1.PerformAction(UltraGridAction.EnterEditMode, False, False)
Private Sub UltraGrid1_AfterEnterEditMode(sender As Object, e As EventArgs) Handles UltraGrid1.AfterEnterEditMode
UltraGrid1.ActiveRowScrollRegion.FirstRow = mFirstRow
[code]
Keep in mind that I had to enable cell editing. Before in your initialize layout event you were explicitly setting NoEdit on certain columns.
Please review this and let me know if you have any questions.
Here comes a small sample. It is not taken from the "real" project but the behaviour is the same.
I try to set the FirstRow after the grid has been bound to its datasource but nothing happens. When I click the button, it works.
It is the same call, but I guess that the grid isn't ready for it in the first place.
I set the FirstRow variable in the InitializeRow, so that the FirstRow Always is the last one in the grid.
I have read around 15 pages of comments around this topic on your forum and many people have asked about this solution, but you have given no
working solution to anyone as far as I can see. I think that it would be good if you can explain to all of us if this is possible or not. I understand the
problem since the grid is created async, but if there is a solution that works all the time (not based on loading time, timers etc.), it would be great since this feature is really needed. It doesn't feel great to ask the user to click a button to go to the grid rows that shows the messages of today, or what you now want to scroll to in the first place.
Keeping my fingers crossed!
A part of this code in the paint event is likely causing the Red X. We'll require a sample to further investigate this for you. Will you be able to isolate this behavior and send us sample? Let me know if you have any questions regarding this matter.