custom categoryattribute for control shadowed property - vb.net

I made a custom CategoryAttribute in order to localize properties of custom controls.
<AttributeUsage(AttributeTargets.Property)> _
Public Class LocalisableCategoryAttribute
Inherits CategoryAttribute
Public Sub New(ByVal resourceName As String)
MyBase.New(resourceName)
End Sub
Protected Overrides Function GetLocalizedString(value As String) As String
Return My.Resources.ResourceManager.GetString(value)
End Function
End Class
My custom controls have both brand new properties with this attribute but I also shadowed some of the "basic" properties (such as the Size and Location) in order to give them this attribute.
<LocalisableCategory("Category_Apparence")> _
Public Shadows Property Size As Size
Get
Return MyBase.Size
End Get
Set(value As Size)
MyBase.Size = value
End Set
End Property
The big problem is that at runtime, sometimes the new "Apparence" category will show up and sometimes the old "Layout" one will. Only shadowed properties have this odd behavior. It's totally random. It's not a compile thing either. You can launch the .exe twice in a row and the property grid won't show the same result. Sometimes it will be put under its old category, sometimes the new localized one.
I am completely at a loss with this since it's so random. Can anyone help?

You have to change:
<AttributeUsage(AttributeTargets.Property)> _
Public Class LocalisableCategoryAttribute
Inherits CategoryAttribute
Private resourceKey As String
Public Sub New(ByVal resourceName As String)
resourceKey = resourceName
End Sub
Protected Overrides Function GetLocalizedString(value As String) As String
Return My.Resources.ResourceManager.GetString(resourceKey)
End Function
End Class

Related

Implementing Interface from C# to VB.NET

I had downloaded a C# project and wanted to work on VB.Net so I decided to convert that from C# to VB.NET and I encountered some problems when it came to implementing interfaces. I keep getting errors in VB.NET about implementation about how I must have Read-Only or Write-Only specifiers. I want to get rid of this error but I don't know how I can achieve this.
I have Three Files:
CustomPaintRichText.vb
IUnderlineableSpellingControl.vb
ISpellingControl.vb
The same goes with C#, however in C# it works fine and I want to try to get it to work exactly like that in VB.net.
CustomPaintRichText.vb:
Public Class CustomPaintRichText
Inherits RichTextBox
Implements IUnderlineableSpellingControl
Public m_underlinedSections As Dictionary(Of Integer, Integer)
Public m_protectedSections As Dictionary(Of Integer, Integer)
Public m_ignoredSections As Dictionary(Of Integer, Integer)
Public Property UnderlinedSections() As Dictionary(Of Integer, Integer)
Get
If m_underlinedSections Is Nothing Then
m_underlinedSections = New Dictionary(Of Integer, Integer)()
End If
Return m_underlinedSections
End Get
Set(value As Dictionary(Of Integer, Integer))
m_underlinedSections = value
End Set
End Property
Public WriteOnly Property ProtectedSections() As Dictionary(Of Integer, Integer)
Set(value As Dictionary(Of Integer, Integer))
m_protectedSections = value
End Set
End Property
Public WriteOnly Property IgnoredSections() As Dictionary(Of Integer, Integer)
Set(value As Dictionary(Of Integer, Integer))
m_ignoredSections = value
End Set
End Property
Private spellingEnabled As Boolean
Private spellingAutoEnabled As Boolean
Private m_isPassWordProtected As Boolean
Private penColour As Pen
Public Property WhatPenColour() As Pen
Get
Return penColour
End Get
Set(value As Pen)
penColour = value
End Set
End Property
Public Property IsSpellingEnabled() As Boolean
Get
Return spellingEnabled
End Get
Set(value As Boolean)
spellingEnabled = value
End Set
End Property
Public Property IsSpellingAutoEnabled() As Boolean
Get
Return spellingAutoEnabled
End Get
Set(value As Boolean)
spellingAutoEnabled = value
If Not spellingEnabled Then
spellingEnabled = value
End If
End Set
End Property
Public Property IsPassWordProtected() As Boolean
Get
Return m_isPassWordProtected
End Get
Set(value As Boolean)
m_isPassWordProtected = value
End Set
End Property
End Class
IUnderlineableSpellingControl.vb:
Public Interface IUnderlineableSpellingControl
Inherits ISpellingControl
Inherits IUnderlineable
End Interface
ISpellingControl.vb:
Public Interface ISpellingControl
<Browsable(True)> _
Property IsSpellingEnabled() As Boolean
Property SelectionStart() As Integer
Property SelectionLength() As Integer
Property SelectedText() As String
Property Text() As String
Property ContextMenuStrip() As ContextMenuStrip
Property WhatPenColour() As Pen
Property Parent() As Control
Event Disposed As EventHandler
Event Enter As EventHandler
Event TextChanged As EventHandler
Property [ReadOnly]() As Boolean
ReadOnly Property IsPassWordProtected() As Boolean
Sub Cut()
Sub Copy()
Sub Paste(clipFormat As DataFormats.Format)
Sub [Select](start As Integer, length As Integer)
Function Focus() As Boolean
Sub Invalidate(invalidateChildren As Boolean)
WriteOnly Property IgnoredSections() As Dictionary(Of Integer, Integer)
End Interface
If I keep the cursor carret next to Implements IUnderlineableSpellingControl and hit ENTER key, within the CustomPaintRichText.vb class, I get:
Public Property ContextMenuStrip1 As ContextMenuStrip Implements ISpellingControl.ContextMenuStrip
Public Sub Copy1() Implements ISpellingControl.Copy
End Sub
Public Sub Cut1() Implements ISpellingControl.Cut
End Sub
Public Event Disposed1(sender As Object, e As EventArgs) Implements ISpellingControl.Disposed
Public Event Enter1(sender As Object, e As EventArgs) Implements ISpellingControl.Enter
Public Function Focus1() As Boolean Implements ISpellingControl.Focus
End Function
Public WriteOnly Property IgnoredSections1 As Dictionary(Of Integer, Integer) Implements ISpellingControl.IgnoredSections
Set(value As Dictionary(Of Integer, Integer))
End Set
End Property
Public Sub Invalidate1(invalidateChildren As Boolean) Implements ISpellingControl.Invalidate
End Sub
Public ReadOnly Property IsPassWordProtected1 As Boolean Implements ISpellingControl.IsPassWordProtected
Get
End Get
End Property
Public Property IsSpellingEnabled1 As Boolean Implements ISpellingControl.IsSpellingEnabled
Public Property Parent1 As Control Implements ISpellingControl.Parent
Public Sub Paste1(clipFormat As DataFormats.Format) Implements ISpellingControl.Paste
End Sub
Public Property ReadOnly1 As Boolean Implements ISpellingControl.ReadOnly
Public Sub Select1(start As Integer, length As Integer) Implements ISpellingControl.Select
End Sub
Public Property SelectedText1 As String Implements ISpellingControl.SelectedText
Public Property SelectionLength1 As Integer Implements ISpellingControl.SelectionLength
Public Property SelectionStart1 As Integer Implements ISpellingControl.SelectionStart
Public Property Text1 As String Implements ISpellingControl.Text
Public Event TextChanged1(sender As Object, e As EventArgs) Implements ISpellingControl.TextChanged
Public Property WhatPenColour1 As Pen Implements ISpellingControl.WhatPenColour
Public Sub CustomPaint1() Implements IUnderlineable.CustomPaint
End Sub
Public Property IsSpellingAutoEnabled1 As Boolean Implements IUnderlineable.IsSpellingAutoEnabled
Public Event KeyDown1(sender As Object, e As KeyEventArgs) Implements IUnderlineable.KeyDown
Public WriteOnly Property ProtectedSections1 As Dictionary(Of Integer, Integer) Implements IUnderlineable.ProtectedSections
Set(value As Dictionary(Of Integer, Integer))
End Set
End Property
Public Sub RemoveWordFromUnderliningList1(wordStart As Integer) Implements IUnderlineable.RemoveWordFromUnderliningList
End Sub
Public Event SelectionChanged1(sender As Object, e As EventArgs) Implements IUnderlineable.SelectionChanged
Public Property UnderlinedSections1 As Dictionary(Of Integer, Integer) Implements IUnderlineable.UnderlinedSections
And when I make changes to the CustomPaintRichText from a form, I will have extra additional controls and ultimately nothing works.
The error is in Implements IUnderlineableSpellingControl. It is underlined saying that: 'CustomPaintRichText' must implement 'Event Disposed(sender As Object, e As System.EventArgs)' for interface 'ISpellingControl'. This is one of 30 errors along with the ..must implement..for interface.
Here's the error list if you want to see what kind of errors I'm getting.
Here are the .cs files in case:
CustomPaintRichText.cs
IUnderlineableSpellingControl.cs
ISpellingControl.cs
Congratulations, you made Hans Passant go "Ugh!" :)
Speaking to his point, though, mixing and matching assemblies compiled from VB, C#, C++/CLI, F#, or whatever, is generally encouraged in the .NET world, and is the practical solution to your problem. However, if you insist on transforming this C# project into its VB equivalent, one needs to understand the differences in how interfaces get implemented between these two languages.
C# has two styles of implementation: implicit and explicit (see http://blogs.msdn.com/b/mhop/archive/2006/12/12/implicit-and-explicit-interface-implementations.aspx). VB has only an explicit style, which doesn't work quite the same as C# (see https://msdn.microsoft.com/en-us/library/28e2e18x.aspx).
All of those "must implement" errors mean pretty much what they say: You must use the Implements keyword on the appropriate members of your subclass, because VB doesn't do implicit implementations of interfaces. That's a C# thing. When you hit the ENTER key with the cursor caret next to Implements IUnderlineableSpellingControl, the IDE generated template code for the affected (apparently missing) members, complete with Implements. It did that in trying to be helpful, but in this case you have to look over the code and put in the Implements clauses where they're needed (and probably get rid off that template code).
C# has a neat implicit style where it will automatically "wire-up" implementations by matching member names between your class and the interfaces being implemented. Should there be more than one interface that have the same member (with the same signature), they will all be implemented with the very same member in your class (see http://blogs.msdn.com/b/mhop/archive/2006/12/12/implicit-and-explicit-interface-implementations.aspx). This can be a wonderful or not-so-good thing depending on the situation.
C# has a limited explicit style. One simply defines a member in the named in the format of InterfaceName.MemberName (see https://msdn.microsoft.com/en-us/library/ms173157.aspx).
VB has only its explicit style, but it allows for a list of interface members in the Implements clause so that they all get implemented by the very same member in your class. This is the work-around for C# implicit implementations that hookup multiple interfaces.
Finally, there are some sources which claim that VB cannot do re-implementations of an interface on a subclass of a superclass that already has it implemented (e.g. http://www.dotnetheaven.com/article/how-to-re-implement-interfaces-in-vb.net). I do not know if that was ever true, but I can aver that the VB of VS 2012 and later allows for such re-implementations.
C# allows you to implement a WriteOnly or ReadOnly property with a read/write property. (VB 2015 also allows this).
You can easily work around this prior to VB 2015 - here's an example for your IsPasswordProtected implementation:
Private ReadOnly Property ISpellingControl_IsPassWordProtected() As Boolean Implements ISpellingControl.IsPassWordProtected
Get
Return IsPassWordProtected
End Get
End Property
Public Property IsPassWordProtected() As Boolean
Get
Return isPassWordProtected_Renamed
End Get
Set(ByVal value As Boolean)
isPassWordProtected_Renamed = value
End Set
End Property
The 'Implements' goes on the new ReadOnly property which calls the existing read/write property.

How to do a collection property inside another collection in user control

I have a user control with a property "Rules" that is a generic list.
Every "rule" is associated to a combobox control and i have to create a property to host data for the combobox. I used another generic list to accomplish this.
In design works well, i can add items normally in property grid, but when i run the program the values are not maintained.
Rules property:
Private _regras As New List(Of ParametrosColunasGrid)
<Category("Ecletica")> _
<Browsable(True)> _
<System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Content)>
Public Property Regras() As List(Of ParametrosColunasGrid)
Get
Return _regras
End Get
Set(value As List(Of ParametrosColunasGrid))
_regras = value
End Set
End Property
Public Class ParametrosColunasGrid
'...
Private _itens_Combo As New List(Of ItemComboBox)
<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>
Public Property ItensCombo As List(Of ItemComboBox)
Get
Return _itens_Combo
End Get
Set(value As List(Of ItemComboBox))
_itens_Combo = value
End Set
End Property
'...
End Class
ItemComboBox Class:
<Serializable()>
Public Class ItemComboBox
Public Property Value As String
Public Property Key As String
Public Overrides Function ToString() As String
Return _Value
End Function
End Class

VB.NET Scope with Shared: Subs and Functions and Web Methods

.NET Gurus,
I have picked up another developers work and I have created a Class Level Public Property boolean isNewValue
However, I cannot access this Property from the "Shared" Subs or Functions that are Public or <WebMethods> that are Public Shared Functions.
I do not understand why, could someone explain?
' Public Property scoped as Public in the Public Class Class_Name
Public Property isNewValueCode As Boolean
Get
Return _isNewValueCode
End Get
Set(ByVal value As Boolean)
_isNeValuewSrvCode = value
End Set
End Property
Later on in the code I have:
Shared Function GetDataItem(ByRef db As myEntities) As CodeDataItem
THIS IS WHERE I NEED TO BE ADD AN IF
If isNewValueCode Then 'Cannot see the isNewValueCode
Dim data As New CodeDataItem
Dim code = db.tbl_services.FirstOrDefault(Function(x) x.id = TargetID)
If (Not code Is Nothing) Then
data = New CodeDataItem(code)
End If
Else
'New code going against different db.tables in Entity Context
End If
Return data
End Function
enter code here
You can not access non shared class members from shared functions without an instance of that class. So to be able to access isNewValueCode you need an instance of what ever class the property belongs to.
Depending on you requirements you could possibly change the property to be shared also, but than it would no longer be a member of any instance of that class.
I imagine you have something simular to this:
Private _isNewValueCode As Boolean
Private _isNeValuewSrvCode As Boolean
Public Property isNewValueCode As Boolean
Get
Return _isNewValueCode
End Get
Set(ByVal value As Boolean)
_isNeValuewSrvCode = value
End Set
End Property
You have a couple of options here, you can make isNewValue and it's private constructors shared like so:
Private Shared _isNewValueCode As Boolean
Private Shared _isNeValuewSrvCode As Boolean
Public Shared Property isNewValueCode As Boolean
Get
Return _isNewValueCode
End Get
Set(ByVal value As Boolean)
_isNeValuewSrvCode = value
End Set
End Property
Or Assuming isNeValuewSrvCode is a copy paste error and you really wanted to assign it back to the same variable, you could simply do this:
Public Shared Property isNewValueCode As Boolean
Take a look at the MSDN Description of shared properties for more info on what exactly the shared keyword does.

Custom CollectionEditor never triggers a property's "set" method

I am trying to implement a way of persisting a collection in a custom settings class. I have successfully created the settings class (inheriting ApplicationSettingsBase) and can save properties using the built-in editors on a PropertyGrid, but my custom implementation of a property grid for collections doesn't persist any of the values I add. Here's my code:
Imports System.Configuration
Imports System.ComponentModel
Imports System.Drawing.Design
Imports System.ComponentModel.Design
Public Class CustomSettings
Inherits ApplicationSettingsBase
<UserScopedSetting()> _
<DefaultSettingValue("White")> _
Public Property BackgroundColor() As Color
Get
BackgroundColor = Me("BackgroundColor")
End Get
Set(ByVal value As Color)
Me("BackgroundColor") = value
End Set
End Property
<UserScopedSetting()> _
<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
<Editor(GetType(CustomStringCollectionEditor), GetType(UITypeEditor))> _
Public Property EmailAddresses() As Collection
Get
EmailAddresses = Me("EmailAddresses")
End Get
Set(ByVal value As Collection)
Me("EmailAddresses") = value
End Set
End Property
End Class
Public Class CustomStringCollectionEditor
Inherits CollectionEditor
Public Sub New()
MyBase.New(GetType(Collection))
End Sub
Protected Overrides Function CreateInstance(ByVal itemType As System.Type) As Object
Return String.Empty
End Function
Protected Overrides Function CreateCollectionItemType() As System.Type
Return GetType(String)
End Function
End Class
I set a breakpoint on the Set methods for both the BackgroundColor property and the EmailAddresses property. The BackgroundColor property works as it should - it breaks on the Set statement and stores the property correctly. But when I close the custom CollectionEditor dialog, the EmailAddresses "Set" method is never called. How can I get my custom editor to actually save the property once it's done being edited?
I think I fixed it (with help from this question). I added an override to the EditValue function in my custom editor. Here is the code:
Public Overrides Function EditValue(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal provider As System.IServiceProvider, ByVal value As Object) As Object
Dim result As Object = MyBase.EditValue(context, provider, value)
DirectCast(context.Instance, CustomSettings).EmailAddresses = DirectCast(result, List(Of String))
Return result
End Function
I also moved from a collection to a list - I read somewhere that was a safer way to go. I also added a constructor to my CustomSettings class that set the EmailAddresses property to a new List(Of String) if it was unset to begin with. I found that the first time it ran, I could edit the list and add items, but they wouldn't be persisted:
Public Sub New()
If Me("EmailAddresses") Is Nothing Then
Me("EmailAddresses") = New List(Of String)
End If
End Sub
And now it's all working like it should. But if this isn't the best way or there's an easier way to do it, please chime in.

Create a dropdown list of valid property values for a custom control

I've created a custom user control that has several properties. One specifies which database I want the control to access. I want to be able to present the user of the control a drop down from which he can select which database the control will interact with.
How do I get the dropdown to work? I can get default values, but have yet to figure out how to get the selectable list.
Any help is apprectiated.
Thanks.
Marshall
You just need to attach your own TypeConverter to your property. You will override the GetStandardValuesSupported and GetStandardValues methods (maybe GetStandardValuesExclusive too) to return the list of databases you want to show.
If you are new to the PropertyGrid and the TypeConverter, here is a document.
It turns out to be simpler than I thought.
I had an enumeration set up for the property, but was having trouble using it for the property type. Said it was unaccessable outside of the class.
Then I had a 'duh' moment and changed the enumeration from Friend to Public, and then I was able to use the enumeration as the property type. As a result the values from the enumeration are listed in a dropdown when I look at the values for that property of the controls.
Thanks to all that answered.
Marshall
I'm a little confused about your problem.
If your user control contains a DropDownList control, just inititalize somewhere in the user control.
The easiest way is in the Codebehind for the usercontrol, just do DropDownList.Items.Add() or whatever the syntax is for adding an item.
Here is the template I use to create a custom datasource for my comboboxes:
Private Class Listing
Private _List As New ArrayList
Public Sub Add(ByVal ItemNumber As Integer, ByVal ItemName As String)
_List.Add(New dataItem(ItemNumber, ItemName))
End Sub
Public ReadOnly Property List() As ArrayList
Get
Return _List
End Get
End Property
End Class
Private Class dataItem
Private _ItemNumber As Integer
Private _ItemName As String
Public Sub New(ByVal intItemNumber As Integer, ByVal strItemName As String)
Me._ItemNumber = intItemNumber
Me._ItemName = strItemName
End Sub
Public ReadOnly Property ItemName() As String
Get
Return _ItemName
End Get
End Property
Public ReadOnly Property ItemNumber() As Integer
Get
Return _ItemNumber
End Get
End Property
Public ReadOnly Property DisplayValue() As String
Get
Return CStr(Me._ItemNumber).Trim & " - " & _ItemName.Trim
End Get
End Property
Public Overrides Function ToString() As String
Return CStr(Me._ItemNumber).Trim & " - " & _ItemName.Trim
End Function
End Class
And this is how I load it:
ListBindSource = New Listing
Me.BindingSource.MoveFirst()
For Each Row As DataRowView In Me.BindingSource.List
Dim strName As String = String.Empty
Dim intPos As Integer = Me.BindingSource.Find("Number", Row("Number"))
If intPos > -1 Then
Me.BindingSource.Position = intPos
strName = Me.BindingSource.Current("Name")
End If
ListBindSource.Add(Row("Number"), strName)
Next
cboNumber.DataSource = ListBindSource.POList
cboNumber.DisplayMember = "DisplayValue"
cboNumber.ValueMember = "Number"
AddHandler cboNumber.SelectedIndexChanged, AddressOf _
cboNumber_SelectedIndexChanged
Hope this helps. One thing to keep in mind is that if cboNumber has a handler already assigned to the SelectedIndexchanged event, you will encounter problems. So don't create a default event.