?
Solved

Determining Row# using Dataview not in default sort order

Posted on 2006-04-21
14
Medium Priority
?
431 Views
Last Modified: 2010-04-23
Not exactly sure how to word it in the Title in 10 words or less.

I am passing a global variable glRecID from a filtered datagrid in one form to another
form that uses the following:

A dataview that is not sorted by RecID...it is sorted by ContactName
Combo box  - ContactName
A Scroll Bar using cmContacts with CurrencyManager BindingContext
A number of text fields using DataBinding to dvContacts


I need to be able to identify the row# of the row containing glRecID = (value)
even though the dataview associated with cmContacts is in a different order.

Once I have the correct row# I can....cmContacts.Position = whatever that row is.
Next time a user scrolls, if they choose to, the cmContacts.position will be in
sequential order.

Correct me if I'm wrong....the value associated with cmContacts.Position is truly
a row#?

Thanks!

Phil Tate
0
Comment
Question by:TSFLLC
  • 7
  • 7
14 Comments
 
LVL 34

Expert Comment

by:Sancler
ID: 16514045
If I've properly understood the scenario here, rather than trying to get the CurrencyManager to find the record I wanted, I would use the data-binding to do that, and then let that set the CurrencyManager's position.  

To do this I would add

   ComboBox.ValueMember = "glRecID"
   ComboBox.DataBindings.Add("SelectedValue", dvContacts, "glRecID")

to the code by which I set the bindings and then use

   ComboBox.SelectedValue = ThePassedValueOf"glRecID"

when the new value arrived.  Changing the selected value of the combobox will point the currency manager to the position of the relevant record without you needing to work out (or even know) what the row# is in the dataview's sort order.

Roger
0
 

Author Comment

by:TSFLLC
ID: 16514779
I think I can work with this.  I never thought about setting the SelectedValue on load of the form
providing I am not adding a new record...that I am displaying an existing record.

The only thing is that I need to set the cmContacts.Position to the current row of the combo box
as it is displayed so that my cmContacts.Position (CurrencyManager) will be pointing to the
current row of the combo box.

Therefore, if someone were to scroll forward/backward to a record, the row position will be
accurate.

?? cmContacts.Position = ComboBox.SelectedIndex

Phil
0
 

Author Comment

by:TSFLLC
ID: 16514802
Also,

Can I temporarily change the databinding to be set to glRecID and then set it back
to the original binding after knowing the row for which I want to place the dvContacts
to?

   Dim xBinding As Binding = ComboBox.DataBindings("contact_id")
   ComboBox.DataBindings.Remove(xBinding)

   ComboBox.ValueMember = "glRecID"
   ComboBox.DataBindings.Add("SelectedValue", dvContacts, "glRecID")
   ComboBox.SelectedValue = ThePassedValueOf"glRecID"

   cmContacts.Position = ComboBox.SelectedIndex

   Dim yBinding As Binding = ComboBox.DataBindings("glRecID")
   ComboBox.DataBindings.Remove(yBinding)

   ComboBox.ValueMember = "contact_id"
   ComboBox.DataBindings.Add("SelectedValue", dvContacts, "contact_id")

0
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 

Author Comment

by:TSFLLC
ID: 16514850
I've loaded this code and am getting the following error on Line 2 where xBinding is removed:

An unhandled exception of type 'System.OutOfMemoryException' occurred in system.windows.forms.dll

Additional information: Error creating window handle.


OutofMemoryException???
0
 
LVL 34

Expert Comment

by:Sancler
ID: 16514920
I'm not sure I've got the scenario right.  Let me describe what I think you want to be happening and, if I'm wrong, let me know in what respects.  Then we can see how best to achieve it.

My assumption was that you'd got two tables with the RecID being common to both - as a primary key in one table and a foreign key in the other table.  I'd assumed that you wanted to pass it from the table in which it was the foreign key to the table in which it was the primary key: in Access terms, a "look-up" table.  Putting some meat on those bones, that the "main" table identified a person by RegID but the details for that person were in dvContacts (or, more properly, in the table from which dvContacts derives).  As you've got dvContacts sorted other than on this key, you can't use the dataview's .Find method to locate the record.

So the thinking behind my suggestion was that you bound the key, as well as the ContactName, to the combobox.  Then - as the same currency manager would be dealing with all references to dvContacts - changing the selected value in the combobox would automatically move the currency manager to the record concerned.

But I now wonder whether my basic understanding was wrong.  It looks, from your later message/s, as though dvContacts has two (what I'm calling "Key") fields - glRecID and contact_id - and that they are different values.

Whilst I was typing all that, your latest message - re out of memory exception - arrived.  I'm  not sure of the detailed cause but (a) it looks like there must be an endless loop somewhere and (b) in any event, I'm not sure - whatever the scenario - the answer is going to be to change bindings mid-stream.

Let's see if, when I better understand the full picture, the best answer is a "binding" one at all.  There are other approaches (though they involve more coding).

By the way, I happened to pick up these messages pretty quickly, but that was coincidence.  It's really my "grass-cutting" afternoon, but I'd just popped in for a coffee.  I might not be quite so prompt with my next reply.

Roger

0
 

Author Comment

by:TSFLLC
ID: 16515100
Thanks Roger.  I have never posted on the weekend so I wasn't sure how much of a response I might get today.


Maybe we should start over.
I realized after I started trying to incorporate the code for changing the databinding that you recommended.....
I have one problem.  glRecID is a global variable I'm setting and the field name glRecID is not in a table which
therefore I can't use in a databinding because it's not in the table?

Here's what I'm doing though.
I have an initial form that has four combo boxes and a datagrid.  The combo boxes are like filter criteria boxes
for the fields in a table I've deemed important in filtering records into this datagrid.  Depending on the 'field description'
chosen from one of these combo boxes...I display a control (text box, (2) text boxes for data range, or another
combo box) below the combo box where the user can enter the 'value' or chose from a secondary combo box to
complete the filter.

Like I said they have four choices.  If they only use the first one then grid.RowFilter = "field_name1.Text = txtbox1.text"
else the RowFilter string gets longer.....1st AND 2nd AND 3rd...etc.
You get the idea.

The datagrid begins with all of the rows of Contacts, for example, until the user starts filtering with the combo boxes.
This form is called BrowseForm.  When the user double-clicks on a row within the grid...my code loads a data entry form
called ContactMgmt.  The table used in BrowseForm is the same table used in ContactMgmt.  
BUT, there are two issues.
1)  The rows get filtered on BrowseForm so I can't get grid.SelectedRow and use it on ContactMgmt.  Right?  The row#
        changes as the grid gets smaller or larger?  Besides, if the change the order by clicking on a field heading they change
        again?  Truly a question for you....I don't know.
2)  I only pass a field called contact_id into a global variable called glRecID and the datatable used in ContactMgmt (although
it is the same table as in BrowseForm) is in contact_description order.

Theoretically I need to do this (just don't know how to do it programatically):
Pass glRecID to ContactMgmt form

Change the sort order of the datatable to contact_id
Find the record....getting the value of the field contact_description (this is the Contact's Full Name)
Change the order back to contact_description
Find the record based on the contact_description
Get the row#
Set cmContacts.Position = row#

Once cmContacts.Position has the correct row#, all of my bindings will populate properly

Prior to yesterday, because this is a new application with VB.net & MSDE, I haven't been working
with indexes at all.  I just created a PRIMARY INDEX (contact_id) and a SECONDARY (contact_description)
I also programmatically started playing with PrimaryKey, DataRow, Find like this:

        dtContact.PrimaryKey = New DataColumn() {dtContact.Columns("contact_id")}
        dtContact.PrimaryKey = New DataColumn() {dtContact.Columns("contact_description")}

        Dim tmpRow As DataRow, tmpValue As String
       
        dvContact.Sort = "contact_id"
        tmpRow = dvsContact.Find(glRecID)
        tmpValue = dtContact.Rows(tmpRow)("contact_description")

        Then change the sort back to contact_description, do the find again
        and identify the row#

But it is just not clicking on how the set up the code string to get a row#.  
If I'm on the right path at all.  I wish I didn't have to do a double-find in order to locate
a row based on one field when it is in the order of another field.

**NOTE - I use this same BrowseForm and subsequent data entry forms in this same way for approximately 15-18 tables/forms.
Each of the data entry forms have a combo box and scroll bar associated with the table....and a button to re-activate the browse form
where they can change the criteria, double-click, and re-activate the data entry form with the newer record.  All of these data entry
forms will have this.  SO, needless to say, this is the foundation of all these forms and extremely important.

Hope that clears things up, Roger.

Thanks!

Phil
0
 
LVL 34

Accepted Solution

by:
Sancler earned 1000 total points
ID: 16515305
Whatever it is that you are passing from BrowseForm to ContactManagement it seems (if I have understood correctly) to be a value which uniquely identifies a record in THE table.  So whether it has been defined as the Primary Key or not doesn't really matter.  Nor does it matter that it starts off being the contact_id from the bound table in the BrowseForm and passes through a global variable called glRecID on its way to the ContactManagement form.  Provided the datatypes of contact_id and glRecID are the same, the value is still, when it reaches ContactManagement, that of the contact_id in the specific, unique record that you want to identify.

So (if I have got it right this time) the code you want on ContactManagment is as follows

   ComboBox.DataSource = dvContacts
   ComboBox.DisplayMember = "contact_description"
   ComboBox.ValueMember = "contact_id"
   ComboBox.DataBindings.Add("SelectedValue", dvContacts, "contact_id")
   ComboBox.SelectedValue = glRecID

You might want to put the last line in some sort of condition - e.g.

   If glRecID <> 0 then ...

- but that depends on whether you will ever be calling (or re-calling) the ContactManagement form other than when a "proper" value has been placed in glRecID.

Then what should happen is along these lines (so far as the observer is concerned: how it all actually happens "behind the scenes" I don't really know).  The .SelectedValue line will cause the combobox to search through its ValueMember values until it finds one equivalent to glRecID.  The (unique) contact_id of the record chosen on the BrowseForm is the one it finds.  It tells the currency manager "this is record that's now been selected".  The currency manager moves its .Position to that record.  As the currency manager's "position index" is in the same order as the dataview's sort order, if the .position is then incremented or decremented the new position is one up, or down, among the records sorted on contact_description.

Hopefully, therefore, you get what you want without having to identify row numbers or change sort orders or bindings.

Just one further thought.  I wonder if using a global variable for passing the contact_id value is the best approach.  An alternative would be to declare a public variable (or property) on your ContactManagement form and set that when you call it from the BrowseForm.  On these lines

On the ContactManagement form

   Public myContact_id as Long 'or whatever

On BrowseForm, in the grid's DoubleClick (or whereever)

   Dim frm As New ContactManagement
   frm.myContact_id = contact_id
   frm.Show

If you did that you would, obviously, need to change the ref to glRecID to a ref to myContact_id.

Just a thought.

Roger
0
 

Author Comment

by:TSFLLC
ID: 16515851
Roger,

I can't believe it is that simple.

All I had to do was modify the combobox.SelectedValue to my RecID and it automatically positioned the
CurrencyManager to that record....even though all of my other fields are bound to the same dataview?

I change the SelectedValue of one of the fields that are bound, and it adjusted them all?  That was too
easy.

Here is my load routine.....JUST THREE EXTRA LINES!!!

    Private Sub ContactMgmt_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        BuildTablesAndViews()
        BuildComboBoxes()
        BindControls()
        If glNewRec = False Then
            Try
                cmboIndex.SelectedValue = glRecID
                RecIDIndex = cmboIndex.SelectedIndex
                VScrollBar1.Value = cmboIndex.SelectedIndex
            Catch ex As Exception
                MsgBox(ex.Message, MsgBoxStyle.OKOnly Or MsgBoxStyle.Critical, Me.Text)
            Finally
            End Try
            dvPropertiesOwned.RowFilter = "owner_id = " & txtContactID.Text
        Else
                cmdClearNew_Click(Nothing, Nothing)
        End If
        VScrollBar1.Maximum = 10000000
    End Sub

If glNewRec = False is what you were talking about regarding setting a condition.

Isn't using glRecID, which is set in my Module1 module  'Public glRecID as Long', basically
the same as 'Public myContact_id as Long' within the ContactMgmt form?  The thing about
it is that glRecID is generic for all of the tables (Contacts, Committees, Meetings, Assessment
Categories, etc.) using this same design format.  

That way all I have to do is paste the code above into each Load of each data entry form and
just change the name of the dataview.

Whatever way this issue is dealt with.....you resolved my problem in a major way!!!

One last question......Does setting VScrollBar.Maximum to the value I set it to cause any real
issues?

        VScrollBar1.Maximum = 10000000

Thanks so much for a Saturday morning resolution!!

Phil
0
 
LVL 34

Expert Comment

by:Sancler
ID: 16516087
Phil

>>
If glNewRec = False is what you were talking about regarding setting a condition.
<<

It looks like it.

>>
Isn't using glRecID, which is set in my Module1 module  'Public glRecID as Long', basically the same as 'Public myContact_id as Long' within the ContactMgmt form?
<<

The end result is the same, yes.  And as you've got a good reason for using a global variable, you should stick with it.  It's just that - personally - I avoid global variables as much as I can.  If there is only one object I want to know about a value, I tend to code so that only that object is told.  But that is not your case.

>>
One last question......Does setting VScrollBar.Maximum to the value I set it to cause any real issues?
<<

Only in GUI terms (and depending how its movements are coded) in that it will (may) give the impression that there's lots more records to scroll through than there really are.  I just wonder why, as you know how many records there are (from the dataview's .Count) you don't set the maximum with reference to that?

Glad it's sorted, and thanks for the points.

>>
I can't believe it is that simple.
<<

Data-binding is pretty powerful/useful, once you get used to it ;-).

Roger
0
 

Author Comment

by:TSFLLC
ID: 16516146
Roger,

I did make the change to VScrollBar1.Maximum = dvContacts.Count

This will be the last issue hopefully to this post.

I have pasted two routines for your review.  The first one is when I click on a button to clear the form.
The second one is if a user uses the scrollbar.  Also, the scrollbar is minimized in size where only the scroll up and
scroll down arrows are visible.  The user clicks each time to move the scrollbar value.

    Private Sub cmdClearNew_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdClearNew.Click
        glNewRec = True
        dvContact.RowFilter = "contact_id = 0"
        dvPropertiesOwned.RowFilter = "owner_id = 0"
        cmboContactType.Focus()
    End Sub

    Private Sub VScrollBar1_Scroll(ByVal sender As System.Object, ByVal e As System.Windows.Forms.ScrollEventArgs) Handles VScrollBar1.Scroll
        Select Case VScrollBar1.Value
            Case Is > RecIDIndex        ' MOVING FORWARD
                If glNewRec = True Then
                    glNewRec = False
                    dvContact.RowFilter = ""
                    cmContact.Position = 0
                    RecIDIndex = 0
                    VScrollBar1.Value = 0
                Else
                    RecIDIndex = VScrollBar1.Value
                    cmContact.Position += 1
                End If
            Case Is < RecIDIndex        ' MOVING BACKWARD
                If glNewRec = True Then
                    glNewRec = False
                    cmContact.Position = 0
                    RecIDIndex = 0
                    VScrollBar1.Value = 0
                Else
                    If VScrollBar1.Value <> -1 Then
                        RecIDIndex = VScrollBar1.Value
                        cmContact.Position -= 1
                    Else
                        RecIDIndex = 0
                        VScrollBar1.Value = 0
                        cmContact.Position = 0
                    End If
                End If
        End Select
        If txtContactID.Text <> "" Then
            dvPropertiesOwned.RowFilter = "owner_id = " & txtContactID.Text
        End If
    End Sub

The code I accepted from you works like a charm in terms of populating the form
initially with a valid record.

If I don't click on the Clear/New button, I can move backwards/forwards with this scroll button for
as long as I want.

If I click on the Clear/New button, decide that I don't want to add a new record, and click on the scroll bar to
redisplay the first record in the dataview....everything goes haywire.

Only 3-4 text fields/combo boxes repopulate with the first record in the dataview.

THEN, if I click on the scrollbar again, it goes into outer space as if it doesn't know what to do.

Is there something I need to be refreshing after resetting the rowfilter = "".  Or is there a different way
to clear my form of data than 'contact_id = 0'...which is not a valid contact_id so that this thing doesn't
go crazy?

Phil

0
 
LVL 34

Expert Comment

by:Sancler
ID: 16517066
Phil

It's difficult to analyse this fully without more detailed knowledge of what controls are on your form and what they're bound to.  For instance, there's obviously more than one dataview involved: to sort of behaviour you describe can result from incopmatible bindings.  And event coding can produce problems - e.g. if a .TextChanged or .SelectedIndexChanged sub tries to reset a .Position or assign a value invalidly.  But I don't think we're really into those abstruser areas here.

My first thought is: why are you using a scrollbar with just the up and down arrows showing?  The more "usual" approach would be to have two buttons - one "Forward", one "Back - containing the code (at its simplest)

   cm.Position += 1 'or -=1

I appreciate that, with the possibility of the record being new, you might want to modify that "simplest" of codes, but I don't see any reason for setting and remembering scrollbar values and RecIDIndex.  Even if, for GUI reasons, you still want to use a minimized scrollbar, its values and RecIDIndex look to me to be superfuous in your current code.  If (other than when the user wants to create a new record) s/he wants to move forward all that is necessary is for the currency manager's .Position to be incremented and if s/he wants to move backward decremented.  There is not even any reason to check if incrementing/decrementing will take the currency manager's .Position "out of range" because, if it would do, the increment/decrement will be ignored.

The next point is that if, with databound controls, you want to add a new record that means you have to add a new, empty record to the underlying datasource.  Just clearing all the controls and typing new data in them won't do it.  (In parenthesis, yes, setting the datafilter for the datasource to something that will produce no records will clear the controls - although with some databindings it can produce problems - but that is not, I think, what you actually want.)  If you add a new record to the underlying datasource and then set the currency manager's position to point to that, the controls will clear - except to the extent that any "new" record has a default value in any field that is bound to a control.

In this case the underlying datasource is, I think, dvContact.  So, if glNewRec = True - whether when the form is first loaded or because the user clicks cmdClearNew - the first code you need is

   dvContact.AddNew
   cm.Position = dvContact.Count -1

The first line of that creates the new row in the underlying datasource and the second line moves the currency manager's position to it so that (except in so far as any of the fields has a default value) all the controls should be cleared.  No matter how a dataview is sorted, a new record is always initially added at the end: hence the dvContact.Count -1.  

Then, one of two things should happen.  Either the user decides to save the new record, in which case the code needed is

   cm.EndCurrentEdit()

That will commit the changes to the application's datatable, put the newly committed record in the right position for the sort order of the dataview and move the currency manager's position to it.  The new record in the datatable will eventually need saving to the database but, in the meantime, we are back where we started but with an additional record in our datatable.

Or the user decides not to create a new record at all, in which case the code needed is

   dvcontact.Delete(cm.Position)

and then we are back where we started.  Having deleted the record at its current .Position, the cm.Position will automatically move to the last valid record.  If you want to set it to something different - e.g. cm.Position = 0 - you can do so.

The simplest way of handling these alternative pieces of code is with "Submit" and "Abandon" buttons, but they could be put in other places.  So, for instance, if a new record has been called for and the user then tries to move forward or back you could show a message box asking if the new record is to be saved or abandoned and run whichever code is appropriate depending on the reply.  And you may want to do some validation.  Etc.

But, for the present, my main point is simply that - so far as I can see - your problems are arising from trying to work around (or even fighting against) the databindings rather than making use of them.

Roger
0
 

Author Comment

by:TSFLLC
ID: 16517741
Roger,

I appreciate your 'due diligence' with this issue. To reply to your posting....

1)  I've never liked a First, Next, Previous, Last button on any form.  I've always felt they take up too much
valuable space.  The use of the ScrollBar provides the same functionality as (2) of the four buttons because
they also take advantage of the CurrencyManager.Position.  In the real world, going to the beginning or to
the end of a table doesn't really do anything for you when you have a specific record to edit.  The only purpose
of the ScrollBar is the same as the Next or Previous button....to be able to move forward/backwards if they
accidentally click on record "Smith, Jay" instead of clicking on "Smith, Jack".  Back up one record on the data
entry screen and you're there.  The ScrollBar takes up considerably less space.

And like I've said all along, it works fine until I try to clear the screen and decide to use it instead of adding a
new record.

2)  I haven't used CurrencyManager before.  I didn't realize that if the Position is at 0 and you attempt to go
backwards that it ignores the effort.  I appreciate you sharing that with me.

3)  If you don't have a value to compare to when you click on a ScrollBar...how does VB know whether you
are going forward or backward (unlike having Next and Previous buttons where it's evident the direction you
are moving).

4)  I understand that if you are to add a record that it should be added to the datasource.  But is it the best
policy to go ahead and add a row when a Clear/New button is clicked or when a Save button is clicked.
Perform your validation on the text entered, combo boxes updated and then add the record to the dataview
and update the database?  I my case, with this specific application, only one record is added at a time and
only when the Save button is clicked.

5) "yes, setting the datafilter for the datasource to something that will produce no records will clear the
controls - although with some databindings it can produce problems"  Is it possible that this is my problem?
I have no problem adding the row to the dataview immediately and, if the user changes their mind about
creating a new record, then deleting the row.  No problem at all.  But I explained what happened when
I tried dvContact.AddNew and adjusting the Position.

6) I think the databindings are great!  Until I attempt to clear the form, changing the Position either direction
works like a charm!


Just so you know, I made a copy of this form and stripped out almost everything except a few text fields and
a combo box that are used in the Load statement.  I've included all of the code in the form at the bottom.

Notice in the ContactMgmt_Load....I included the two lines of code regarding adding a new row and then
setting the CurrencyManager position.  It always displays the first record in the table.  If I manually set
cmContact.Position = 45,  it sets it to the appropriate row.  If I set it to -1, first record.  If I set it to
dvContact.Count or dvContact.Count -1...first record.

I also was mislead by a friend of mind, whom I respect for his COM, C++, and VB.Net ability, that when you
add a row in a dataview, that it carries over into your table and all other dataviews.  This is apparently not
true because I did the following and nothing changed:

        MsgBox(dtContact.Rows.Count)
        dvContact.AddNew()
        MsgBox(dtContact.Rows.Count)

I've designed in Access with VBA for 15 years.  I've never seen anything that required so much effort in
my life.  I'm sure you've been in the same situation I'm in right now.  Here it is....after Midnight, Sunday morning
1:00 am and I'm beating away at some ridiculous problem that I already have working with a RecordSet in
Access.

This application I am dealing with took me almost a year to write in Access (and is fully-functional with customers
using it) and I've made the wonderful decision to re-write it in VB.net because it's suppose to have all of this
wonderful functionality.  Right now I don't see that it's worth the effort.  What takes (3) lines of code in VBA seems
to take (10) in VB.net.

Also,  what is combobox_AfterUpdate in VBA (so that you can specifically execute some code when you have manually
changed the value) seems to be nowhere to be found in VB.  The .SelectedIndexChanged or .SelectedValueChanged
seems to be my best bet....only it gets executed on Load even when you don't physically touch the control!  

Give me a break!  I'm done complaining now!

Maybe you'll see something in my code that looks out of kilter.  At this point I'm wasted and need some sleep.

Thanks Roger!  I appreciate your help.

Phil


Imports System.Data
Imports System.Data.SqlClient

Public Class ContactMgmt
    Inherits System.Windows.Forms.Form

    Private daContact As New SqlDataAdapter
    Private dsContact As New DataSet
    Private dtContact As New DataTable
    Private dvContact As New DataView
    Private cmContact As CurrencyManager
    Private RecIDIndex As Integer


#Region " Windows Form Designer generated code "

    Public Sub New()
        MyBase.New()

        'This call is required by the Windows Form Designer.
        InitializeComponent()

        'Add any initialization after the InitializeComponent() call

    End Sub

    'Form overrides dispose to clean up the component list.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    Friend WithEvents Button7 As System.Windows.Forms.Button
    Friend WithEvents cmdClose As System.Windows.Forms.Button
    Friend WithEvents cmdSave As System.Windows.Forms.Button
    Friend WithEvents cmdSaveClose As System.Windows.Forms.Button
    Friend WithEvents cmdClearNew As System.Windows.Forms.Button
    Friend WithEvents cmdDelete As System.Windows.Forms.Button
    Friend WithEvents GroupBox1 As System.Windows.Forms.GroupBox
    Friend WithEvents Label2 As System.Windows.Forms.Label
    Friend WithEvents txtInitial As System.Windows.Forms.TextBox
    Friend WithEvents txtLastName As System.Windows.Forms.TextBox
    Friend WithEvents txtFirstName As System.Windows.Forms.TextBox
    Friend WithEvents txtCity As System.Windows.Forms.TextBox
    Friend WithEvents txtAddress2 As System.Windows.Forms.TextBox
    Friend WithEvents Label29 As System.Windows.Forms.Label
    Friend WithEvents Label30 As System.Windows.Forms.Label
    Friend WithEvents Label31 As System.Windows.Forms.Label
    Friend WithEvents Label32 As System.Windows.Forms.Label
    Friend WithEvents txtAddress1 As System.Windows.Forms.TextBox
    Friend WithEvents txtCompany As System.Windows.Forms.TextBox
    Friend WithEvents txtState As System.Windows.Forms.TextBox
    Friend WithEvents txtZipCode As System.Windows.Forms.TextBox
    Friend WithEvents Button1 As System.Windows.Forms.Button
    Friend WithEvents Label1 As System.Windows.Forms.Label
    Friend WithEvents VScrollBar1 As System.Windows.Forms.VScrollBar
    Friend WithEvents Label40 As System.Windows.Forms.Label
    Friend WithEvents txtContactID As System.Windows.Forms.TextBox
    Friend WithEvents txtContact As System.Windows.Forms.TextBox
    Friend WithEvents cmboIndex As System.Windows.Forms.ComboBox
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.Button7 = New System.Windows.Forms.Button
        Me.cmdClose = New System.Windows.Forms.Button
        Me.cmdSave = New System.Windows.Forms.Button
        Me.cmdSaveClose = New System.Windows.Forms.Button
        Me.cmdClearNew = New System.Windows.Forms.Button
        Me.cmdDelete = New System.Windows.Forms.Button
        Me.GroupBox1 = New System.Windows.Forms.GroupBox
        Me.cmboIndex = New System.Windows.Forms.ComboBox
        Me.Label40 = New System.Windows.Forms.Label
        Me.txtContactID = New System.Windows.Forms.TextBox
        Me.Label2 = New System.Windows.Forms.Label
        Me.txtContact = New System.Windows.Forms.TextBox
        Me.txtInitial = New System.Windows.Forms.TextBox
        Me.txtLastName = New System.Windows.Forms.TextBox
        Me.txtFirstName = New System.Windows.Forms.TextBox
        Me.txtCity = New System.Windows.Forms.TextBox
        Me.txtAddress2 = New System.Windows.Forms.TextBox
        Me.Label29 = New System.Windows.Forms.Label
        Me.Label30 = New System.Windows.Forms.Label
        Me.Label31 = New System.Windows.Forms.Label
        Me.Label32 = New System.Windows.Forms.Label
        Me.txtAddress1 = New System.Windows.Forms.TextBox
        Me.txtCompany = New System.Windows.Forms.TextBox
        Me.txtState = New System.Windows.Forms.TextBox
        Me.txtZipCode = New System.Windows.Forms.TextBox
        Me.Button1 = New System.Windows.Forms.Button
        Me.VScrollBar1 = New System.Windows.Forms.VScrollBar
        Me.Label1 = New System.Windows.Forms.Label
        Me.GroupBox1.SuspendLayout()
        Me.SuspendLayout()
        '
        'Button7
        '
        Me.Button7.Location = New System.Drawing.Point(40, 520)
        Me.Button7.Name = "Button7"
        Me.Button7.Size = New System.Drawing.Size(30, 25)
        Me.Button7.TabIndex = 135
        Me.Button7.Text = "?"
        '
        'cmdClose
        '
        Me.cmdClose.Location = New System.Drawing.Point(765, 520)
        Me.cmdClose.Name = "cmdClose"
        Me.cmdClose.Size = New System.Drawing.Size(95, 25)
        Me.cmdClose.TabIndex = 7
        Me.cmdClose.Text = "Close"
        '
        'cmdSave
        '
        Me.cmdSave.Location = New System.Drawing.Point(555, 520)
        Me.cmdSave.Name = "cmdSave"
        Me.cmdSave.Size = New System.Drawing.Size(95, 25)
        Me.cmdSave.TabIndex = 5
        Me.cmdSave.Text = "Save"
        '
        'cmdSaveClose
        '
        Me.cmdSaveClose.Location = New System.Drawing.Point(660, 520)
        Me.cmdSaveClose.Name = "cmdSaveClose"
        Me.cmdSaveClose.Size = New System.Drawing.Size(95, 25)
        Me.cmdSaveClose.TabIndex = 6
        Me.cmdSaveClose.Text = "Save && Close"
        '
        'cmdClearNew
        '
        Me.cmdClearNew.Location = New System.Drawing.Point(345, 520)
        Me.cmdClearNew.Name = "cmdClearNew"
        Me.cmdClearNew.Size = New System.Drawing.Size(95, 25)
        Me.cmdClearNew.TabIndex = 3
        Me.cmdClearNew.Text = "Clear / New"
        '
        'cmdDelete
        '
        Me.cmdDelete.Location = New System.Drawing.Point(450, 520)
        Me.cmdDelete.Name = "cmdDelete"
        Me.cmdDelete.Size = New System.Drawing.Size(95, 25)
        Me.cmdDelete.TabIndex = 4
        Me.cmdDelete.Text = "Delete"
        '
        'GroupBox1
        '
        Me.GroupBox1.Controls.Add(Me.cmboIndex)
        Me.GroupBox1.Controls.Add(Me.Label40)
        Me.GroupBox1.Controls.Add(Me.txtContactID)
        Me.GroupBox1.Controls.Add(Me.Label2)
        Me.GroupBox1.Controls.Add(Me.txtContact)
        Me.GroupBox1.Controls.Add(Me.txtInitial)
        Me.GroupBox1.Controls.Add(Me.txtLastName)
        Me.GroupBox1.Controls.Add(Me.txtFirstName)
        Me.GroupBox1.Controls.Add(Me.txtCity)
        Me.GroupBox1.Controls.Add(Me.txtAddress2)
        Me.GroupBox1.Controls.Add(Me.Label29)
        Me.GroupBox1.Controls.Add(Me.Label30)
        Me.GroupBox1.Controls.Add(Me.Label31)
        Me.GroupBox1.Controls.Add(Me.Label32)
        Me.GroupBox1.Controls.Add(Me.txtAddress1)
        Me.GroupBox1.Controls.Add(Me.txtCompany)
        Me.GroupBox1.Controls.Add(Me.txtState)
        Me.GroupBox1.Controls.Add(Me.txtZipCode)
        Me.GroupBox1.Controls.Add(Me.Button1)
        Me.GroupBox1.Controls.Add(Me.VScrollBar1)
        Me.GroupBox1.Controls.Add(Me.Label1)
        Me.GroupBox1.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.GroupBox1.Location = New System.Drawing.Point(15, 10)
        Me.GroupBox1.Name = "GroupBox1"
        Me.GroupBox1.Size = New System.Drawing.Size(855, 245)
        Me.GroupBox1.TabIndex = 0
        Me.GroupBox1.TabStop = False
        Me.GroupBox1.Text = "Contact Info"
        '
        'cmboIndex
        '
        Me.cmboIndex.BackColor = System.Drawing.SystemColors.Window
        Me.cmboIndex.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.cmboIndex.Location = New System.Drawing.Point(130, 20)
        Me.cmboIndex.Name = "cmboIndex"
        Me.cmboIndex.Size = New System.Drawing.Size(315, 21)
        Me.cmboIndex.TabIndex = 220
        '
        'Label40
        '
        Me.Label40.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.Label40.Location = New System.Drawing.Point(680, 35)
        Me.Label40.Name = "Label40"
        Me.Label40.Size = New System.Drawing.Size(85, 15)
        Me.Label40.TabIndex = 219
        Me.Label40.Text = "Contact ID"
        Me.Label40.TextAlign = System.Drawing.ContentAlignment.MiddleRight
        '
        'txtContactID
        '
        Me.txtContactID.BackColor = System.Drawing.SystemColors.ActiveBorder
        Me.txtContactID.BorderStyle = System.Windows.Forms.BorderStyle.None
        Me.txtContactID.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.txtContactID.ForeColor = System.Drawing.SystemColors.InactiveCaption
        Me.txtContactID.Location = New System.Drawing.Point(780, 35)
        Me.txtContactID.Name = "txtContactID"
        Me.txtContactID.Size = New System.Drawing.Size(65, 13)
        Me.txtContactID.TabIndex = 218
        Me.txtContactID.TabStop = False
        Me.txtContactID.Text = ""
        '
        'Label2
        '
        Me.Label2.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.Label2.Location = New System.Drawing.Point(25, 85)
        Me.Label2.Name = "Label2"
        Me.Label2.Size = New System.Drawing.Size(95, 16)
        Me.Label2.TabIndex = 217
        Me.Label2.Text = "Contact Index"
        Me.Label2.TextAlign = System.Drawing.ContentAlignment.TopRight
        '
        'txtContact
        '
        Me.txtContact.BackColor = System.Drawing.Color.PaleTurquoise
        Me.txtContact.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.txtContact.Location = New System.Drawing.Point(130, 80)
        Me.txtContact.Name = "txtContact"
        Me.txtContact.Size = New System.Drawing.Size(300, 20)
        Me.txtContact.TabIndex = 5
        Me.txtContact.Text = ""
        '
        'txtInitial
        '
        Me.txtInitial.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.txtInitial.Location = New System.Drawing.Point(295, 105)
        Me.txtInitial.Name = "txtInitial"
        Me.txtInitial.Size = New System.Drawing.Size(29, 20)
        Me.txtInitial.TabIndex = 8
        Me.txtInitial.Text = ""
        '
        'txtLastName
        '
        Me.txtLastName.BackColor = System.Drawing.Color.PaleTurquoise
        Me.txtLastName.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.txtLastName.Location = New System.Drawing.Point(330, 105)
        Me.txtLastName.Name = "txtLastName"
        Me.txtLastName.TabIndex = 9
        Me.txtLastName.Text = ""
        '
        'txtFirstName
        '
        Me.txtFirstName.BackColor = System.Drawing.Color.PaleTurquoise
        Me.txtFirstName.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.txtFirstName.Location = New System.Drawing.Point(190, 105)
        Me.txtFirstName.Name = "txtFirstName"
        Me.txtFirstName.TabIndex = 7
        Me.txtFirstName.Text = ""
        '
        'txtCity
        '
        Me.txtCity.BackColor = System.Drawing.Color.PaleTurquoise
        Me.txtCity.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.txtCity.Location = New System.Drawing.Point(130, 205)
        Me.txtCity.Name = "txtCity"
        Me.txtCity.Size = New System.Drawing.Size(155, 20)
        Me.txtCity.TabIndex = 13
        Me.txtCity.Text = ""
        '
        'txtAddress2
        '
        Me.txtAddress2.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.txtAddress2.Location = New System.Drawing.Point(130, 180)
        Me.txtAddress2.Name = "txtAddress2"
        Me.txtAddress2.Size = New System.Drawing.Size(300, 20)
        Me.txtAddress2.TabIndex = 12
        Me.txtAddress2.Text = ""
        '
        'Label29
        '
        Me.Label29.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.Label29.Location = New System.Drawing.Point(20, 210)
        Me.Label29.Name = "Label29"
        Me.Label29.Size = New System.Drawing.Size(100, 15)
        Me.Label29.TabIndex = 211
        Me.Label29.Text = "City/State/Zip"
        Me.Label29.TextAlign = System.Drawing.ContentAlignment.MiddleRight
        '
        'Label30
        '
        Me.Label30.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.Label30.Location = New System.Drawing.Point(20, 160)
        Me.Label30.Name = "Label30"
        Me.Label30.Size = New System.Drawing.Size(100, 15)
        Me.Label30.TabIndex = 210
        Me.Label30.Text = "Address"
        Me.Label30.TextAlign = System.Drawing.ContentAlignment.MiddleRight
        '
        'Label31
        '
        Me.Label31.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.Label31.Location = New System.Drawing.Point(20, 135)
        Me.Label31.Name = "Label31"
        Me.Label31.Size = New System.Drawing.Size(100, 15)
        Me.Label31.TabIndex = 209
        Me.Label31.Text = "Company"
        Me.Label31.TextAlign = System.Drawing.ContentAlignment.MiddleRight
        '
        'Label32
        '
        Me.Label32.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.Label32.Location = New System.Drawing.Point(20, 110)
        Me.Label32.Name = "Label32"
        Me.Label32.Size = New System.Drawing.Size(100, 15)
        Me.Label32.TabIndex = 208
        Me.Label32.Text = "Name (f/i/l)"
        Me.Label32.TextAlign = System.Drawing.ContentAlignment.MiddleRight
        '
        'txtAddress1
        '
        Me.txtAddress1.BackColor = System.Drawing.Color.PaleTurquoise
        Me.txtAddress1.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.txtAddress1.Location = New System.Drawing.Point(130, 155)
        Me.txtAddress1.Name = "txtAddress1"
        Me.txtAddress1.Size = New System.Drawing.Size(300, 20)
        Me.txtAddress1.TabIndex = 11
        Me.txtAddress1.Text = ""
        '
        'txtCompany
        '
        Me.txtCompany.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.txtCompany.Location = New System.Drawing.Point(130, 130)
        Me.txtCompany.Name = "txtCompany"
        Me.txtCompany.Size = New System.Drawing.Size(300, 20)
        Me.txtCompany.TabIndex = 10
        Me.txtCompany.Text = ""
        '
        'txtState
        '
        Me.txtState.BackColor = System.Drawing.Color.PaleTurquoise
        Me.txtState.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.txtState.Location = New System.Drawing.Point(290, 205)
        Me.txtState.Name = "txtState"
        Me.txtState.Size = New System.Drawing.Size(40, 20)
        Me.txtState.TabIndex = 14
        Me.txtState.Text = ""
        '
        'txtZipCode
        '
        Me.txtZipCode.BackColor = System.Drawing.Color.PaleTurquoise
        Me.txtZipCode.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.txtZipCode.Location = New System.Drawing.Point(335, 205)
        Me.txtZipCode.Name = "txtZipCode"
        Me.txtZipCode.Size = New System.Drawing.Size(95, 20)
        Me.txtZipCode.TabIndex = 15
        Me.txtZipCode.Text = ""
        '
        'Button1
        '
        Me.Button1.Location = New System.Drawing.Point(485, 20)
        Me.Button1.Name = "Button1"
        Me.Button1.Size = New System.Drawing.Size(28, 20)
        Me.Button1.TabIndex = 2
        Me.Button1.Text = "..."
        '
        'VScrollBar1
        '
        Me.VScrollBar1.Location = New System.Drawing.Point(455, 20)
        Me.VScrollBar1.Name = "VScrollBar1"
        Me.VScrollBar1.Size = New System.Drawing.Size(24, 20)
        Me.VScrollBar1.TabIndex = 1
        '
        'Label1
        '
        Me.Label1.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.Label1.Location = New System.Drawing.Point(5, 25)
        Me.Label1.Name = "Label1"
        Me.Label1.Size = New System.Drawing.Size(125, 16)
        Me.Label1.TabIndex = 207
        Me.Label1.Text = "Lookup (Contact Index)"
        '
        'ContactMgmt
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(882, 563)
        Me.Controls.Add(Me.Button7)
        Me.Controls.Add(Me.cmdClose)
        Me.Controls.Add(Me.cmdSave)
        Me.Controls.Add(Me.cmdSaveClose)
        Me.Controls.Add(Me.cmdClearNew)
        Me.Controls.Add(Me.cmdDelete)
        Me.Controls.Add(Me.GroupBox1)
        Me.Name = "ContactMgmt"
        Me.Text = "Contact Entry"
        Me.GroupBox1.ResumeLayout(False)
        Me.ResumeLayout(False)

    End Sub

#End Region
    Private Sub ContactMgmt_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        BuildTablesAndViews()
        BuildComboBoxes()
        BindControls()
        If glNewRec = False Then
            Try
                cmboIndex.SelectedValue = glRecID
                RecIDIndex = cmboIndex.SelectedIndex
                VScrollBar1.Value = cmboIndex.SelectedIndex
            Catch ex As Exception
                MsgBox(ex.Message, MsgBoxStyle.OKOnly Or MsgBoxStyle.Critical, Me.Text)
            Finally
            End Try
        Else
            cmdClearNew_Click(Nothing, Nothing)
        End If
        VScrollBar1.Maximum = dvContact.Count + 100
    End Sub
    Private Sub VScrollBar1_Scroll(ByVal sender As System.Object, ByVal e As System.Windows.Forms.ScrollEventArgs) Handles VScrollBar1.Scroll
        Select Case VScrollBar1.Value
            Case Is > RecIDIndex        ' MOVING FORWARD
                If glNewRec = True Then
                    glNewRec = False
                    cmContact.Position = 0
                    RecIDIndex = 0
                    VScrollBar1.Value = 0
                Else
                    RecIDIndex = VScrollBar1.Value
                    cmContact.Position += 1
                End If
            Case Is < RecIDIndex        ' MOVING BACKWARD
                If glNewRec = True Then
                    glNewRec = False
                    RecIDIndex = 0
                    VScrollBar1.Value = 0
                Else
                    If VScrollBar1.Value <> -1 Then
                        RecIDIndex = VScrollBar1.Value
                        cmContact.Position -= 1
                    Else
                        RecIDIndex = 0
                        VScrollBar1.Value = 0
                        cmContact.Position = 0
                    End If
                End If
        End Select
    End Sub
    Sub BuildTablesAndViews()
        daContact.SelectCommand = New SqlCommand
        daContact.SelectCommand.Connection = TSFConnection
        daContact.SelectCommand.CommandType = CommandType.Text
        daContact.SelectCommand.CommandText = "SELECT * FROM contact ORDER BY contact_index"
        daContact.Fill(dsContact, "contact")
        dtContact = dsContact.Tables("contact")
        dvContact = dtContact.DefaultView
    End Sub
    Sub BuildComboBoxes()
        With cmboIndex
            .DataSource = dvContact
            .ValueMember = "contact_id"
            .DisplayMember = "contact_index"
        End With
    End Sub
    Private Sub BindControls()
        txtContactID.DataBindings.Add("Text", dvContact, "contact_id")
        cmboIndex.DataBindings.Add("SelectedValue", dvContact, "contact_id")
        txtFirstName.DataBindings.Add("Text", dvContact, "first_name")
        txtInitial.DataBindings.Add("Text", dvContact, "initial")
        txtLastName.DataBindings.Add("Text", dvContact, "last_name")
        txtCompany.DataBindings.Add("Text", dvContact, "company")
        txtAddress1.DataBindings.Add("Text", dvContact, "address1")
        txtAddress2.DataBindings.Add("Text", dvContact, "address2")
        txtCity.DataBindings.Add("Text", dvContact, "city")
        txtState.DataBindings.Add("Text", dvContact, "state")
        txtZipCode.DataBindings.Add("Text", dvContact, "zip_code")
        cmContact = CType(Me.BindingContext(dvContact), CurrencyManager)
    End Sub
    Private Sub cmdClearNew_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdClearNew.Click
        glNewRec = True
        dvContact.AddNew()
        cmContact.Position = dvContact.Count - 1
    End Sub
End Class
0
 
LVL 34

Expert Comment

by:Sancler
ID: 16520136
Phil

Sorry about the delay.  I hope you didn't think I'd abandoned this.  But I've had to think/experiment.

First, the main problem.  It is a consequence of binding the combobox's "SelectedValue" to the contact_id in order easily to find the record number passed when the ContactMgmt form is first opened.  It's fine for that purpose but, after that, it interferes with the "normal" processes.  As I said when I first suggested it, it's a technique that's normally used with "look-up" tables, and in that context I've never seen it producing problems.  But in this context - where the binding is to the single table to which you want to make amendments/additions, it (obviously) does.  I'm still not sure of the details, but it seems to stop the currency manager recognising new rows.  That doesn't totally surprise me now I've thought about it.  The various manifestations of binding manager base do have a tendency to "do their own thing" without reporting errors.  As I mentioned, if you try to increment/decrement a currency manager outside its range it simply ignores the instruction.  And, which I haven't mentioned, if you set a currency manager's position to a record which has an invalid value (or a binding that conflicts with valid values or another binding) the currency manager will often, off its own bat, simply move to the nearest valid record it can find.  I'm assuming the present problem is a variation on that latter theme but, as I say, I haven't worked out all the details.  Interestingly, if you press the "New" button twice in your test code, it does move to a new clean record.

So, the solution?  You're going to hate me for this, I'm afraid.  Just remove that binding.  The point is that because - as part of its "normal" binding

        With cmboIndex
            .DataSource = dvContact
            .ValueMember = "contact_id"
            .DisplayMember = "contact_index"
        End With

- its value member is already bound to the dv, setting the combo's selectedvalue to the sought record's id will automatically (through that binding) set the currency manager's .Position to it.  In this context (unlike with a look-up table) the "SelectedValue" binding is superfluous.

On other points.  The scrollbar thing is a matter of personal choice.  I agree "first" and "last" buttons are usually a waste of space.  But I'd personally still go for two buttons (made as small as possible) rather than a scrollbar if I didn't want scrollbar functionality.  But I agree that, if you are sticking with a scrollbar, you do need the extra lines so you know whether the move is forward or back.

On points 4 and 5 - how new records should be added in a data-bound environment - I don't really think its a matter of choice.  The point here is that when a control is data-bound it has (despite appearances sometimes) no existence independent of the datasource to which it is bound.  If a textbox shows "abc" that is because a RECORD shows "abc" in the bound field (or the user has entered "abc" because s/he wants that value to go in a RECORD's bound field).  If you simply clear all the bound controls you are, effectively, setting all fields in the current record to "" or 0 or DBNull or whatever.  The fact that you don't .EndCurrentEdit means that - on the initial clearance of the controls - those changes don't get committed to the datatable but, when those changes are "committed", either by calling .EndCurrentEdit or by navigating to a new record, they will be.  And they will be committed either to an existing record or, if that's not what you want, they have to be committed to a new one.  It is POSSIBLE to clear all the controls, let the user enter all the new values, then validate, then create a new record and then, with code, loop through all the controls and put the relevant values into the relevant field in the new record.  But, to me, that loses the whole point of databinding.  That you create the new record first (which clears the controls) does not mean that you have either to "commit" that new record to the datasource or, even if you do that, that you have then to save it to the database.  It is, by analogy, no more than taking a clean sheet of paper to write a new record on.  You don't have to actually USE it as a new record if you don't like it when it's done.

On your friend's point that adding a new row to a dataview carries over to the datatable and all other dataviews, I agree with that subject to two provisos. First, it will only carry over when the new record is "committed" by an .EndCurrentEdit - whether called explicitly or implicitly by the user navigating to a different record.  Second, it won't carry over to a dataview which is filtered on a condition that the new record does not meet.  A useful analogy here is adding a new record direct to a datatable.  It's a three stage process.

    Dim dr As DataRow = dataTable.NewRow 'equivalent to dv.NewRow
    'code to load values into dr's fields 'equivalent to the user entering values in bound controls
    dataTable.Rows.Add(dr) 'equivalent to .EndCurrentEdit

It's only after stage 3 that the dataTable.Row.Count goes up.

As to moving to VB.NET from Access, I know the feeling.  But I'm not sure it's any more difficult than learning any new language/technique and, for my money, once the hours of screaming at the stupid thing have been got over, it's been worth it.

Roger

0
 
LVL 34

Expert Comment

by:Sancler
ID: 16520157
PS on the SelectedIndexChanged point.  Yes, that it fires on loading does produce difficulties.  So my almost invariable practice is to declare a form level Boolean - Loading - which I set to True when starting to load and only set to False when all loading, data-binding, etc is done and have, as the first line in subs like Combo_SelectedIndexChanged

    If Loading Exit Sub

Roger
0

Featured Post

Get your problem seen by more experts

Be seen. Boost your question’s priority for more expert views and faster solutions

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Article by: Kraeven
Introduction Remote Share is a simple remote sharing tool, enabling you to see, add and remove remote or local shares. The application is written in VB.NET targeting the .NET framework 2.0. The source code and the compiled programs have been in…
The ECB site provides FX rates for major currencies since its inception in 1999 in the form of an XML feed. The files have the following format (reducted for brevity) (CODE) There are three files available HERE (http://www.ecb.europa.eu/stats/exch…
Look below the covers at a subform control , and the form that is inside it. Explore properties and see how easy it is to aggregate, get statistics, and synchronize results for your data. A Microsoft Access subform is used to show relevant calcul…
There may be issues when you are trying to access Outlook or send & receive emails or due to Outlook crash which leads to corrupt or damaged PST file. To eliminate the corruption from your PST file, you need to repair the corrupt Outlook PST file. U…
Suggested Courses

609 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question