VB ToolStripControlHost remove Text property - vb.net

I have a class that allows ToolStrip to have a NumericUpDown. I added a properties (Value, Minimum and Maximum). It is working but I want to remove the Text property since I added the Value property. Is there a way to do this?
Please see image where you can see the designer properties
Below is the class
<Windows.Forms.Design.ToolStripItemDesignerAvailabilityAttribute(Windows.Forms.Design.ToolStripItemDesignerAvailability.ToolStrip), DebuggerStepThrough()>
Public Class ToolStripNumericUpDown
Inherits Windows.Forms.ToolStripControlHost
Public Sub New()
MyBase.New(New System.Windows.Forms.NumericUpDown())
End Sub
Public ReadOnly Property ToolStripNumericUpDownControl() As Windows.Forms.NumericUpDown
Get
Return TryCast(Control, Windows.Forms.NumericUpDown)
End Get
End Property
Public Property Value() As Long
Get
Return ToolStripNumericUpDownControl.Value
End Get
Set(ByVal value As Long)
ToolStripNumericUpDownControl.Value = value
End Set
End Property
Public Property Minimum() As Long
Get
Return ToolStripNumericUpDownControl.Minimum
End Get
Set(ByVal value As Long)
ToolStripNumericUpDownControl.Minimum = value
End Set
End Property
Public Property Maximum() As Long
Get
Return ToolStripNumericUpDownControl.Maximum
End Get
Set(ByVal value As Long)
ToolStripNumericUpDownControl.Maximum = value
End Set
End Property
Protected Overrides Sub OnSubscribeControlEvents(ByVal c As Windows.Forms.Control)
MyBase.OnSubscribeControlEvents(c)
AddHandler DirectCast(c, Windows.Forms.NumericUpDown).ValueChanged, AddressOf OnValueChanged
End Sub
Protected Overrides Sub OnUnsubscribeControlEvents(ByVal c As Windows.Forms.Control)
MyBase.OnUnsubscribeControlEvents(c)
RemoveHandler DirectCast(c, Windows.Forms.NumericUpDown).ValueChanged, AddressOf OnValueChanged
End Sub
Public Event ValueChanged As EventHandler
Private Sub OnValueChanged(ByVal sender As Object, ByVal e As EventArgs)
RaiseEvent ValueChanged(Me, e)
End Sub
End Class

Related

How to assign typical NumericUpDown properties to custom NumericUpDown based control?

I'm trying to make a DataGridViewColumn that inherits all of the properties of a typical NumericUpDown control. I found an answer here on StackOverflow (https://stackoverflow.com/a/55788490/692250) and a MSDN article here (https://learn.microsoft.com/en-us/dotnet/desktop/winforms/controls/how-to-host-controls-in-windows-forms-datagridview-cells?view=netframeworkdesktop-4.8&redirectedfrom=MSDN#code-snippet-1).
The code from the article works well, but... I cannot assign a Maximum nor Minimum value to the control (both are stuck at the "default" Minimum of 0 and Maximum of 100).
I tried to add in code from the SO answer (by adding in a Min and Max field) but doing so locks both in to 0 and unable to change.
The question in short: how do I add the necessary properties to allow to this work.
The custom control is question:
Public Class NumericUpDownColumn
Inherits DataGridViewColumn
Public Sub New()
MyBase.New(New NumericUpDownCell())
End Sub
Public Overrides Property CellTemplate() As DataGridViewCell
Get
Return MyBase.CellTemplate
End Get
Set(ByVal value As DataGridViewCell)
' Ensure that the cell used for the template is a CalendarCell.
If Not (value Is Nothing) AndAlso Not value.GetType().IsAssignableFrom(GetType(NumericUpDownCell)) Then
Throw New InvalidCastException("Must be an Integer")
End If
MyBase.CellTemplate = value
End Set
End Property
End Class
Public Class NumericUpDownCell
Inherits DataGridViewTextBoxCell
Public Sub New()
' Number Format
Me.Style.Format = "0"
End Sub
Public Overrides Sub InitializeEditingControl(ByVal rowIndex As Integer, ByVal initialFormattedValue As Object, ByVal dataGridViewCellStyle As DataGridViewCellStyle)
' Set the value of the editing control to the current cell value.
MyBase.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle)
Dim ctl As NumericUpDownEditingControl = CType(DataGridView.EditingControl, NumericUpDownEditingControl)
ctl.Value = CType(Me.Value, Decimal)
End Sub
Public Overrides ReadOnly Property EditType() As Type
Get
' Return the type of the editing contol that CalendarCell uses.
Return GetType(NumericUpDownEditingControl)
End Get
End Property
Public Overrides ReadOnly Property ValueType() As Type
Get
' Return the type of the value that CalendarCell contains.
Return GetType(Decimal)
End Get
End Property
Public Overrides ReadOnly Property DefaultNewRowValue() As Object
Get
' Use the current date and time as the default value.
Return 0
End Get
End Property
End Class
Class NumericUpDownEditingControl
Inherits NumericUpDown
Implements IDataGridViewEditingControl
Private dataGridViewControl As DataGridView
Private valueIsChanged As Boolean = False
Private rowIndexNum As Integer
Public Sub New()
Me.DecimalPlaces = 0
End Sub
Public Property EditingControlFormattedValue() As Object Implements IDataGridViewEditingControl.EditingControlFormattedValue
Get
Return Me.Value.ToString("#")
End Get
Set(ByVal value As Object)
If TypeOf value Is Decimal Then
Me.Value = Decimal.Parse(value.ToString)
End If
End Set
End Property
Public Function GetEditingControlFormattedValue(ByVal context As DataGridViewDataErrorContexts) As Object Implements IDataGridViewEditingControl.GetEditingControlFormattedValue
Return Me.Value.ToString("#")
End Function
Public Sub ApplyCellStyleToEditingControl(ByVal dataGridViewCellStyle As DataGridViewCellStyle) Implements IDataGridViewEditingControl.ApplyCellStyleToEditingControl
Me.Font = dataGridViewCellStyle.Font
Me.ForeColor = dataGridViewCellStyle.ForeColor
Me.BackColor = dataGridViewCellStyle.BackColor
End Sub
Public Property EditingControlRowIndex() As Integer Implements IDataGridViewEditingControl.EditingControlRowIndex
Get
Return rowIndexNum
End Get
Set(ByVal value As Integer)
rowIndexNum = value
End Set
End Property
Public Function EditingControlWantsInputKey(ByVal key As Keys, ByVal dataGridViewWantsInputKey As Boolean) As Boolean Implements IDataGridViewEditingControl.EditingControlWantsInputKey
' Let the DateTimePicker handle the keys listed.
Select Case key And Keys.KeyCode
Case Keys.Left, Keys.Up, Keys.Down, Keys.Right, Keys.Home, Keys.End, Keys.PageDown, Keys.PageUp
Return True
Case Else
Return False
End Select
End Function
Public Sub PrepareEditingControlForEdit(ByVal selectAll As Boolean) Implements IDataGridViewEditingControl.PrepareEditingControlForEdit
' No preparation needs to be done.
End Sub
Public ReadOnly Property RepositionEditingControlOnValueChange() As Boolean Implements IDataGridViewEditingControl.RepositionEditingControlOnValueChange
Get
Return False
End Get
End Property
Public Property EditingControlDataGridView() As DataGridView Implements IDataGridViewEditingControl.EditingControlDataGridView
Get
Return dataGridViewControl
End Get
Set(ByVal value As DataGridView)
dataGridViewControl = value
End Set
End Property
Public Property EditingControlValueChanged() As Boolean Implements IDataGridViewEditingControl.EditingControlValueChanged
Get
Return valueIsChanged
End Get
Set(ByVal value As Boolean)
valueIsChanged = value
End Set
End Property
Public ReadOnly Property EditingControlCursor() As Cursor Implements IDataGridViewEditingControl.EditingPanelCursor
Get
Return MyBase.Cursor
End Get
End Property
Protected Overrides Sub OnValueChanged(ByVal eventargs As EventArgs)
' Notify the DataGridView that the contents of the cell have changed.
valueIsChanged = True
Me.EditingControlDataGridView.NotifyCurrentCellDirty(True)
MyBase.OnValueChanged(eventargs)
End Sub
End Class
Focusing on it a bit more, I found that I can assign values to the Maximum and Minimum fields as so:
' Set the value of the editing control to the current cell value.
MyBase.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle)
Dim ctl As NumericUpDownEditingControl = CType(DataGridView.EditingControl, NumericUpDownEditingControl)
ctl.Value = CType(Me.Value, Decimal)
ctl.Maximum = 180
ctl.Minimum = -1
End Sub
But I cannot seem to pass to this field via the usual means of .Maximum or .Minimum on the constructor. Any tips/advice?
This is an easy fix and very easy to overlook.
In your NumericUpDownCell class you need to override the Clone method to include the new Minimum and Maximum properties, for example:
First, add the new properties the NumericUpDownCell class and then override the Clone function
Public Class NumericUpDownCell
Inherits DataGridViewTextBoxCell
...
Public Property Minimum() As Integer
Public Property Maximum() As Integer
...
Public Overrides Function Clone() As Object
Dim retVal As NumericUpDownCell = CType(MyBase.Clone, NumericUpDownCell)
retVal.Minimum = Me.Minimum
retVal.Maximum = Me.Maximum
Return retVal
End Function
End Class
Second, inside the NumericUpDownCell classes InitializeEditingControl method, add the two lines:
ctl.Minimum = Me.Minimum
ctl.Maximum = Me.Maximum
When you setup the new column, get the CellTemplate to set the new properties, as per:
Dim upDownColumn As New NumericUpDownColumn
Dim cellTemplate As NumericUpDownCell = CType(upDownColumn .CellTemplate, NumericUpDownCell)
cellTemplate.Minimum = 10
cellTemplate.Maximum = 15
Or, following you preference to setup via the constructor, add a new constructor to the NumericUpdDownColumn class as per
Public Sub New(minValue As Integer, maxValue As Integer)
MyBase.New(New NumericUpDownCell())
Dim template As NumericUpDownCell = CType(CellTemplate, NumericUpDownCell)
template.Minimum = minValue
template.Maximum = maxValue
End Sub
and then use it like:
Dim upDownColumn As New NumericUpDownColumn(100, 150)
Dim cellTemplate As NumericUpDownCell = CType(upDownColumn.CellTemplate, NumericUpDownCell)
DataGridView1.Columns.Add(upDownColumn)

DateTimePicker in Datagrid - clear It's value?

I have used code from MSDN site that adds DateTimePicker in Datagrid. I have shorten It a bit, so now DateTimepicker doesn't show It's default value and enters in column only when user selects value from It. Now I want to add code for clearing DTP with Delete or Backspace key. I know DTP can't have null value, but I've managed to do It with other DTP's (which are not inside Datagridview) like this:
Private Sub DataGridView1_KeyDown(sender As Object, e As KeyEventArgs) Handles DataGridView1.KeyDown
If e.KeyCode = Keys.Delete Or e.KeyCode = Keys.Back Then
DTPtest.format = DTPtest.custom
DTPtest.Format = DateTimePickerFormat.Custom
DTPtest.CustomFormat = " "
End If
End Sub
Code for hosting DateTimePicker in Datagrid is above my knowledge, and I don't know where I should create something similar that will work same. ANY HELP MUCH APPRECIATED !!!
Here is my code for hosting DaTeTimePicker (from MSDN as mentioned):
Public Class CalendarColumn
Inherits DataGridViewColumn
Public Sub New()
MyBase.New(New CalendarCell())
End Sub
Public Overrides Property CellTemplate() As DataGridViewCell
Get
Return MyBase.CellTemplate
End Get
Set(ByVal value As DataGridViewCell)
' Ensure that the cell used for the template is a CalendarCell.
If (value IsNot Nothing) AndAlso
Not value.GetType().IsAssignableFrom(GetType(CalendarCell)) _
Then
Throw New InvalidCastException("Must be a CalendarCell")
End If
MyBase.CellTemplate = value
End Set
End Property
End Class
Public Class CalendarCell
Inherits DataGridViewTextBoxCell
Public Sub New()
' Use the short date format.
Me.Style.Format = "d"
End Sub
Public Overrides Sub InitializeEditingControl(ByVal rowIndex As Integer,
ByVal initialFormattedValue As Object,
ByVal dataGridViewCellStyle As DataGridViewCellStyle)
' Set the value of the editing control to the current cell value.
MyBase.InitializeEditingControl(rowIndex, initialFormattedValue,
dataGridViewCellStyle)
Dim ctl As CalendarEditingControl =
CType(DataGridView.EditingControl, CalendarEditingControl)
End Sub
Public Overrides ReadOnly Property EditType() As Type
Get
' Return the type of the editing control that CalendarCell uses.
Return GetType(CalendarEditingControl)
End Get
End Property
Public Overrides ReadOnly Property ValueType() As Type
Get
' Return the type of the value that CalendarCell contains.
Return GetType(DateTime)
End Get
End Property
End Class
Class CalendarEditingControl
Inherits DateTimePicker
Implements IDataGridViewEditingControl
Private dataGridViewControl As DataGridView
Private valueIsChanged As Boolean = False
Private rowIndexNum As Integer
Public Sub New()
Me.Format = DateTimePickerFormat.Short
End Sub
Public Property EditingControlFormattedValue() As Object _
Implements IDataGridViewEditingControl.EditingControlFormattedValue
Get
Return Me.Value.ToShortDateString()
End Get
Set(ByVal value As Object)
Try
' This will throw an exception of the string is
' null, empty, or not in the format of a date.
Me.Value = DateTime.Parse(CStr(value))
Catch
' In the case of an exception, just use the default
' value so we're not left with a null value.
Me.Value = DateTime.Now
End Try
End Set
End Property
Public Function GetEditingControlFormattedValue(ByVal context _
As DataGridViewDataErrorContexts) As Object _
Implements IDataGridViewEditingControl.GetEditingControlFormattedValue
Return Me.Value.ToShortDateString()
End Function
Public Sub ApplyCellStyleToEditingControl(ByVal dataGridViewCellStyle As _
DataGridViewCellStyle) _
Implements IDataGridViewEditingControl.ApplyCellStyleToEditingControl
Me.Font = dataGridViewCellStyle.Font
Me.CalendarForeColor = dataGridViewCellStyle.ForeColor
Me.CalendarMonthBackground = dataGridViewCellStyle.BackColor
End Sub
Public Property EditingControlRowIndex() As Integer _
Implements IDataGridViewEditingControl.EditingControlRowIndex
Get
Return rowIndexNum
End Get
Set(ByVal value As Integer)
rowIndexNum = value
End Set
End Property
Public Function EditingControlWantsInputKey(ByVal key As Keys,
ByVal dataGridViewWantsInputKey As Boolean) As Boolean _
Implements IDataGridViewEditingControl.EditingControlWantsInputKey
' Let the DateTimePicker handle the keys listed.
Select Case key And Keys.KeyCode
Case Keys.Left, Keys.Up, Keys.Down, Keys.Right,
Keys.Home, Keys.End, Keys.PageDown, Keys.PageUp
Return True
Case Else
Return Not dataGridViewWantsInputKey
End Select
End Function
Public Sub PrepareEditingControlForEdit(ByVal selectAll As Boolean) _
Implements IDataGridViewEditingControl.PrepareEditingControlForEdit
' No preparation needs to be done.
End Sub
Public ReadOnly Property RepositionEditingControlOnValueChange() _
As Boolean Implements _
IDataGridViewEditingControl.RepositionEditingControlOnValueChange
Get
Return False
End Get
End Property
Public Property EditingControlDataGridView() As DataGridView _
Implements IDataGridViewEditingControl.EditingControlDataGridView
Get
Return dataGridViewControl
End Get
Set(ByVal value As DataGridView)
dataGridViewControl = value
End Set
End Property
Public Property EditingControlValueChanged() As Boolean _
Implements IDataGridViewEditingControl.EditingControlValueChanged
Get
Return valueIsChanged
End Get
Set(ByVal value As Boolean)
valueIsChanged = value
End Set
End Property
Public ReadOnly Property EditingControlCursor() As Cursor _
Implements IDataGridViewEditingControl.EditingPanelCursor
Get
Return MyBase.Cursor
End Get
End Property
Protected Overrides Sub OnValueChanged(ByVal eventargs As EventArgs)
' Notify the DataGridView that the contents of the cell have changed.
valueIsChanged = True
Me.EditingControlDataGridView.NotifyCurrentCellDirty(True)
MyBase.OnValueChanged(eventargs)
End Sub
End Class
Solved, pretty simple actually:
Private Sub DataGridView1_KeyDown(sender As Object, e As KeyEventArgs) Handles DataGridView1.KeyDown
If e.KeyCode = Keys.Delete Or e.KeyCode = Keys.Back Then
DataGridView1.CurrentCell.Value = Nothing
End If
End Sub
Only thing that It's not same is that you have to go out of cell, and then return into It for delete.

custom datagridview column Control will not accept a period (.)

I cannot get a control I found online to accept a period(.). I need to be able to enter numeric values with decimal places. I wanted to use the numericupdown control in a datagridview cell so I can use the up-down arrows for adjusting values.
This control implments the NumericUpDown control as the editing control on a datagridview column. I found it online (don't remember where), and ti was based on a similar custom datagridview column based in a calendar control.
I made a few modifications to it so I could set the maximum, minimum, decimalplaces and imcrement properties.
However, even when decimal places is set to 2 and increment is .1, when I'm typing a value the control will not accept a period.
Below is the code, which includes the classes for the column, cell, and editing control. Please help. I have no clue what the problem is.
Public Class NumericUpDownColumn
Inherits DataGridViewColumn
Public Sub New()
MyBase.New(New NumericUpDownCell())
End Sub
Public Overrides Property CellTemplate() As DataGridViewCell
Get
Return MyBase.CellTemplate
End Get
Set(ByVal value As DataGridViewCell)
' Ensure that the cell used for the template is a CalendarCell.
If Not (value Is Nothing) AndAlso _
Not value.GetType().IsAssignableFrom(GetType(NumericUpDownCell)) _
Then
Throw New InvalidCastException("Must be a CalendarCell")
End If
MyBase.CellTemplate = value
End Set
End Property
Private _Maximum As Decimal = 100
Private _Minimum As Decimal = 0
Private _Increment As Decimal = 0.1
Private _DecimalPlaces As Integer = 2
Public Property DecimalPlaces() As Integer
Get
Return _DecimalPlaces
End Get
Set(ByVal value As Integer)
If _DecimalPlaces = value Then
Return
End If
_DecimalPlaces = value
End Set
End Property
Public Property Maximum() As Decimal
Get
Return _Maximum
End Get
Set(ByVal value As Decimal)
_Maximum = value
End Set
End Property
_
Public Property Minimum() As Decimal
Get
Return _Minimum
End Get
Set(ByVal value As Decimal)
_Minimum = value
End Set
End Property
_
Public Property Increment() As Decimal
Get
Return _Increment
End Get
Set(ByVal value As Decimal)
_Increment = value
End Set
End Property
End Class
Public Class NumericUpDownCell
Inherits DataGridViewTextBoxCell
Public Sub New()
' Use the short date format.
Me.Style.Format = "N2"
End Sub
Public Overrides Sub InitializeEditingControl(ByVal rowIndex As Integer, _
ByVal initialFormattedValue As Object, _
ByVal dataGridViewCellStyle As DataGridViewCellStyle)
' Set the value of the editing control to the current cell value.
MyBase.InitializeEditingControl(rowIndex, initialFormattedValue, _
dataGridViewCellStyle)
Dim ctl As NumericUpDownEditingControl = _
CType(DataGridView.EditingControl, NumericUpDownEditingControl)
RemoveHandler ctl.Enter, AddressOf Me.OnNumericEnter
AddHandler ctl.Enter, AddressOf Me.OnNumericEnter
ctl.Maximum = CType(Me.DataGridView.Columns(Me.ColumnIndex), NumericUpDownColumn).Maximum
ctl.Minimum = CType(Me.DataGridView.Columns(Me.ColumnIndex), NumericUpDownColumn).Minimum
ctl.Increment = CType(Me.DataGridView.Columns(Me.ColumnIndex), NumericUpDownColumn).Increment
ctl.DecimalPlaces = CType(Me.DataGridView.Columns(Me.ColumnIndex), NumericUpDownColumn).DecimalPlaces
ctl.ThousandsSeparator = True
ctl.Value = CType(Me.Value, Decimal)
End Sub
'''
''' Handle on enter event of numeric
'''
'''
'''
'''
Private Sub OnNumericEnter(ByVal sender As Object, ByVal e As EventArgs)
Dim control As NumericUpDownEditingControl = CType(sender, NumericUpDownEditingControl)
Dim strValue As String = control.Value.ToString("N2")
control.Select(0, strValue.Length)
End Sub
Public Overrides ReadOnly Property EditType() As Type
Get
' Return the type of the editing contol that CalendarCell uses.
Return GetType(NumericUpDownEditingControl)
End Get
End Property
Public Overrides ReadOnly Property ValueType() As Type
Get
' Return the type of the value that CalendarCell contains.
Return GetType(Decimal)
End Get
End Property
Public Overrides ReadOnly Property DefaultNewRowValue() As Object
Get
' Use the current date and time as the default value.
Return 0
End Get
End Property
End Class
Class NumericUpDownEditingControl
Inherits NumericUpDown
Implements IDataGridViewEditingControl
Private dataGridViewControl As DataGridView
Private valueIsChanged As Boolean = False
Private rowIndexNum As Integer
Public Sub New()
End Sub
Public Property EditingControlFormattedValue() As Object _
Implements IDataGridViewEditingControl.EditingControlFormattedValue
Get
Return Me.Value.ToString("N2")
End Get
Set(ByVal value As Object)
If TypeOf value Is Decimal Then
Me.Value = Decimal.Parse(value)
End If
End Set
End Property
_
Public Function GetEditingControlFormattedValue(ByVal context _
As DataGridViewDataErrorContexts) As Object _
Implements IDataGridViewEditingControl.GetEditingControlFormattedValue
Return Me.Value.ToString("N2")
End Function
Public Sub ApplyCellStyleToEditingControl(ByVal dataGridViewCellStyle As _
DataGridViewCellStyle) _
Implements IDataGridViewEditingControl.ApplyCellStyleToEditingControl
Me.Font = dataGridViewCellStyle.Font
Me.ForeColor = dataGridViewCellStyle.ForeColor
Me.BackColor = dataGridViewCellStyle.BackColor
End Sub
Public Property EditingControlRowIndex() As Integer _
Implements IDataGridViewEditingControl.EditingControlRowIndex
Get
Return rowIndexNum
End Get
Set(ByVal value As Integer)
rowIndexNum = value
End Set
End Property
Public Function EditingControlWantsInputKey(ByVal key As Keys, _
ByVal dataGridViewWantsInputKey As Boolean) As Boolean _
Implements IDataGridViewEditingControl.EditingControlWantsInputKey
' Let the DateTimePicker handle the keys listed.
Select Case key And Keys.KeyCode
'Case Keys.Left, Keys.Up, Keys.Down, Keys.Right, _
' Keys.Home, Keys.End, Keys.PageDown, Keys.PageUp
Case Keys.Up, Keys.Down
Return True
Case Else
Return False
End Select
End Function
Public Sub PrepareEditingControlForEdit(ByVal selectAll As Boolean) _
Implements IDataGridViewEditingControl.PrepareEditingControlForEdit
' No preparation needs to be done.
End Sub
Public ReadOnly Property RepositionEditingControlOnValueChange() _
As Boolean Implements _
IDataGridViewEditingControl.RepositionEditingControlOnValueChange
Get
Return False
End Get
End Property
Public Property EditingControlDataGridView() As DataGridView _
Implements IDataGridViewEditingControl.EditingControlDataGridView
Get
Return dataGridViewControl
End Get
Set(ByVal value As DataGridView)
dataGridViewControl = value
End Set
End Property
Public Property EditingControlValueChanged() As Boolean _
Implements IDataGridViewEditingControl.EditingControlValueChanged
Get
Return valueIsChanged
End Get
Set(ByVal value As Boolean)
valueIsChanged = value
End Set
End Property
Public ReadOnly Property EditingControlCursor() As Cursor _
Implements IDataGridViewEditingControl.EditingPanelCursor
Get
Return MyBase.Cursor
End Get
End Property
Protected Overrides Sub OnValueChanged(ByVal eventargs As EventArgs)
' Notify the DataGridView that the contents of the cell have changed.
valueIsChanged = True
Me.EditingControlDataGridView.NotifyCurrentCellDirty(True)
MyBase.OnValueChanged(eventargs)
End Sub
End Class
I used an old programmers trick to solve this. I just downloaded the sample code from the post Plutonix referenced and just added the DLL it came with to my project. This work fine and saved me a lot of trouble I wasn't looking for.

Linq to SQL to ObservableCollection for treeview in VB.Net

I have some Linq to SQL table classes that are joined together. I currently have it bound to a treeview using just the LINQ to SQL query. It works, but it doesn't show when stuff is added or removed from the database.
I implemented INotifyPropertyChanged but it isn't updating the treeview.
I also tried using Bindable Linq, but it doesn't seem to make a difference.
I found an example of a way to easily create ObservableCollections without having to create more classes: jimlynn.wordpress.com/2008/12/09/using-observablecollection-with-linq/, (which is kind of important because I have a future project looming that will require interacting with a lot of tables (30 or so) and just creating the Linq to SQL classes is going to be a pain).
Property ModelQuery As ObservableCollection(Of dbModels) = New ObservableCollection(Of dbModels)().PopulateFrom(From mm In tblModels.AsBindable Order By mm.ModelName)
Is this a good way to go, or am I going to have to create a separate ObservableCollection and maintain them both in code. If I'm going to use this binding stuff, I'm really looking for a way to have stuff just linked together so I don't have to update multiple structures whenever a change is made.
Main table:
<Table(Name:="Models")> Public Class dbModels
Implements INotifyPropertyChanged
Private _changed As Boolean
Public Event PropertyChanged(ByVal sender As Object, ByVal e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
Protected Overridable Sub OnPropertyChanged(ByVal Propertyname As String)
If Not Propertyname.Contains("Changed") Then
Changed = True
End If
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(Propertyname))
End Sub
Public Property Changed() As Boolean
Get
Return _changed
End Get
Set(ByVal value As Boolean)
If _changed <> value Then
_changed = value
OnPropertyChanged("Changed")
End If
End Set
End Property
Private _ModelID As Integer
<Column(Storage:="_ModelID", DbType:="int IDENTITY NOT NULL", IsPrimaryKey:=True, IsDbGenerated:=True, Name:="ModelID")> _
Public Property ModelID() As Integer
Get
Return Me._ModelID
End Get
Set(value As Integer)
Me._ModelID = value
End Set
End Property
Private _ModelName As String
<Column(Storage:="_ModelName", DbType:="Varchar(200)", Name:="ModelName")> _
Public Property ModelName() As String
Get
Return Me._ModelName
End Get
Set(value As String)
Me._ModelName = value
End Set
End Property
Private _ModelYears As EntitySet(Of dbModelYears) = New EntitySet(Of dbModelYears)
<Association(Storage:="_ModelYears", DeleteRule:="CASCADE", OtherKey:="ModelID")> _
Public Property ModelYears As EntitySet(Of dbModelYears)
Get
Return _ModelYears
End Get
Set(value As EntitySet(Of dbModelYears))
_ModelYears.Assign(value)
End Set
End Property
End Class
Joined table:
<Table(Name:="ModelYears")> Public Class dbModelYears
Implements INotifyPropertyChanged
Private _changed As Boolean
Public Event PropertyChanged(ByVal sender As Object, ByVal e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
Protected Overridable Sub OnPropertyChanged(ByVal Propertyname As String)
If Not Propertyname.Contains("Changed") Then
Changed = True
End If
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(Propertyname))
End Sub
Public Property Changed() As Boolean
Get
Return _changed
End Get
Set(ByVal value As Boolean)
If _changed <> value Then
_changed = value
OnPropertyChanged("Changed")
End If
End Set
End Property
Private _ModelYearID As Integer
<Column(Storage:="_ModelYearID", DbType:="int IDENTITY NOT NULL", IsPrimaryKey:=True, IsDbGenerated:=True, Name:="ModelYearID")> _
Public Property ModelYearID() As Integer
Get
Return Me._ModelYearID
End Get
Set(value As Integer)
Me._ModelYearID = value
End Set
End Property
Private _ModelID As Integer
<Column(Storage:="_ModelID", DbType:="int", Name:="ModelID")> _
Public Property ModelID() As Integer
Get
Return Me._ModelID
End Get
Set(value As Integer)
Me._ModelID = value
End Set
End Property
Private _ModelYear As String
<Column(Storage:="_ModelYear", DbType:="Varchar(50)", Name:="ModelYear")> _
Public Property ModelYear() As String
Get
Return Me._ModelYear
End Get
Set(value As String)
Me._ModelYear = value
End Set
End Property
End Class
Implementing INotifyPropertyChanged or using ObservableCollection wont notify you of changes in the database. You are going to have to execute the query every time you want to retrieve new data from the database.
Alternatively you could possibly use SqlDependency to set up a query notification dependency between your application and an instance of SQL Server.
EDIT:
From comments.
To hook up the PropertyChanged event of the items in the collection.
For Each item As var In itemsCollection
Dim notifyItem = TryCast(item, INotifyPropertyChanged)
If item IsNot Nothing Then
AddHandler notifyItem.PropertyChanged, AddressOf ItemChanged
End If
Next
Public Sub ItemChanged(sender As Object, e As PropertyChangedEventArgs)
'Handle event
End Sub

Avoid duplicated event handlers?

How do I avoid an event from being handled twice (if is the same handler?)
Module Module1
Sub Main()
Dim item As New Item
AddHandler item.TitleChanged, AddressOf Item_TitleChanged
AddHandler item.TitleChanged, AddressOf Item_TitleChanged
item.Title = "asdf"
Stop
End Sub
Private Sub Item_TitleChanged(sender As Object, e As EventArgs)
Console.WriteLine("Title changed!")
End Sub
End Module
Public Class Item
Private m_Title As String
Public Property Title() As String
Get
Return m_Title
End Get
Set(ByVal value As String)
m_Title = value
RaiseEvent TitleChanged(Me, EventArgs.Empty)
End Set
End Property
Public Event TitleChanged As EventHandler
End Class
Output:
Title changed!
Title changed!
Desired output:
Title changed!
I want the event manager to detect that this event is already handled by this handler and so it shouldn't rehandle (or readd) it.
You can also just always call RemoveHandler before AddHandler. I have found this practical in specific scenarios.
Wrapping the event handler list in a HashSet will make sure the handlers are not duplicate references, the following snippet replacement for the question's Item class will work under the above sample (it won't re-add the handler if it's already in the HashSet):
Public Class Item
Private m_Title As String
Public Property Title() As String
Get
Return m_Title
End Get
Set(ByVal value As String)
m_Title = value
RaiseEvent TitleChanged(Me, EventArgs.Empty)
End Set
End Property
Private handlers As New HashSet(Of EventHandler)
Public Custom Event TitleChanged As EventHandler
AddHandler(value As EventHandler)
handlers.Add(value)
End AddHandler
RemoveHandler(value As EventHandler)
handlers.Remove(value)
End RemoveHandler
RaiseEvent(sender As Object, e As System.EventArgs)
For Each handler In handlers.Where(Function(h) h IsNot Nothing)
handler(sender, e)
Next
End RaiseEvent
End Event
End Class