|
I want to programmatically invalidate the visible repeater items such that ItemValueNeeded will be fired for each of them. What I have is a transaction register where each repeater item is a transaction that shows a running total on the right. When one transaction is updated, the running balance is recomputed for the affected items in my datatable, but the new values will not automatically be shown for visible items until they are scrolled out of view and back.
One might suggest that a quick n dirty approach would be to get the currently displayed items out of view and then return programmatically, but this is no good if in fact there aren't enough repeater items.
Thanks | | Lance77035 Friday, October 02, 2009 2:14 AM | I have found a viable workaround. When an item is updated that affects other items, the other currently visible items can be updated by:
1) bookmarking the currentItemIndex 2) for each additional displayed item, make the item current 3) manually set the child control's updated value(s) 4) return to the bookmarked currentItemIndex
Here's the code if anyone's interested:
Public Class AccountRegister
Protected ds As DataSet1 Private dv As DataView
Private balance() As Single
Private Sub AccountRegister_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Dim ta As New DataSet1TableAdapters.getregisterTableAdapter ds = New DataSet1 ta.Fill(ds.getregister, 2) ReDim balance(ds.getregister.Rows.Count - 1)
ds.getregister.transaction_nameColumn.DefaultValue = "<new>"
Rebalance()
For r As Integer = 0 To dv.Count - 1 DataRepeater1.AddNew() Next
End Sub
Private Function GetTransactionForRow(ByVal index As Integer) As Transaction Dim row As DataSet1.getregisterRow = dv(index).Row Dim t As New Transaction With t .TransactionId = row.transaction_id .TransactionDate = row.transaction_date If row.Ischeck_numberNull Then .CheckNumber = Nothing Else .CheckNumber = row.check_number End If .TransactionName = row.transaction_name If row.IsstatusNull Then .Status = Nothing Else .Status = row.status End If .Amount = row.amount .Balance = balance(index) If row.Isbudget_main_categoryNull Then .BudgetCategory = Nothing Else .BudgetCategory = row.budget_main_category End If If row.Isbudget_sub_categoryNull Then .BudgetSubcategory = Nothing Else .BudgetSubcategory = row.budget_sub_category End If If row.IsmemoNull Then .Memo = Nothing Else .Memo = row.memo End If End With Return t End Function
Private Sub DataRepeater1_ItemValueNeeded(ByVal sender As Object, ByVal e As Microsoft.VisualBasic.PowerPacks.DataRepeaterItemValueEventArgs) Handles DataRepeater1.ItemValueNeeded e.Value = GetTransactionForRow(e.ItemIndex) End Sub
Private Sub DataRepeater1_ItemValuePushed(ByVal sender As Object, ByVal e As Microsoft.VisualBasic.PowerPacks.DataRepeaterItemValueEventArgs) Handles DataRepeater1.ItemValuePushed
Dim row As DataSet1.getregisterRow = dv(e.ItemIndex).Row Dim ev As Transaction = e.Value If row.transaction_id <> ev.TransactionId Then MsgBox("Transaction ID mismatch on update.", MsgBoxStyle.Critical) Exit Sub End If
'if date or amount changes then a rebal is needed from the new transaction date forward Dim bRebal As Boolean = False, dBeginDate As Date If row.amount <> ev.Amount OrElse row.transaction_date <> ev.TransactionDate Then bRebal = True If row.transaction_date < ev.TransactionDate Then 'date has been moved into the future, start rebal with original date dBeginDate = row.transaction_date Else 'date unchanged, or moved into the past, start with the new date dBeginDate = ev.TransactionDate End If End If
row.BeginEdit() row.amount = ev.Amount row.budget_main_category = ev.BudgetCategory row.budget_sub_category = ev.BudgetSubcategory row.check_number = ev.CheckNumber row.status = ev.Status row.transaction_date = ev.TransactionDate row.transaction_name = ev.TransactionName row.memo = ev.Memo row.EndEdit()
If bRebal Then Rebalance(dBeginDate, ev) End If
End Sub
Private Sub Rebalance(Optional ByVal beginDate As Date = Nothing, Optional ByVal focusTransaction As Transaction = Nothing)
RefreshView() 'date, transid 'to find the first row for the date we want, need a new view
Dim newPosition As Integer = 0 Dim runningBal As Single = 0 Dim iStartRow As Integer If beginDate = Date.MinValue Then iStartRow = 0 Else Dim q1 = From myRow As DataSet1.getregisterRow In dv.Table.Rows _ Where myRow.transaction_date = beginDate _ Order By myRow.transaction_id Ascending _ Select myRow.transaction_id iStartRow = dv.Find(New Object() {beginDate, q1(0)}) If iStartRow > 0 Then runningBal = balance(iStartRow - 1) End If End If For r As Integer = iStartRow To dv.Count - 1 Dim row As DataSet1.getregisterRow = dv(r).Row runningBal += row.amount balance(r) = runningBal If Not IsNothing(focusTransaction) AndAlso row.transaction_id = focusTransaction.TransactionId Then newPosition = r End If Next
'cause balances to refresh in visible display If Not IsNothing(focusTransaction) Then DataRepeater1.CurrentItemIndex = newPosition Dim first As Integer = DataRepeater1.FirstDisplayedItemIndex Dim count As Integer = DataRepeater1.DisplayedItemCount(True) Dim refreshCount As Integer = first + count - newPosition For i As Integer = DataRepeater1.CurrentItemIndex To DataRepeater1.CurrentItemIndex + refreshCount - 1 DataRepeater1.CurrentItemIndex = i With CType(DataRepeater1.CurrentItem.Controls(0), RegisterEntry) .Transaction = GetTransactionForRow(i) End With Next DataRepeater1.CurrentItemIndex = newPosition DataRepeater1.CurrentItem.Controls(0).Focus() End If
End Sub
Private Sub RefreshView() dv = New DataView(ds.getregister, "", "transaction_date, transaction_id", DataViewRowState.CurrentRows) End Sub
End Class - Marked As Answer byLance77035 Sunday, October 11, 2009 5:34 AM
-
| | Lance77035 Sunday, October 11, 2009 5:33 AM | Hello Lance, Thanks for your posting. I am a little confused about your problem, did you mean that you have a aggregate column in your datatable, and when user changes the value in one row, you want the aggregate column updated automatically? I have the following code, and when I navigate to another item and the aggregate column is updated. DataTable dt = new DataTable(); BindingSource bs = new BindingSource(); private void Form2_Load(object sender, EventArgs e) { dt.Columns.Add("name"); dt.Columns.Add("price", typeof(decimal)); dt.Columns.Add("volume", typeof(int)); dt.Columns.Add("total", typeof(decimal), "price * volume"); dt.Rows.Add("item1", 1.25, 3); dt.Rows.Add("item2", 2.25, 8); dt.Rows.Add("item3", 4.25, 6); dt.AcceptChanges(); bs.DataSource = dt; this.textBox1.DataBindings.Add("Text", bs, "name"); this.textBox2.DataBindings.Add("Text", bs, "price"); this.textBox3.DataBindings.Add("Text", bs, "volume"); this.label1.DataBindings.Add("Text", bs, "total"); this.dataRepeater1.DataSource = bs; } Let me know if this works for you. If not, could you please send me a simple project that can reproduce this issue to my mail box(v-rzhang[at]microsoft.com)? Thanks, Rong-Chun Zhang MSDN Subscriber Support in Forum If you have any feedback on our support, please contact msdnmg[at]microsoft.com
Please remember to mark the replies as answers if they help and unmark them if they provide no help. Welcome to the All-In-One Code Framework! If you have any feedback, please tell us. | | Rong-Chun Zhang Friday, October 02, 2009 10:49 AM | Sorry I thought my question was clearer than that. I am using VirtualMode = True, and I alread know how to update the current repeater item. What I was describing is that my repeater items are entries in a transaction register. I think you must know what that is. There is a running balance along the right column of these transactions. If you change the amount on one, the balance on that item and any visible item below it should also change.
My question was how can I effect the update of the visible repeater items other than the one I just updated?
Picture it:
Ten repeater items, each one representing a financial transaction:
9/12/09 | AT&T | -90.00 | 9,910.00 9/13/09 | Target | -50.00 | 9,860.00 9/14/09 | Randall's | -85.00 | 9,775.00 <--- update this repeater row's amount, then this + other visible balances must be redisplayed 9/15/09 | Best Buy | -20.00 | 9,755.00 9/16/09 | Starbucks | -4.00 | 9,751.00 etc...
If I change the Randall's transaction amount to -87.00, I know very well how to display the running balance for that repeater item. My question is how can I cause the other visible items to display their updated column? How can I cause each of the subsequent repeater rows to fire the ItemValueNeeded event, or do something to get those running balance columns updated?
Thanks | | Lance77035 Sunday, October 04, 2009 3:56 AM | Hello Lance,
Thanks for your feeback.
What is the datasource the DataRepeater is bound to? I bind a DataTable to the DataRepeater, it still work with Virtual Mode enabled.
DataTable dt = new DataTable(); BindingSource bs = new BindingSource();
private void Form2_Load(object sender, EventArgs e) { dt.Columns.Add("name"); dt.Columns.Add("price", typeof(decimal)); dt.Columns.Add("volume", typeof(int)); dt.Columns.Add("total", typeof(decimal), "price * volume"); dt.Rows.Add("item1", 1.25, 3); dt.Rows.Add("item2", 2.25, 8); dt.Rows.Add("item3", 4.25, 6); dt.AcceptChanges();
bs.DataSource = dt;
this.dataRepeater1.ItemValueNeeded += new Microsoft.VisualBasic.PowerPacks.DataRepeaterItemValueEventHandler(dataRepeater1_ItemValueNeeded); this.dataRepeater1.ItemValuePushed += new Microsoft.VisualBasic.PowerPacks.DataRepeaterItemValueEventHandler(dataRepeater1_ItemValuePushed); this.dataRepeater1.DataSource = bs; }
void dataRepeater1_ItemValuePushed(object sender, Microsoft.VisualBasic.PowerPacks.DataRepeaterItemValueEventArgs e) { DataRowView emp = dt.DefaultView[e.ItemIndex]; switch (e.Control.Name) { case "textBox1": emp[0] = e.Control.Text; break; case "textBox2": emp[1] = e.Control.Text; break; case "textBox3": emp[2] = e.Control.Text; break; default: MessageBox.Show("Error during ItemValuePushed unexpected control: " + e.Control.Name); break; } }
void dataRepeater1_ItemValueNeeded(object sender, Microsoft.VisualBasic.PowerPacks.DataRepeaterItemValueEventArgs e) { if (e.ItemIndex < dt.Rows.Count) { switch (e.Control.Name) { case "textBox1": e.Value = dt.DefaultView[e.ItemIndex][0].ToString(); break; case "textBox2": e.Value = dt.DefaultView[e.ItemIndex][1].ToString(); break; case "textBox3": e.Value = dt.DefaultView[e.ItemIndex][2].ToString(); break; case "label1": e.Value = dt.DefaultView[e.ItemIndex][3].ToString(); break; } } }
private void button1_Click(object sender, EventArgs e) { dt.Rows[1][2] = this.txtVolumn.Text; }
If it is convenient for you, could you please show us more details about how you bind the DataRepeater? It would be more helpful if you could send me a simple project that can reproduce this issue to my mail box(v-rzhang[at]microsoft.com).
Thanks, Rong-Chun Zhang MSDN Subscriber Support in Forum If you have any feedback on our support, please contact msdnmg[at]microsoft.com
Please remember to mark the replies as answers if they help and unmark them if they provide no help.
Welcome to the All-In-One Code Framework! If you have any feedback, please tell us. | | Rong-Chun Zhang Tuesday, October 06, 2009 11:58 AM | Your code demonstrates how to handle the ItemValueNeeded event, but as I've mentioned I already know how to do that.
This is what I'm trying to ask:
What I'm asking is how can I cause ALL of the currently visible items to be redisplayed in the DataRepeater?
This is my only question. My data source is really beside the point of my question. The reason I'm using VirtualMode = True is because I am not using a bound data source; I wish to call the DataRepeater AddNew myself, and handle the ItemValueNeeded and ItemValuePushed events myself programmatically, not with data binding. But again, that is rather beside the point of my question.
If there are 10 items currently visible, perhaps out of 100 total items, and I've just updated one of those items and handled its ItemValuePushed event, is there a way for me to get the other 9 currently visible items to be redisplayed? I want the values shown in the other 9 visible items to be refreshed somehow--without having to remove and re-add those items to the DataRepeater, or completely rebinding to the datasource. I want to find some way to update the other visible items--to cause and handle THEIR ItemValueNeeded events. I can remove those other visible items from the DataRepeater and re-add them, but that causes screen flashes and other disruptive behaviors that will be unacceptable.
When I update the current item and handle its ItemValuePushed event, I update my proprietary data source manually, which also happens to make certain updates to other rows in that datasource. I need a way to get those OTHER row changes to be reflected in the corresponding visible items in the repeater.
There's just no other way to be clearer about what I wish to do.
Here is the code I currently have. The code in the Rebalance method is what causes the balance field for certain OTHER records to be updated, and attempts to those updated values into the corresponding, currently visible repeater items. This solution to getting those fields updated produces undesirable screen flicker and usability issues. I just want a simple way to get the values updated in the other visible items! That's all!!!
Private Sub AccountRegister_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Dim ta As New DataSet1TableAdapters.getregisterTableAdapter ds = New DataSet1 ta.Fill(ds.getregister, 2) ReDim balance(ds.getregister.Rows.Count - 1)
ds.getregister.transaction_nameColumn.DefaultValue = "<new>"
Rebalance()
For r As Integer = 0 To dv.Count - 1 DataRepeater1.AddNew() Next
End Sub
Private Sub DataRepeater1_ItemValueNeeded(ByVal sender As Object, ByVal e As Microsoft.VisualBasic.PowerPacks.DataRepeaterItemValueEventArgs) Handles DataRepeater1.ItemValueNeeded
Dim row As DataSet1.getregisterRow = dv(e.ItemIndex).Row Dim t As New Transaction With t .TransactionId = row.transaction_id .TransactionDate = row.transaction_date If row.Ischeck_numberNull Then .CheckNumber = Nothing Else .CheckNumber = row.check_number End If .TransactionName = row.transaction_name If row.IsstatusNull Then .Status = Nothing Else .Status = row.status End If .Amount = row.amount .Balance = balance(e.ItemIndex) If row.Isbudget_main_categoryNull Then .BudgetCategory = Nothing Else .BudgetCategory = row.budget_main_category End If If row.Isbudget_sub_categoryNull Then .BudgetSubcategory = Nothing Else .BudgetSubcategory = row.budget_sub_category End If If row.IsmemoNull Then .Memo = Nothing Else .Memo = row.memo End If End With e.Value = t
End Sub
Private Sub DataRepeater1_ItemValuePushed(ByVal sender As Object, ByVal e As Microsoft.VisualBasic.PowerPacks.DataRepeaterItemValueEventArgs) Handles DataRepeater1.ItemValuePushed
Dim row As DataSet1.getregisterRow = dv(e.ItemIndex).Row Dim ev As Transaction = e.Value If row.transaction_id <> ev.TransactionId Then MsgBox("Transaction ID mismatch on update.", MsgBoxStyle.Critical) Exit Sub End If
'if date or amount changes then a rebal is needed from the new transaction date forward Dim bRebal As Boolean = False, dBeginDate As Date If row.amount <> ev.Amount OrElse row.transaction_date <> ev.TransactionDate Then bRebal = True If row.transaction_date < ev.TransactionDate Then 'date has been moved into the future, start rebal with original date dBeginDate = row.transaction_date Else 'date unchanged, or moved into the past, start with the new date dBeginDate = ev.TransactionDate End If End If
row.BeginEdit() row.amount = ev.Amount row.budget_main_category = ev.BudgetCategory row.budget_sub_category = ev.BudgetSubcategory row.check_number = ev.CheckNumber row.status = ev.Status row.transaction_date = ev.TransactionDate row.transaction_name = ev.TransactionName row.memo = ev.Memo row.EndEdit()
If bRebal Then Rebalance(dBeginDate, ev) End If
End Sub
Private Sub Rebalance(Optional ByVal beginDate As Date = Nothing, Optional ByVal focusTransaction As Transaction = Nothing)
RefreshView() 'date, transid 'to find the first row for the date we want, need a new view
Dim newPosition As Integer = 0 Dim runningBal As Single = 0 Dim iStartRow As Integer If beginDate = Date.MinValue Then iStartRow = 0 Else Dim q1 = From myRow As DataSet1.getregisterRow In dv.Table.Rows _ Where myRow.transaction_date = beginDate _ Order By myRow.transaction_id Ascending _ Select myRow.transaction_id iStartRow = dv.Find(New Object() {beginDate, q1(0)}) If iStartRow > 0 Then runningBal = balance(iStartRow - 1) End If End If For r As Integer = iStartRow To dv.Count - 1 Dim row As DataSet1.getregisterRow = dv(r).Row runningBal += row.amount balance(r) = runningBal If Not IsNothing(focusTransaction) AndAlso row.transaction_id = focusTransaction.TransactionId Then newPosition = r End If Next
If Not IsNothing(focusTransaction) Then Me.DoubleBuffered = True Me.SuspendLayout() DataRepeater1.CurrentItemIndex = newPosition Dim first As Integer = DataRepeater1.FirstDisplayedItemIndex Dim count As Integer = DataRepeater1.DisplayedItemCount(True) Dim removeCount As Integer = first + count - newPosition DataRepeater1.Visible = False ''cause balances to refresh in visible display For i As Integer = 1 To removeCount DataRepeater1.RemoveAt(newPosition) Next For i As Integer = 1 To removeCount DataRepeater1.AddNew() Next DataRepeater1.Visible = True DataRepeater1.CurrentItemIndex = newPosition Me.ResumeLayout() End If
End Sub
Private Sub RefreshView() dv = New DataView(ds.getregister, "", "transaction_date, transaction_id", DataViewRowState.CurrentRows) End Sub
| | Lance77035 Sunday, October 11, 2009 4:17 AM | I have found a viable workaround. When an item is updated that affects other items, the other currently visible items can be updated by:
1) bookmarking the currentItemIndex 2) for each additional displayed item, make the item current 3) manually set the child control's updated value(s) 4) return to the bookmarked currentItemIndex
Here's the code if anyone's interested:
Public Class AccountRegister
Protected ds As DataSet1 Private dv As DataView
Private balance() As Single
Private Sub AccountRegister_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Dim ta As New DataSet1TableAdapters.getregisterTableAdapter ds = New DataSet1 ta.Fill(ds.getregister, 2) ReDim balance(ds.getregister.Rows.Count - 1)
ds.getregister.transaction_nameColumn.DefaultValue = "<new>"
Rebalance()
For r As Integer = 0 To dv.Count - 1 DataRepeater1.AddNew() Next
End Sub
Private Function GetTransactionForRow(ByVal index As Integer) As Transaction Dim row As DataSet1.getregisterRow = dv(index).Row Dim t As New Transaction With t .TransactionId = row.transaction_id .TransactionDate = row.transaction_date If row.Ischeck_numberNull Then .CheckNumber = Nothing Else .CheckNumber = row.check_number End If .TransactionName = row.transaction_name If row.IsstatusNull Then .Status = Nothing Else .Status = row.status End If .Amount = row.amount .Balance = balance(index) If row.Isbudget_main_categoryNull Then .BudgetCategory = Nothing Else .BudgetCategory = row.budget_main_category End If If row.Isbudget_sub_categoryNull Then .BudgetSubcategory = Nothing Else .BudgetSubcategory = row.budget_sub_category End If If row.IsmemoNull Then .Memo = Nothing Else .Memo = row.memo End If End With Return t End Function
Private Sub DataRepeater1_ItemValueNeeded(ByVal sender As Object, ByVal e As Microsoft.VisualBasic.PowerPacks.DataRepeaterItemValueEventArgs) Handles DataRepeater1.ItemValueNeeded e.Value = GetTransactionForRow(e.ItemIndex) End Sub
Private Sub DataRepeater1_ItemValuePushed(ByVal sender As Object, ByVal e As Microsoft.VisualBasic.PowerPacks.DataRepeaterItemValueEventArgs) Handles DataRepeater1.ItemValuePushed
Dim row As DataSet1.getregisterRow = dv(e.ItemIndex).Row Dim ev As Transaction = e.Value If row.transaction_id <> ev.TransactionId Then MsgBox("Transaction ID mismatch on update.", MsgBoxStyle.Critical) Exit Sub End If
'if date or amount changes then a rebal is needed from the new transaction date forward Dim bRebal As Boolean = False, dBeginDate As Date If row.amount <> ev.Amount OrElse row.transaction_date <> ev.TransactionDate Then bRebal = True If row.transaction_date < ev.TransactionDate Then 'date has been moved into the future, start rebal with original date dBeginDate = row.transaction_date Else 'date unchanged, or moved into the past, start with the new date dBeginDate = ev.TransactionDate End If End If
row.BeginEdit() row.amount = ev.Amount row.budget_main_category = ev.BudgetCategory row.budget_sub_category = ev.BudgetSubcategory row.check_number = ev.CheckNumber row.status = ev.Status row.transaction_date = ev.TransactionDate row.transaction_name = ev.TransactionName row.memo = ev.Memo row.EndEdit()
If bRebal Then Rebalance(dBeginDate, ev) End If
End Sub
Private Sub Rebalance(Optional ByVal beginDate As Date = Nothing, Optional ByVal focusTransaction As Transaction = Nothing)
RefreshView() 'date, transid 'to find the first row for the date we want, need a new view
Dim newPosition As Integer = 0 Dim runningBal As Single = 0 Dim iStartRow As Integer If beginDate = Date.MinValue Then iStartRow = 0 Else Dim q1 = From myRow As DataSet1.getregisterRow In dv.Table.Rows _ Where myRow.transaction_date = beginDate _ Order By myRow.transaction_id Ascending _ Select myRow.transaction_id iStartRow = dv.Find(New Object() {beginDate, q1(0)}) If iStartRow > 0 Then runningBal = balance(iStartRow - 1) End If End If For r As Integer = iStartRow To dv.Count - 1 Dim row As DataSet1.getregisterRow = dv(r).Row runningBal += row.amount balance(r) = runningBal If Not IsNothing(focusTransaction) AndAlso row.transaction_id = focusTransaction.TransactionId Then newPosition = r End If Next
'cause balances to refresh in visible display If Not IsNothing(focusTransaction) Then DataRepeater1.CurrentItemIndex = newPosition Dim first As Integer = DataRepeater1.FirstDisplayedItemIndex Dim count As Integer = DataRepeater1.DisplayedItemCount(True) Dim refreshCount As Integer = first + count - newPosition For i As Integer = DataRepeater1.CurrentItemIndex To DataRepeater1.CurrentItemIndex + refreshCount - 1 DataRepeater1.CurrentItemIndex = i With CType(DataRepeater1.CurrentItem.Controls(0), RegisterEntry) .Transaction = GetTransactionForRow(i) End With Next DataRepeater1.CurrentItemIndex = newPosition DataRepeater1.CurrentItem.Controls(0).Focus() End If
End Sub
Private Sub RefreshView() dv = New DataView(ds.getregister, "", "transaction_date, transaction_id", DataViewRowState.CurrentRows) End Sub
End Class - Marked As Answer byLance77035 Sunday, October 11, 2009 5:34 AM
-
| | Lance77035 Sunday, October 11, 2009 5:33 AM |
|