Linq DataContext still retrieve "Deleted" object - vb.net

I'm working with a grid bound to Linq Data "Object list".
The grid behave in a "disconnected fashion" it means that when I edit or suppress a line the change is not immediately submitted to the database. There is a separate "save" button that is to be clicked after all changes are made to call the DataContext.SubmitChanges method.
The problem is when i delete a line in the grid, the deleteonsubmit method is well called, but when i rebind the grid just after, the line is still here !
I need a behavior like with Datatables where the lines in the Deleted state are not displayed when the datatable is bound to a grid. Is this possible ?
Addendum
There is not a lot of code, because the GridView use it's automatic behavior (AutoGenerateColumns, AutoGenerateEditButton etc)
Here is the code I use to Delete the Object
<DataObjectMethod(DataObjectMethodType.Delete)> _
Public Sub Supprimer(ByVal code As Integer)
Dim ctx = getDataContext()
Dim leGerant = (From g In ctx.Gerant Where g.CODE = code).FirstOrDefault
ctx.Gerant.DeleteOnSubmit(leGerant)
End Sub
Here is the code I use to select objects
<DataObjectMethod(DataObjectMethodType.Select)> _
Public Function Selectionner()
Dim ctx = getDataContext()
Return From g In ctx.Gerant
End Function
#magnus
You didn't understand my problem. I do call SubmitChanges when the save button is clicked, and the changes take effects well. The problem is not here.
The problem is when i delete a line in the grid, the deleteonsubmit method is well called, but when i rebind the grid just after, the line is still here ! Normally the deleted object shouldn't appear as a line in the GridView, even if I have not submit my changes to the database yet.

DeleteOnSubmit does not 'delete' the item. It marks it as to be deleted on the next submit.
You need to add the submit line so it becomes:
Dim ctx = getDataContext()
Dim leGerant = (From g In ctx.Gerant Where g.CODE = code).FirstOrDefault
ctx.Gerant.DeleteOnSubmit(leGerant)
ctx.SubmitChanges()
Edit: Assuming the ctx is the same instance in both your calls you could try
<DataObjectMethod(DataObjectMethodType.Select)> _
Public Function Selectionner()
Dim ctx = getDataContext()
Dim deleted = ctx.ObjectStateManager
.GetObjectStateEntries(System.Data.EntityState.Deleted).OfType<Gerant>();
Return (From g In ctx.Gerant).Except(deleted);
End Function
This is untested so I am unsure if it will work but it seems to compile.

Related

TableAdapter data has changed but property "has Changes" stays at "false" so udate does nothing

It's mostly in the title...
I'm using VB (obviously, see below), but I'm a total beginner with visual studio.
Here is the test code I'm using (it is a simple test button I designed to test the problem I have in the code elsewhere):
Private Sub Test_Click(sender As Object, e As EventArgs) Handles Test.Click
Dim FDBdataset As New FDBDataSet()
Dim FDBTableAdapter As New FDBDataSetTableAdapters.T_PicturesTableAdapter
For Each row As DataRow In FDBTableAdapter.GetData()
row.BeginEdit()
If row("id").ToString = 58672.ToString Then
row.BeginEdit()
Console.Write("Previous Value = " & row("ImgFileName").ToString)
row("ImgFileName") = "Tagada"
row.EndEdit()
Console.WriteLine(", Current Row Value = " & row("ImgFileName").ToString & " - HasChanges : " & FDBdataset.HasChanges())
End If
Next
FDBTableAdapter.Update(FDBdataset)
The output in the console is:
Previous Value = Aphidecta_obliterata_58672, Current Row Value = Tagada - HasChanges : False
I don't understand what is wrong and how to correct it...
I would be very grateful for some help here !
TableAdapter seems set up correctly, and I can read from it, parse rows; display field values, etc...
Update method reports as being correctly set up by the datasource designer.
Code runs without errors but does not affect the DB content.
Why would you think that FDBdataset has any changes in it? Where are you making any changes to it? You have this:
For Each row As DataRow In FDBTableAdapter.GetData()
and GetData returns a new DataTable, so you're making change to that DataTable completely independent of FDBdataset. Use one or the other but not both.
Based on the code you have there, you don't need the DataSet. You can just use the DataTable returnd by GetData:
Dim adapter As New FDBDataSetTableAdapters.T_PicturesTableAdapter
Dim table = FDBTableAdapter.GetData()
For Each row In table
If row.id = 58672 Then
row.ImgFileName = "Tagada"
End If
Next
adapter.Update(table)
Notice that I tidied up your code a lot too. If you're going to use a typed DataSet then use it.
If you actually do need a DataSet for some reason then make the changes to it, not an unrelated DataTable:
Dim data As New FDBDataSet
Dim adapter As New FDBDataSetTableAdapters.T_PicturesTableAdapter
adapter.Fill(data)
For Each row In data.T_Pictures
If row.id = 58672 Then
row.ImgFileName = "Tagada"
End If
Next
adapter.Update(data)
That second code snippet may need some adjustments but I think it should work.
So, the moral of the story is that the Fill method populates an existing DataTable, which can be part of a DataSet but doesn't have to be, while the GetData method creates a new DataTable, populates it and returns it. That new DataTable is not part of any DataSet.

How to set the CheckBox column in DataGridView value to true

GD All,
I've looking around for a solution to my below challenge.
I have got a form with an unbound datagridview, the dg has one added column that allows user to select a method to be used.
The state of the event is stored in a database and after re-opening the form the code checks if the event is in an 'open' state, if so it compares the previously selected method with the methods in the datagrid and should set the previously activated method to be the 'selected' method.
Yet I can't seem to get this to work unfortunately...
The below code loops through the methods in the dg and compares the values, if it meets the methodID it should set the value to 'True' or to the TrueValue anyway.
This is initialized if the database check returns true and after full initialisation of the form, where session.methodID is a field in the returned LINQ query.
For Each r As DataGridViewRow In dgMethods.Rows
If r.Cells(1).Value = session.methodID Then
Dim c As DataGridViewCheckBoxCell = r.Cells(0)
c.Value = c.TrueValue
End If
Next
Unfortunately, this doesn't set the checkbox to 'Checked'.
The loop runs and evaluates the comparison between r.Cells(1).Value and session.methodID correct and triggers correctly.
The interesting thing is if I do a similar loop after the 'CellContentClick' event it does do exactly what is expected. (the example below sets all checkbox values to checked)
Private Sub dgMethods_CellContentClick(sender As Object, e As DataGridViewCellEventArgs) Handles dgMethods.CellContentClick
'Only single selection allowed, so clear table before submitting new selection
For Each r As DataGridViewRow In dgMethods.Rows
Dim c As DataGridViewCheckBoxCell = r.Cells(0)
c.Value = c.TrueValue
Next
dgMethods.CommitEdit(DataGridViewDataErrorContexts.Commit)
End Sub
So, apparently there is a difference in the state between just calling the loop on the dgMethods and when the dgMethods.CellContentClick event has triggered, yet I do not know which one ?
There are many many post on trying to set the CheckBox column yet I have not been able to get any of them working.
Anybody have any idea ?
I would appreciate your suggestions ?
I was not sure of being undestand your question... but there's s simple way to check and change the state of a chechbox cell in a datagridview:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
For Each dr As DataGridViewRow In DataGridView1.Rows
If CBool(dr.Cells(0).Value) = True Then dr.Cells(0).Value = False : Continue For
If CBool(dr.Cells(0).Value) = False Then dr.Cells(0).Value = True
Next
End Sub
In this example, when you click this button for each row in the datagridview, checks the checkboxcell and set the value to FALSE or TRUE depending of his value.
Hope this helps you.
And let me one more tip. If you get acces to the cells for his name instead of his index use his name, it should helps you avoiding troubles ;)
GD All,
After searching further I came across the following interesting behaviour.
The method selection process is part of a form called 'frmAddEvent', the frmAddEvent form is called from a main form using below routine.
The new form instance is created and afterwards filled using a public sub in the form class called InitializeForm() which uses a GUID parameter to retrieve corresponding data to set the form fields.
If Not (isOpened(rsTankName.unqID)) Then
Dim newForm As New frmAddEvent() '(rsTankName)
newForm.InitializeForm(rsTankName)
newForm.Show()
Else
End If
The initialization sub queries several datatables and sets the corresponding fields in the new form instance correctly if applicable.
Part of that setting is the method selection in the dgMethods datagridview.
It would appear that the sequence in which you call the form makes all the difference as the below code works perfectly:
If Not (isOpened(rsTankName.unqID)) Then
Dim newForm As New frmAddEvent() '(rsTankName)
newForm.Show()
newForm.InitializeForm(rsTankName)
Else
End If
So calling the newForm.InitializeForm(rsTankName)after the newForm.Show event allows the datagridview to set the CheckBoxColumn correctly.
Likely because the actual CheckBox itself is only actually generated upon the Show command, despite the fact that it is 'available' as a cell with DataGridViewCheckBoxColumn properties in the datagrid, directly after the New frmAddEvent has created the new form instance. The actual CheckBox and its corresponding CheckedState is not created before the newForm.Show event is called. It would appear that the when the CheckBox is created for display (during the newForm.Show event) there is no comparison made to its actual value.
So, in order to set the Checkbox column on initiating a new form you have to call the Show event prior to setting the DataGridViewCheckBoxColumn values otherwise the CheckBox will not show it as 'Checked'.

How do Settings/Options save their selves?

Not sure if my title makes much sense, so I will try to explain my question here. So basically I am expanding my program by allowing things to be customized within it.
Say for example I do this: I click on File -> Options, and a new form is opened with tabs. I have different settings that you can toggle via dropdown box and checkboxes. Now once a user sets the settings they want, or don't want, they click on a button that says either "OK" or "Cancel".
What is the method to saving these settings, or reverting back to the original settings? Do you save via txt file, or is this a default function within a certain line of code?
UPDATE:
So I fixed my previous issue. Now I am having another with the saves. The saves are working good, but I want to use them in selecting my CheckListBox Collection range and also have that range load on start as well. so these are the 2 things that I have been using to do so, that results in adding to the previous, set, collection.
Working for RNG:
Dim rand As New Random()
Dim winners = Enumerable.Range(1, My.Settings.numberSetting).OrderBy(Function(r) rand.Next()).Take(5).ToArray()
Not working for Onload CheckListBox:
Me.LotteryNumbers.Items.Add(1, My.Settings.numberSetting)
If I remove the 1 from Me.LotteryNumbers.Items.Add, the result is this:
This ought not compile:
LotteryNumbers.Items.Add(1, My.Settings.numberSetting)
The overload which takes a second argument expect a Boolean to set the item added to Checked or not. One way is to add items in a loop:
Dim maxNums = My.Settings.numberSetting
' make sure it is empty
clb.Items.Clear()
For n As Int32 = 1 To maxNums
clb.Items.Add(n.ToString)
Next
I don't like using items in Settings as variables, so it grabs the current value to use. Another way uses AddRange:
clb.Items.AddRange(Enumerable.Range(1, maxNums).Select(Function(s) s.ToString()).ToArray())
Items is an collection of Object, so the Select converts to string to add them.
NEVER ORDER BY RANDOM.NEXT()
Mostly you get lucky, but it's not guaranteed. It's only a matter of time before that code blows up on you at run time. The longer the sequence to be sorted, the more likely you are to get an exception.
What you should do instead is implement a Fisher-Yates sort method:
Private rand As New Random()
Public Function Shuffle(Of T)(ByVal items As IList(Of T)) As IList(Of T)
For i As Integer = items.Count - 1 To 1 Step -1
Dim j As Integer = rand.Next(i + 1)
Dim temp As T= items(i)
items(i) = items(j)
items(j) = temp
Next
Return items
End Function
Solution for the working code to update and save checklistbox box count.
Private Sub OptionOkButton_Click(sender As Object, e As EventArgs) Handles OptionOkButton.Click
Main.LotteryNumbers.Items.Clear()
My.Settings.numberSetting = CInt(NumberCombo.Text)
Dim maxNum = My.Settings.numberSetting
Main.LotteryNumbers.Items.AddRange(Enumerable.Range(1, maxNum).Select(Function(s) s.ToString()).ToArray())
My.Settings.Save()
Me.Close()
End Sub

How to reference controls located on different Tabs (VB.NET)

I have an application written in VB.NET that reads data from a file and displays the data on the screen.
Depending on the data in the file, the program has a TabControl with up to 3 tabs and each tab in turn has a DataGridView for displaying data. For example I have a TabControl that has a tab called "Saturday" and a tab called "Sunday".
The problem I am having is that when I read data from a file, the program displays all the data on the Saturday's tab grid because I am not sure how to reference the Grid on the Sunday tab.
To add the DataGridView I am using the following code:
Grid = New DataGridView
Grid.Dock = DockStyle.Fill
Grid.Name = "Grid" & TabControl.SelectedIndex
Grid.Tag = "Grid" & TabControl.SelectedIndex
And this is how I am reading the data in:
If reader.GetAttribute("controltype") = "Tab" Then
SelectedTab = reader.Name
End If
If reader.Name = "cell" Then
y = y + 1
Grid.Rows(i).Cells(y).Style.BackColor = Color.FromName(reader.ReadElementString("cell"))
End If
What I almost want to do is something like (pseudocode):
SelectedTab.Grid.Rows(i).Cells(y).Style.BackColor = Color.FromName(reader.ReadElementString("cell"))
However when I use the above code it complains:
'Grid' is not a member of 'String'
I hope you understand the issue. Let me know if you need clarification
Your code is a little unclear. However, it appears to me that the following line:
If reader.GetAttribute("controltype") = "Tab" Then
SelectedTab = reader.Name
End If
is creating at least one problem. It looks like you are attempting to refer to a Tabpage control by the string representation of its name, but unless I missed something, what that line is actually doing is trying to make a tabpage control type("SelectedTab") refer to a string type. If that is the case, then you will want to try this instead:
If reader.GetAttribute("controltype") = "Tab" Then
TabControl1.SelectedTab = TabControl1.TabPages(reader.name)
End If
It is a little hard to tell from the code you have posted, but that might get you headed down the right path.
++++++++++++
UPDATE: It appears from your code that you are naming each DGV control by appending the index of the tab on which it is located to the string "grid." I am going to assume that you are using a class member variable named "SelectedTab" to represent the current tab selected in the control. I will assume that at the top of your class you have done something like this:
'Form-or-class scoped memebr variables:
Private SelectedTab As TabPage
Private SelectedGrid As DataGridView
You should be able to refer to the active grid control using something like this:
Private Sub TabControl1_SelectedIndexChanged(sender As Object, e As System.EventArgs) Handles TabControl1.SelectedIndexChanged
' Set SelectedTab member variable to refer to the new selected tab page:
SelectedTab = TabControl1.SelectedTab
' Set the SelectedGrid to refer to the grid control hosted on the selected tab page:
SelectedGrid = TabControl1.SelectedTab.Controls("Grid" & TabControl1.SelectedIndex.ToString())
End Sub
From here, you should be able to use the member variable for SelectedGrid to refer to the grid present on which ever tab page is selected in your tab control.
It is challenging to address your concerns with only fragments of your code. If you have additional difficulties, please post more of your code, so we can better see what else is going on.
Hope that helps!
Okay, I would go about something like this. Maybe you can simply use a DataSet to load the XML data in one line (if they have been saved with DataSet.WriteXML before).
Dim ds As New DataSet
Dim p As TabPage
Dim gv As DataGridView
ds.ReadXml("F:\testdata.xml")
For i As Integer = TabControl1.TabPages.Count - 1 To 0 Step -1
TabControl1.TabPages.RemoveAt(i)
Next
For Each dt As DataTable In ds.Tables
p = New TabPage(dt.TableName)
gv = New DataGridView
' ... configure the gv here...
gv.AutoGenerateColumns = True
gv.Dock = DockStyle.Fill
' ...
gv.DataSource = dt
TabControl1.TabPages.Add(p)
p.Controls.Add(gv)
Next

should changing the parent of a listbox change the selected index?

You wouldn’t think so, but it does when the listbox is bound to a datasource (as far as I can see).
I’ve reduced the behaviour to the code below. The "if" line toggles between loading a list via data binding and loading a list “manually” (both use the same data table). In each case I set the selected index afterwards, and then change the parent form. With manual loading, the selected index is retained, with binding it is lost. I cannot see how this makes any sense – I don't see why changing the host form should alter any property of the list. Is this a bug?
Public Class Form1
Sub main() Handles Me.Load
Dim ListControl1 As ListBox = New ListBox
ListControl1.Parent = Me
Dim dt = New DataTable
dt.Columns.Add("intColourID")
dt.Columns.Add("strName")
dt.Rows.Add({1, "Red"})
dt.Rows.Add({2, "Green"})
dt.Rows.Add({3, "Blue"})
ListControl1.ValueMember = dt.Columns(0).ColumnName
ListControl1.DisplayMember = dt.Columns(1).ColumnName
If False Then
ListControl1.DataSource = dt
Else
For i = 0 To dt.Rows.Count - 1
ListControl1.Items.Add(dt.Rows(i)("strName").ToString)
Next
End If
ListControl1.SelectedIndex = 2
Dim z As Form = New Form
ListControl1.Parent = z
z.Show()
End Sub
End Class
The correct way of adding a control to a form is not to set its parent, but to add it to the Controls collection of the form. If I do it like this I do not get an exception (the three last lines commented out as you write in your comment).
Me.Controls.Add(ListControl1) ' Instead of ListControl1.Parent = Me