I have used following code from stack over flow
combobox AfterUpdate
Private Sub InvoiceCombo_AfterUpdate()
If IsNull(Me.InvoiceCombo) Then
Me.Product=Null
Me.qty=Null
Me.Rate=Null
Else
Me.Product=me.InvoiceCombo.Column(1)
Me.Qty=me.InvoiceCombo.Column(2)
Me.Rate=me.InvoceCombo.Column(3)
End If
End Sub
this is working fine but the problem is cannot add multiple rows(multiple products, like multiple items in one invoice)
Related
I have been encountering an error in my VB.Net code that says "No row can be added to a DataGridView control that does not have columns. Columns must be added first.". When I was trying to code a live search on a datagridview1. Please help me.
Here is the code for the datagrid live search that encounters an error:
Private Sub txt_search_TextChanged(sender As Object, e As EventArgs) Handles txt_search.TextChanged
DataGridView1.Rows.Clear()
'' Searching via room number or category id, ie room type
Try
conn.Open()
Dim cmd As New MySqlCommand("SELECT r.id, r.room_no, c.name, r.description, r.price FROM categories c JOIN rooms r ON c.id = r.category_id WHERE room_no LIKE '%" & txt_search.Text & "%' or name LIKE '%" & txt_search.Text & "%'", conn)
dr = cmd.ExecuteReader
While dr.Read
DataGridView1.Rows.Add(dr.Item("ID"), dr.Item("room_no"), dr.Item("name"), dr.Item("description"), dr.Item("price"))
End While
dr.Dispose()
Catch ex As Exception
MsgBox(ex.Message)
End Try
conn.Close()
End Sub
I would do a few things differently.
You should not declare your connection at the class level, instead create a new connection and dispose immediately when done.
Same with the DataRow.
I personally wouldn't like to see results immediately after typing a character in a searchbox. If I have a typo, then want to erase it and fix it, I need to wait for the typo result, then see the fixed result. We'll use a timer to create this delay.
Which brings me to the other issue, which is that you are hitting your database on the UI thread, so add that to the last item, your end user is experiencing a UI freeze while waiting for results with each keypress. We will query off the UI and invoke the update back when done.
Don't put textbox Text directly into a query or you will be opened up to sql injection issues. We will use parameters
(Oh, and I haven't even arrived at your question yet!)
You can simply databind the result to the grid instead of trying to add rows. You can only add rows if the grid has predefined rows with a template for new rows. You can do that but this locks your UI into a particular design so if you want to change the query later, you will also need to fix the UI. Too tightly coupled. We'll use a class to define the data. You will get these if you use an ORM which is vastly superior to using a DataReader, but you don't so I made one.
Let's see...
Private Class Result
Public Sub New(iD As Integer, room_no As String, name As String, description As String, price As Decimal)
Me.ID = iD
Me.Room_no = room_no
Me.Name = name
Me.Description = description
Me.Price = price
End Sub
' Update data types according to your actual database types
Public Property ID As Integer
Public Property Room_no As String
Public Property Name As String
Public Property Description As String
Public Property Price As Decimal
End Class
You will want to make a timer to tick when you want to hit the database. Also record the current state of the textbox at this time. This is all you do in the TextChanged event handler
Private queryTimer As New System.Threading.Timer(AddressOf queryCallback)
Private queryTerm As String
Private Sub txt_search_TextChanged(sender As Object, e As EventArgs) Handles txt_search.TextChanged
queryTerm = txt_search.Text.Trim() ' do you want to trim whitespace?
' Execute search 500ms later.
' If multiple keystrokes are pressed quickly,
' previous timer schedules are cancelled and only the last happens.
queryTimer.Change(500, -1)
End Sub
This is the timer callback, which runs off the UI and invokes the UI update when it's done
Private Sub queryCallback(state As Object)
' This routine is called on a background thread, off the UI
Dim results As New List(Of Result)()
Using conn As New MySqlConnection("connection string")
conn.Open()
' build command with parameter
Using cmd As New MySqlCommand(
$"SELECT r.id, r.room_no, c.name, r.description, r.price
FROM categories c
JOIN rooms r ON c.id = r.category_id
WHERE room_no LIKE '%#search%'
or name LIKE '%#search%'", conn)
cmd.Parameters.AddWithValue("#search", queryTerm)
Using dr = cmd.ExecuteReader()
While dr.Read()
' Update conversions according to actual data types
' here as well as in the class I provided.
results.Add(New Result(
Convert.ToInt32(dr("id")),
dr("room_no").ToString(),
dr("name").ToString(),
dr("description").ToString(),
Convert.ToDecimal(dr("price"))))
End While
End Using
End Using
End Using
' Can't update the UI from a background thread, so this call is invoked
DataGridView1.Invoke(
Sub()
DataGridView1.DataSource = Nothing
DataGridView1.DataSource = results
End Sub)
End Sub
Additional information is in remarks in the code provided
I am working on a very, very simple project which sets a followup Flag on a selected message when running a macro.
I have created this so far which works very nicely, but I want to improve it by replacing the manual input of "YA" initials and put the default profile user initials as the initials.
I have found the contactitem object, but I have no idea how to get the initials out of it to put it as a .FlagRequest
Any idea's about this?
This is currently my code: (the GetCurrentItem just returns the selected email)
Public Sub SetCustomFlagNormal()
Dim objMsg As Object
Set objMsg = GetCurrentItem()
With objMsg
.MarkAsTask olMarkThisWeek
.TaskDueDate = Now
.FlagRequest = "YA"
.ReminderSet = True
.ReminderTime = Now + 2
.Save
End With
Set objMsg = Nothing
End Sub
The Outlook object model provides the CurrentUser property for the Namespace and Account classes. The property of the Namespace class returns the display name of the currently logged-on user as a Recipient object.
Sub DisplayCurrentUser()
Dim myNamespace As Outlook.NameSpace
Set myNameSpace = Application.GetNameSpace("MAPI")
MsgBox myNameSpace.CurrentUser
End Sub
The property of the Account class returns a Recipient object that represents the current user identity for the account. See the Name property of the Recipient class.
I have finally found a way.
After getting to multiple website, I was able to determine that Outlook doesn't offer a way to get the user initials and that Word has to be called upon to get the initials
As office uses the same initials throughout all office applications.
I got my response on this website.
http://blogs.technet.com/b/heyscriptingguy/archive/2008/03/18/how-can-i-retrieve-the-user-name-and-user-initials-from-microsoft-powerpoint.aspx
Hope it helps
Zoho Creator is a great system for quickly creating simple cloud applications. I've run into a problem with sub-forms, though: currently, Zoho Creator does not provide functionality for sorting sub-form records by a specified column. Instead, it sorts records in the order in which they were added.
My sub-form is a Creator Form that's linked to another Creator Form (basically, 2 different tables). The forms are linked with a bi-directional lookup relationship.
I've seen and tried implementing these "hacks", but none of them work for my situation:
[Zoho Forums, "Subforms sorting rows"][1]
[Zoho Forums, "Hack to sort rows of a subform and pre-populate row fields that I want to preset"][2]
I also called Zoho tech support, and after looking at my application, they said that sorting sub-form records is not currently possible.
Any other ideas?
My tested solution is still a hack, but until Zoho implements a method to sort sub-form records via the GUI, this will have to do.
First, create a function that you can call from anywhere (e.g. when a new sub-form record is added or changed)--for details on that, go here: http://www.zoho.com/creator/help/script/functions.html
This function will first duplicate the sub-form records by the parent record ID (sorting by the appropriate column) and then delete all sub-form records that were inserted before the script started:
int SubFormRecords_SortByAnything_ReturnCount(int ParentRecordID)
{
scriptStartTime = zoho.currenttime;
for each rSubFormRecord in SubFormRecords [ParentFieldName = input.ParentRecordID] sort by FieldName1, FieldName3, FieldName2
{
NewSubFormRecordID = insert into SubFormRecords
[
FieldName1 = rSubFormRecord.FieldName1
FieldName2 = rSubFormRecord.FieldName2
FieldName3 = rSubFormRecord.FieldName3
];
}
delete from SubFormRecords[ (Series == input.ParentRecordID && Added_Time < scriptStartTime) ];
return SubFormRecords[ParentFieldName == input.EventID].count();
}
Once the above sorting function is in place (customized for your application), call it when appropriate. I call it when adding a record associated with the sub-form, or when I change the sorting column values.
That works well, and as long as you don't have complex logic associated with adding and deleting records, it should have minimal impact on application performance.
Please let me know whether that works for you, and if you have any better ideas.
Caveat: This solution is not suitable for forms containing additional sub-form records because deleting the records will delete linked sub-form values.
Thanks.
I have a a very simple workaround:
1) You have to add a Form Workflow
2)Record Event - Create OR Edit OR Create/Edit (As per your requirement)
3)Form Event - On successful form submission
4)Let Main_Form be the link name of the Main Form
4)Let Sub_Form be the Link name of the Sub Form (Not the link name you specify in the main form for the same sub form)
4)Let Field1 and Field2 are fields of subform on which you want to sort subform records
5)Let Link_ID be lookup field of Mainform ID in the subform
Workflow
1)Sub_Records = Sub_Form[Link_ID == input.ID] sort by Field1,Field2;
(sort by multiple fields, add asc/desc as per requirement)
2)delete from Sub_Form[Link_ID == input.ID];
3)for each sub_record in Sub_Records
{
insert into Sub_Form
[
Added_User = zoho.loginuser
Link_ID = input.ID
Field1 = sub_record.Field1
Field2 = sub_record.Field2
]
}
//Now you check the results in edit view of the main form
I am using the Exchange Web Services Managed API to work with Tasks (Exchange 2007 SP1). I can create them fine. However, when I try to do updates, it works for all of the fields except for the .Body field. Whenever I try to access (read/update) that field, it gives the following error:
"You must load or assign this property before you can read its value."
The code I am using looks like this:
//impersonate the person whose tasks you want to read
Me.Impersonate(userName); //home-made function to handle impersonation
//build the search filter
Exchange.SearchFilter.SearchFilterCollection filter = New Exchange.SearchFilter.SearchFilterCollection();
filter.Add(New Exchange.SearchFilter.IsEqualTo(Exchange.TaskSchema.Categories, "Sales"));
//do the search
EWS.Task exTask = esb.FindItems(Exchange.WellKnownFolderName.Tasks, filter, New Exchange.ItemView(Integer.MaxValue));
exTask.Subject = txtSubject.Text; //this works fine
exTask.Body = txtBody.Text; //This one gives the error implying that the object isn't loaded
The strange thing is that, inspecting the property bag shows that the object contains 33 properties, but {Body} is not one of them. That property seems to be inherited from the base class .Item, or something.
So, do I need to re-load the object as type Item? Or reload it via .Bind or something? Keep in mind that I need to do this with thousands of items, so efficiency does matter to me.
Calling the Load method solved my problem :)
foreach (Item item in findResults.Items)
{
item.Load();
string subject = item.Subject;
string mailMessage = item.Body;
}
I had the same problem when using the EWS. My Code is requesting the events(Appointments) from the
Outlook calendar, at the end I couldn't reach to the body of the Event itself.
The missing point in my situation was the following "forgive me if there is any typo errors":
After gathering the Appointments, which are also derived from EWS Item Class, I did the following:
1- Create a List with the type Item:
List<Item> items = new List<Item>();
2- Added all appointments to items list:
if(oAppointmentList.Items.Count > 0) // Prevent the exception
{
foreach( Appointment app in oAppointmentList)
{
items.Add(app);
}
}
3- Used the exchanged service "I have already created and used":
oExchangeService.LoadPropertiesForItems(items, PropertySet.FirstClassProperties);
now if you try to use app.Body.Text, it will return it successfully.
Enjoy Coding and Best Luck
I forgot to mention the resource:
http://social.technet.microsoft.com/Forums/en-US/exchangesvrdevelopment/thread/ce1e0527-e2db-490d-817e-83f586fb1b44
He mentioned the use of Linq to save the intermediate step, it will help you avoid using the List items and save some memory!
RockmanX
You can load properties using a custom property set. Some properties are Extended properties instead of FirstClassProperties.
Little example:
_customPropertySet = new PropertySet(BasePropertySet.FirstClassProperties, AppointmentSchema.MyResponseType, AppointmentSchema.IsMeeting, AppointmentSchema.ICalUid);
_customPropertySet.RequestedBodyType = BodyType.Text;
appointment.Load(_customPropertySet);
The data sources have the same structure, but different data. One would be used for rows that are saved (view mode), and the other one would be for rows that are being added or edited (edit/new rows). How can that be acomplished?
I have a standard foreign key column that references a standard lookup table which has an ID, Name and Active (bit). The combo box column uses that lookup table to show the list, but only active items. Let's say a lookup item is used and later deactivated (Active = 0). The combo box column now shows errors because the ID is not found in the list. Does anyone have any ideas how to solve it?
Not sure if you're still looking but I've just come across the same problem. Here's how I addressed it, hope it is of use to you.
Define a function which returns a DataGridViewComboCell with the appropriate datasource set (collection objects defined elsewhere, note that I'm using EntitySpaces in this example to populate the DataSource):
Private Function GetStockComboDataSource(ByVal type As eStockComboType) As DataGridViewComboBoxCell
Try
Dim cell As New DataGridViewComboBoxCell
Select Case type
Case eStockComboType.StockIn
cell.DataSource = Me.StockInCol.Query.LoadDataTable
cell.DisplayMember = "FullName"
cell.ValueMember = JCStockInMetadata.ColumnNames.StockItemID
Case eStockComboType.StockItem
cell.DataSource = StockItemCol.Query.LoadDataTable
cell.ValueMember = JCStockItemMetadata.ColumnNames.StockItemID
cell.DisplayMember = "FullName"
End Select
Return cell
End Function
Now when it comes to setting the combo DataSource (I use the RowEnter event here for example):
Private Sub dgvStock_RowEnter(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles dgvStock.RowEnter
Try
If IsNumeric(Me.dgvStock.Rows(e.RowIndex).Cells("ID").Value) Then
Dim intValue As Integer = Convert.ToInt32(Me.dgvStock.Rows(e.RowIndex).Cells("Stock Item").Value)
Me.dgvStock.Rows(e.RowIndex).Cells("Stock Item") = GetStockComboDataSource(eStockComboType.StockItem)
Me.dgvStock.Rows(e.RowIndex).Cells("Stock Item").Value = intValue
Else
Me.dgvStock.Rows(e.RowIndex).Cells("Stock Item") = GetStockComboDataSource(eStockComboType.StockIn)
End If
Catch ex As Exception
JCExceptionLogger.LogException(ex)
End Try
End Sub
Here I switch the combo DataSource depending on whether the ID column is numeric but you can implement whatever business logic you require. Note that I save the value of the cell before setting the combo (intValue). This appears to be necessary otherwise the combo will display as blank.