ComboBox Render in DotNetBar SuperDataGrid - vb.net

I want to render a column in the DotNetBar SuperDataGrid Controll as a comboBox (more precisely as a GridComboBoxExEditControl) .
The SuperDataGrid is connected to a binding source.
In the DataBindingComplete Event I have the following:
Private Sub SuperGrid_DataBindingComplete(ByVal sender As Object, ByVal e As DevComponents.DotNetBar.SuperGrid.GridDataBindingCompleteEventArgs) Handles SuperGrid.DataBindingComplete
Dim panel As DevComponents.DotNetBar.SuperGrid.GridPanel
panel = e.GridPanel
panel.Columns("ArticleID").RenderType = GetType(MyComboBox)
panel.Columns("ArticleID").RenderParams = New Object() {ArticleBindingSource, "Article", "Article"}
End Sub
And the MyComboBox class goes:
Public Class MyComboBox
Inherits GridComboBoxExEditControl
Public Sub New(ByVal Bind As BindingSource, ByVal disp As String, ByVal val As String)
DataSource = Bind
DisplayMember = disp
ValueMember = val
End Sub
End Class
This results in the the following:
The SuperDataGrid Shows the correct Values in the cells. The binding is correct and every value of "ArticleID" if rendered as "Article".
The problem is that when the value shifts From ArticleID = 1 to ArticleID = 2
(Article = "Article No1" to Article = "Artcle No2") the SuperDataGrid goes in some form of endless loop and the value start shifting between 1 and 2. Am I doing something wrong?
If anyone can offer some advice on this subject I will be very grateful.
P.S.
I also tried this:
panel.Columns("ArticleID").EditorType = GetType(GridComboBoxExEditControl)
Dim art As GridComboBoxExEditControl = DirectCast(panel.Columns("ArticleID").EditControl, GridComboBoxExEditControl)
art.DataSource = ArticleBindingSource
art.DisplayMember = "Article"
art.ValueMember = "ArticleID"
Got the same result.

{ArticleBindingSource, "Article", "ArticleID"} I think incorect when same value for DisplayMember,
ValueMember

Related

Sort items in a listbox which contain integer from highest to lowest for VB.NET

(I have about 3 weeks of knowledge so sorry if I can't use the right naming conventions (like string for words)).
I would recommend reading this and then looking at the image I've provided. Also anything red ignore it and blue shows where data travels
To sum it up, I'm entering data, for example, a most football goals challenge and will enter the persons name how many points they achieved (1-100) and the event (series 1,2 or 3) this will then be entered into the 'solo' listbox and repeated until 20 participants have been added into the listbox. Then I will click the 'Add to Rank' button and all the items within solo listbox will be added to the 'RANK', listbox.
Now this is the tricky part, all I'm trying to do is sort the listbox by descending points (most to least, 100-1) which is why I put the points first, but I don't know how to use classes or arrays.
Can you maybe share you code? would be nice too see what you are doing.
Dim List As New List(Of Integer)
'Add ListBox Items to List
For Each ListBoxItem In ListBox1.Items
List.Add(ListBoxItem)
Next
'Sort the List and Clear the ListBox
List.Sort()
ListBox1.Items.Clear()
'Add to ListBox Ascending
For Each ListItem In List
ListBox1.Items.Add(ListItem)
Next
'Add to ListBox Descending
For i = List.Count - 1 To 0 Step -1
ListBox1.Items.Add(List(i))
Next
small example here for you
Don't store the data in the UI. Store it in data structures in code. Here is some code you can use to do that.
First of all, make a class to hold a Solo. This is a fundamental of object oriented programming. It will give you easy access to the properties of the solo, importantly the score which you can use to sort. Then it doesn't matter in what order you display it.
Public Class Solo
Public Sub New(name As String, score As Integer, series As String)
Me.Name = name
Me.Score = score
Me.Series = series
End Sub
Public Property Name As String
Public Property Score As Integer
Public Property Series As String
Public ReadOnly Property Text As String
Get
Return $"{Name}, {Series}, {Score} Points"
End Get
End Property
End Class
In your form, you can use this code. It will bind the data to the ListBoxes. You still have the data in your code to do other things with.
Private serieses As List(Of String)
Private solos As List(Of Solo)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
serieses = New List(Of String)() From {"Series 1", "Series 2", "Series 3"}
solos = New List(Of Solo)()
Me.SeriesComboBox.DataSource = serieses
End Sub
Private Sub AddButton_Click(sender As Object, e As EventArgs) Handles AddButton.Click
Dim score As Integer
If Not String.IsNullOrEmpty(NameTextBox.Text) AndAlso Integer.TryParse(ScoreTextBox.Text, score) Then
solos.Add(New Solo(NameTextBox.Text, score, SeriesComboBox.Text))
bindSolos()
End If
End Sub
Private Sub ShowRankButton_Click(sender As Object, e As EventArgs) Handles ShowRankButton.Click
bindRanks()
End Sub
Private Sub bindSolos()
SoloListBox.DataSource = Nothing
SoloListBox.DataSource = solos
SoloListBox.DisplayMember = "Text"
End Sub
Private Sub bindRanks()
RankListBox.DataSource = Nothing
RankListBox.DataSource = solos.OrderByDescending(Function(s) s.Score).ToList()
RankListBox.DisplayMember = "Text"
End Sub
Note the series are populated in Form_Load, not in the designer. In fact there is no data in the designer, as it should be.
You can rethink the rank button. You don't need it at all and can just bind the sorted ranks when you add, like this
Private Sub bindSolos()
SoloListBox.DataSource = Nothing
SoloListBox.DataSource = solos
SoloListBox.DisplayMember = "Text"
RankListBox.DataSource = solos.OrderByDescending(Function(s) s.Score).ToList()
RankListBox.DisplayMember = "Text"
End Sub
And really, you can just automatically just sort the solo ListBox and get rid of the second ListBox, so when you Add, they are always displayed in the solo ListBox sorted.
Private Sub bindSolos()
SoloListBox.DataSource = Nothing
SoloListBox.DataSource = solos.OrderByDescending(Function(s) s.Score).ToList()
SoloListBox.DisplayMember = "Text"
End Sub

Why i can't pass data from form textbox to form textbox inside panel VB.NET

This is the code when calling form and showing inside panel
Dim frmLubesInterface As LubesInterface = New LubesInterface
with frmLubesInterface
.Text = "frmLubesInterface"
.TopLevel = False
Panel6.Controls.Add(frmLubesInterface)
.StartPosition =
.FormStartPosition.CenterScreen
.Show()
end with
This is code passing data from form and show inside form which is inside of panel
Dim Itemname as string = ""
Itemname = txtItemNameSearch.Text
LubesInterface.txtItem.Text = Itemname - **this part is where i pass the value of data to form textbox inside panel**
To summary i can't pass the value of textbox to form textbox inside the panel, but when showing it as msgbox it show the value.
I am not sure what you mean by "Global" that seems to mean something different to different people.
You could do it in one of two ways as far as I am concerned, you can either pass the values to constructor or create properties and get/set those properties.
Public Class Form1
Private Sub Button1_Click_1(sender As Object, e As EventArgs) Handles Button1.Click
Dim TxtFromTxtBoxOnForm2 As String = String.Empty
Dim Form2 As New Form2
With Form2
TxtFromTxtBoxOnForm2 = .ItmTxt
.TopLevel = False
.StartPosition = FormStartPosition.Manual
Panel1.Controls.Add(Form2)
.Show()
End With
End Sub
End Class
Public Class Form2
Public Property ItmTxt As String
Get
Return TextBoxOnForm2.Text
End Get
Set(value As String)
TextBoxOnForm2.Text = value
End Set
End Property
End Class
I already get it. i should declare global and call the form inside the panel.

is there any way to simplify this code? vb.net [duplicate]

i have been created buttons and textboxs by coding in next loop,
the result
'T(x).Name = "text_1"
'T(x).Name = "text_2"
'T(x).Name = "text_3"
'....
'B(x).Name = "button_1"
'B(x).Name = "button_2"
'B(x).Name = "button_3"
'...
and i want to get textbox property whene i click the button,
i can get button property when click like button_1.Name.ToString
but i cant get the text_1,2,3 .... property.
i do some trick by split function button_1.Name.ToString and get the last number
and add it to the textbox name like "text_" & button_1.Name.ToString but i can't convert this string to object.
Update
Here's the code I'm using to load the controls in the loop:
C_A_TEXT(x) = New TextBox()
C_A_TEXT(x).Dock = System.Windows.Forms.DockStyle.Fill
C_A_TEXT(x).Location = New System.Drawing.Point(270, 5)
C_A_TEXT(x).Margin = New System.Windows.Forms.Padding(0)
C_A_TEXT(x).Size = New System.Drawing.Size(70, 27)
C_A_TEXT(x).TabIndex = 5
C_A_TEXT(x).Name = "NEW_RECHARGE_COUNT_TEXT_" & x
Update 2
Here's some more code:
AddHandler C_A_BUTTONS(x).Click, AddressOf C_A_BUTTON
Private Sub C_A_BUTTON(ByVal sender As System.Object, ByVal e As System.EventArgs)
Dim thisButton As Button = sender Dim A = CType(Me.Controls("NEW_RECHARGE_COUNT_TEXT_1"), TextBox)
MsgBox(A.Text.ToString) 'Error!
End Sub
You can access the controls by name via the Form.Controls property, for instance:
Dim text1 As TextBox = CType(Me.Controls("text_1"), TextBox)
As a quick useful tip to note, you don't seem to have to specify the type of control within the CType statement for purposes of accessing a control on your form. I came across this when trying to access multiple types of form controls, such as buttons and textboxes, all with the same line of code.
CType(Controls("NAME_OF_CONTROL"), Control)
Note that, rather than specifying exactly what type of control, such as 'TextBox' or 'Button', you simply state 'Control'. This allows you to universally change any type of control, without needing to specify its type.
I couldn't find this anywhere else, so I thought I'd share it!
Below is the code.
Dim oObj As Object = Me.Controls.Find("control name", True).FirstOrDefault()
Obj.Property = Value
I hope it helps.
Dim sometext As TextBox = CType(Me.Controls("sometext "), TextBox)
The title of the thread and your description of the problem at hand seem a little different from each other.
To answer your title (to find a control by its name) use the following:
Dim myControlToFind = LayoutRoot.FindName("NAMEOFCONTROL")
More information on this method can be found here .
To answer the description of your issue as (to access a code generated control after it is clicked) do the following:
In the loop where you are creating the control(s) add the following handler
Addhandler YOURCONTROL.Clicked, AddressOf Textbox_Clicked
...and then this will handle the click event
Private Sub Textbox_Clicked(sender as object, e as RoutedEventArgs)
Dim tbClicked = Ctype(sender, TextBox)
'You can now access any of the properties of the textbox, for example
Dim txt as String = tbClicked.Text
Dim name as String = tbClicked.Name
Dim height as Double = tbClicked.Height
End Sub
None of the above worked for me. This does:
Dim selVal As String = CType(Form.FindControl(myListName), DropDownList).SelectedValue

Update data bound control when data changes

I have a List(T) that is bound to some controls, a read-only DataGridView, a ComboBox and a few Labels. This works fine, the controls are all populated correctly when the form loads, the Label.Text and DataGridView row focus all change as the ComboBox selection is changed.
But if I change the data in an object on the List the data shown in the controls does not update to reflect the changed data.
My class T implements the INotifyChanged interface and the label control data bindings update mode is set to OnPropertychanged.
I can force the DataGridView to update by calling its Refresh() method, but trying the same for the labels seems to have no effect.
So how can I make changes to the data in the objects in my list update the data shown in the Label controls? Have I done something wrong?
My MRE so far:
Class Form1
' Form1 has a DataGridView, a ComboBox, a Label, a Button and a TextBox
Dim FooList As New List(Of Foo)(3)
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
For Index As Integer = 0 To FooList.Capacity - 1
FooList.Add(New Foo() With {.Bar = Index, .Baz = 0})
Next
' Shows all Bar and Baz
DataGridView1.DataSource = FooList
' User selects Bar value
ComboBox1.DataSource = FooList
ComboBox1.DisplayMember = "Bar"
' Related Baz value shows
Label1.DataBindings.Add(New Binding("Text", FooList, "Baz", DataSourceUpdateMode.OnPropertyChanged))
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
' This is not _actually_ how I'm selecting indexes and changing the data
' But for the MRE it changes the Baz property
'Change the Baz value on the List, should result in Label1 changing
FooList(ComboBox1.SelectedItem.Bar).Baz = TextBox1.Text.Convert.ToUInt16
' Should I even need this when my list objects have INotifyChanged?
DataGridView1.Refresh()
End Sub
End Class
Class Foo
Implements INotifyChanged
Private _bar As UInt16
Private _baz As UInt16
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(ByVal PropertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(PropertyName))
End Sub
Property Bar As UInt 16
Get
Return _bar
End Get
Set(value As Byte)
If Not (value = _bar) Then
_bar = Bar
NotifyPropertyChanged("Bar")
End If
End Set
End Property
Property Baz As UInt 16
Get
Return _baz
End Get
Set(value As Byte)
If Not (value = _baz) Then
_baz = Baz
NotifyPropertyChanged("Baz")
End If
End Set
End Property
End Class
One way to have changes in the collection reflected in bound controls, is to "reset" the DataSource:
FooList.Add(New Foo(...))
dgv1.DataSource = Nothing
dgv1.DataSource = FooList
If the control is something like a ListBox, you have to also reset the DisplayMember and ValueMember properties because they get cleared. This is a greasy way to notify controls of changes to the list because many things get reset. In a ListBox for instance, the SelectedItems collection is cleared.
A much better way to let changes to your collection flow thru to controls is to use a BindingList(Of T). Changes to the collection/list (adds, removes) will automatically and instantly be shown in your control.
INotifyPropertyChanged goes one step further. If a property value on an item in the list changes, the BindingList<T> will catch the PropertyChanged events your class raises and "forward" the changes to the control.
To be clear, the BindingList handlea changes to the list, while INotifyPropertyChanged handles changes to the items in the list.
FooList(13).Name = "Ziggy"
Using a List<T> the name change won't show up unless you "reset" the DataSource. Using a BindingList<T> alone, it wont show up right away - when the list changes, it should show up. Implementing INotifyPropertyChanged allows the change to show up right away.
Your code is mostly correct for it, except it is not INotifyChanged. But there is also a shortcut as of Net 4.5:
Private Sub NotifyChange(<CallerMemberName> Optional propname As String = "")
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propname))
End Sub
CallerMemberName is an attribute which allows you to forego actually passing the name; the named ends up being substituted at runtime:
Private _name As String
Public Property Name As String
Get
Return _name
End Get
Set(value As String)
If _name <> value Then
_name = value
NotifyChange()
End If
End Set
End Property
If nothing else it can cut down on copy/paste errors if there are lots of properties to raise events for (ie Bar property using NotifyChange("Foo") because you copied the code from that setter).
I figured I can push the data around the other way. That is, instead of updating the data in the list and then trying to update the controls, I update the control and the List data is updated via the binding.
E.g. My click event from above now becomes:
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Label1.Text = TextBox1.Text
DataGridView1.Refresh()
End Sub
Though TBH Im not a fan of that and I'm still puzzled as to how I could better use the INotifyPropertyChanged interface .

Gridviews and DropdownLists

Is it possible to change the data source of a dropdown list in a gridview from another dropdown list selected index changed method in the same gridview?
for example I have a dropdown that needs to change its contents depending on what is chosen in the previous cell of the gridview, which is also a dropdown list.
Any Help would be much appreciated
Thanks
Instead of changing the DataSource when the 1st DropDownList.SelectedIndex changes, you could set the DataSource of the 2nd DropDownList when it is being edited.
An example of how this can be achieved can be found here.
In this article, the author hooks to the EditingControlShowing event in order to change the type of the ComboBox. This can be easily modified to change the DataSource instead:
Private Sub DataGridView1_EditingControlShowing(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) Handles DataGridView1.EditingControlShowing
' make sure we are editing the 2nd ComboBox:'
Dim comboBoxColumn2 As DataGridViewComboBoxColumn = DataGridView1.Columns(2)
If (DataGridView1.CurrentCellAddress.X = comboBoxColumn2.DisplayIndex) Then
'here you retrieve the value of the 1st ComboBox:'
Dim comboBox1Value As object = DataGridView1.SelectedRow... 'fill with whatever is needed'
Dim cb As ComboBox = e.Control
If (cb IsNot Nothing) Then
cb.DataSource = Nothing 'maybe not needed, I'm not sure
cb.DataSource = 'here, set the data source based on the value of ComboBox1'
End If
End If
End Sub
Here is another way how I would do this, by example: Two columns (Types, Days), if the user drops-down and chooses 'week', a second combo populates with week days, otherwise, weekends.
For the purpose of this example, add a grid (DataGridView1) with two ComboBoxCell columns and let the first column have these items: week, weekend.
This class will be our data source:
Class WeekDataItem
Sub New(ByVal id As Integer, ByVal name As String)
Me.ID = id
Me.Name = name
End Sub
Public Property ID() As Integer
Get
Return _ID
End Get
Set(ByVal value As Integer)
_ID = value
End Set
End Property
Private _ID As Integer
Public Property Name() As String
Get
Return _Name
End Get
Set(ByVal value As String)
_Name = value
End Set
End Property
Private _Name As String
End Class
This function will return our data source, based on the key which can be 'week' or 'weekend':
Function getWeekDataSource(ByVal key As String) As List(Of WeekDataItem)
getWeekDataSource = New List(Of WeekDataItem)
If (key = "week") Then
getWeekDataSource.Add(New WeekDataItem(1, "monday"))
getWeekDataSource.Add(New WeekDataItem(2, "tuesday"))
getWeekDataSource.Add(New WeekDataItem(3, "wednesday"))
getWeekDataSource.Add(New WeekDataItem(4, "thrusday"))
getWeekDataSource.Add(New WeekDataItem(5, "friday"))
ElseIf (key = "weekend") Then
getWeekDataSource.Add(New WeekDataItem(6, "caturday"))
getWeekDataSource.Add(New WeekDataItem(7, "sunday"))
End If
End Function
And lastly, this event will fire when the Type combo value changes, and assign the appropriate data source to our days combo:
Private Sub DataGridView1_CellValueChanged(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellValueChanged
' if the type dropdown value changed
If (e.ColumnIndex = clmTypes.Index) Then
' set the week dropdown data source for the current row
If Not IsNothing(DataGridView1.CurrentRow) Then
' get the combobox cell we want to change
Dim comboCell As DataGridViewComboBoxCell
comboCell = CType(DataGridView1.CurrentRow.Cells(clmDays.Index), DataGridViewComboBoxCell)
' assign it's new data source
comboCell.DataSource = getWeekDataSource(CStr(DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex).Value))
' update the data source members so it displays info properly
comboCell.DisplayMember = "Name"
comboCell.ValueMember = "ID"
End If
End If
End Sub
Note that this event fires after the cell is validated, ie once you tab off the cell.
It is totally possible. How are populating your dropdownlists ? If all the data is dynamic then you will have to rebuild the entire grid everytime you change the dropdownlist selected item.
If I am not wrong , you are trying to apply Filter mechanism. Are you ? Another way I have done in the past is to build my data source for DropDownList from the rows of GridView. Think about the data that you already have on the screen. Once you are in PreRender Function you can bind needed data in your dropdownlist, this way you will cut out load.