Method to return extending type instance from extended control - vb.net

In my solution, I have a custom component which implements IExtenderProvider in order to provide properties to other controls. I would like to implement a method for that component which, taking a control as argument, would return the instance of the extender component it is associated with, something like this:
Public Function GetErrorProvider(c As Control) As MyErrorProvider
Dim errorProvider as MyErrorProvider
'Some code here
Return errorProvider
End Function
I thought of simply looking at the form and looping for a control of the MyErrorProvider type and use that, as I am not going to have more than one of this component per form, but I would like a more direct approach. I want this for some logic that depends on runtime defined values for that instance, outside the scope of forms.
Any ideas/suggestions?
Thanks

For completeness purposes, I am adding the solution that worked converted from the C# code linked above and adjusted slightly. It appears this can only be accomplished with reflection (correct me if I am wrong!):
Public Shared Function GetErrorProvider(control As Control) As MyErrorProvider
'get the containing form of the control
Dim form = control.GetContainerControl()
'use reflection to get to "components" field
Dim componentField = form.[GetType]().GetField("components", BindingFlags.NonPublic Or BindingFlags.Instance)
If componentField IsNot Nothing Then
'get the component collection from field
Dim components = componentField.GetValue(form)
'locate the ErrorProvider within the collection
Return TryCast(components, IContainer).Components.OfType(Of MyErrorProvider)().FirstOrDefault()
Else
Return Nothing
End If
End Function

Related

Pass value from form to button text vb.net

I am learning vb.net and I'm having issues searching for what I need. I want to create a button that is "re-usable" throughout my application without needing to write code for each instance. So, what I would like to start with is take a variable in a form, example, public integer value and when this value changes I want to write to the text of a button. I know I can easily do this by writing code in the form btn_xxx.text = variable, but what if I have several buttons and each button looks at the same variable? Currently what I do is create a component which inherits a button and have a timer that on tick will look at the variable and write to the text. I'm sure there is a better way. Can anyone point me in the right direction? I know part of my problem is I don't know the nomenclature on what things are called, so hopefully I asked my question without too much confusion.
I saw this, https://www.daniweb.com/programming/software-development/threads/124842/detect-variable-change, but I don't see how to adapt that to my situation.
Here is what I have:
Private WithEvents Active_Alarm As New Nav_Active_Alarm
Then inside of a sub that calculates the count:
Active_Alarm.Count = CInt(dt_Active_Alarms.Rows.Count)
The user control:
Public Class Nav_Active_Alarm
Private mActive_Alarm_Count As Integer
Public Event Active_Alarm_Count_Changed(ByVal mvalue As Integer)
Public Property Count() As Integer
Get
Count = mActive_Alarm_Count
End Get
Set(ByVal value As Integer)
mActive_Alarm_Count = value
If Not Me.DesignMode Then
RaiseEvent Active_Alarm_Count_Changed(mActive_Alarm_Count)
test()
End If
End Set
End Property
Private Sub test()
If Not Me.DesignMode Then
If mActive_Alarm_Count = 0 Then
Me.btn_Goto_Active_Alarm.Image = My.Resources.Alarm_Clock_Static
'console or msgbox will work but updating the image will not
Else
Me.btn_Goto_Active_Alarm.Image = My.Resources.Alarm_Clock_Animation
'console or msgbox will work but updating the image will not
End If
End If
End Sub
End Class
If I write to console or add a msgbox I will see the event working. But, the image will not change. If I call the test sub from a timer it will work. Why won't the button update (by the way, I did try refresh and update in the code)?
Observer pattern is what you probably looking for.
This is quick and dirty.
Create a class to hold the variable value. Add a method that adds a button instance to a list.
Then a button that needs to know about the variable calls the register method.
When the value of the variable changes, it iterates through the list of buttons and sets the Text property of each one.
You might have jumped in a bit too deep too quick here. Google Custom data binding in .net, there's loads of built in stuff you can use. Though do it yourself is a good exercise.
A simple method to do this might be:
Create a form level list to hold the buttons you are interested in
Add the buttons you are interested in, into the list (maybe in form load or some other place where you have initialization code)
Create a private property in your form with a backing variable to hold the value you want to have applied to the buttons. In the setter portion spin through the list and set each buttons text.
Dim lstButtons As New List(Of Button)
Sub SetupButtons()
'call from form load or other init code
lstButtons.Add(btnPopulate)
lstButtons.Add(btnPopulate)
End Sub
Private _buttonText As String
Private Property ButtonText As String
Get
Return _buttonText
End Get
Set(value As String)
_buttonText = value
For Each b As Button In lstButtons
b.Text = value
Next
End Set
End Property
When you set the property - which now acts as your variable - it will update all of your textboxes for you.
I realize you mentioned without having to write code - but something has to tie things together. Even if you used the observer pattern (which is an elegant solution for this - so props to those who suggested it) you'd probably end up creating a class to hold the property and have that class implement the INotifyPropertyChanged from System.ComponentModel, and then you'd also have to have each button have a databinding for its text property to the property in the object of your class. There isn't really a way (that I can think of) to get around having to write some code for each form you do this in (though the class part you'd only have to write once of course).

Remove Properties and Events from UserControl vb.net

I´m devoloment my own userControl with vb.net. I´m new with this task.
I want to remove default properties.
After google, I found several topics, like this:
Removing certain properties in a user control, i.e. forcing one value and not editable in Design mode
So, I´m trying to use it, but doesn´t works for me. I don´t know what I missing or doing wrong.
Public Class MyControlDesigner
Inherits System.Windows.Forms.Design.ControlDesigner
Protected Overrides Sub PreFilterProperties(ByVal properties As System.Collections.IDictionary)
MyBase.PreFilterProperties(properties)
properties.Remove("BackColor")
properties.Remove("ForeColor")
properties.Remove("Font")
End Sub
End Class
<DesignerAttribute(GetType(MyControlDesigner))> _
Public Class MyUserControl
' ...
End Class
To hide overwrite properties I follow this topic Hiding inherited properties and this works fine, for some of them.
<Browsable(False), EditorBrowsable(EditorBrowsableState.Never)> _
Public Shadows Property AutoScroll() As Boolean
Get
Return m_AutoScroll
End Get
Set(ByVal value As Boolean)
m_AutoScroll = value
End Set
End Property
But still, I have other properties that I don´t know how to hide or remove. Like Font, ForeColor, Margin etc...
Thanks advanced
Edit: Once I finish my control, I don´t want to see, all the properties like the picture, Only I want to show mine´s.
Edit: Add code from #Plutonix
I do not have access to that control/tool/property editor, but you can try to use a TypeConverter. This works with a control that inherits from UserControl to hide properties from a Property Grid, but it wont hide them from the VS IDE property editor.
The VS IDE uses reflection to get the property list and apparently ignores the TypeConverter. If your tool does something similar, this wont work - again, I dont have the tool to test it, but it is simple and worth a try.
I created an actual UserControl with a few controls on it. Then:
Imports System.ComponentModel
Public Class YControlConverter
Inherits TypeConverter
Public Overrides Function GetPropertiesSupported(context As ITypeDescriptorContext) As Boolean
Return True
End Function
Public Overrides Function GetProperties(context As ITypeDescriptorContext,
value As Object,
attributes() As Attribute) As PropertyDescriptorCollection
Dim propNames() As String = {"backcolor", "forecolor",
"autoscroll", "autoscrollminsize",
"autoscrollmargin", "autoscrolloffset",
"autoscrollposition"}
Dim pdc As PropertyDescriptorCollection = TypeDescriptor.GetProperties(context.Instance)
' collection to store the ones we want:
Dim myPDCList As New List(Of PropertyDescriptor)
For Each pd As PropertyDescriptor In pdc
If propNames.Contains(pd.Name.ToLowerInvariant) = False Then
myPDCList.Add(pd)
End If
Next
Return New PropertyDescriptorCollection(myPDCList.ToArray())
End Function
End Class
Then decorate your usercontrol with the TypeConverter:
<TypeConverter(GetType(YControlConverter))>
Public Class YControl
This basically runs thru the PropertyDescriptorCollection for the control and filters out the unwanted properties before returning the new collection. If it works, just add the names to the propNames array that you want to hide. View in a PropertyGrid:
As you can see, all the AutoScroll... properties are removed as well as BackColor. The others are gone as well. If the editor will use your TypeConverter instead of reflection, it should work.
--
How to test your TypeConverter using a PropertyGrid. Using a form with a property grid and a button, in the button click:
Dim yp As New YControl
PropertyGrid1.SelectedObject = yp
If the AutoScroll... properties are missing from the prop grid, your TypeConverter works! If they still show in the other tool, it is using reflection like VS.

How to set current object (Me) to a new object stored in an array in Visual Basic for Excel

I have an array of objects in an excel vba project. I have created another instance of the same class and set 1 of its properties. I am then trying to search through the array of objects to find the object in the array that matches the current one on the same property. I would like to set the current object to the one in the array inside one of the current object's methods using the self reference Me.
I tried:
Set Me = objectArray(index)
This does not work. It says that this is an improper use of the Me keyword. Is there a way to set the current object to another object of the same type? Thanks!
Edit:
I have an object that has child objects:
Me.friShift.shiftType.loadFromArray
Here, shiftType is the object of type CVocabulary, which is my self defined class. It has a sub called loadFromArray that looks like this:
Public Sub loadFromArray()
Dim index As Integer
index = searchVocabArray(Me.typed)
If (index = -1) Then
Exit Sub
End If
Set Me = vocabArray(index)
End Sub
vocabArray() is a global array containing CVocabulary objects.
If it is not possible to Set an object from within itself, I can try something else. This is just the easiest and most direct way of doing this. I'm sure I can just set each parameter from the current object to the value of the parameter from the object in the array, but if it was possible to do something like the above, that would have been my preferred method.
You can do it by giving itself to the function as a parameter. I'll show it in VBScript because the classes are more clear, but the concept is the same as in VBA:
public myObject
set myObject = new x
myObject.ChangeMe MyObject
msgbox typename(myObject) ' <- outputs 'y'
class x
public sub changeMe(byref object)
set object = new y
end sub
end class
class y
' just an empty class
end class
But this is not a good programming pattern and could cause messy code (maintenance and debugging would be an issue) and even memory leaks. You should create an (Abstract) Factory, Builder or Provider that returns an object as you ask for it.
Factory: creates a new predefined object
Builder: creates a new object that is configured in the builder
Provider: returns an existing object that is predefined earlier
I don't beleive you can use Me in this context - you are trying to use Me as it was used in VB6 (which was equivalent to 'this' in C#). This is not appropriate in VBA.
Without some code snippets its hard to see what you are doing. Can you perform the search in a module and create instances of this class there? You can then do:
Set class2 = objectArrayofClass1(index)
As you've already seen that Me cannot be changed. You can handle memorized objects through
a function in a public Module like basExternal:
Public Function loadFromArrayByIndex(ByVal lIndex)
dim xobj as Object
Set xobj = vocabArray(lIndex)
'
' do modifications and handling on this object:
' ...
'
End Function
.

Passing two similar forms as the same type and still being able to access its objects

I am trying to implement another form into already complete routine. Basically all the code is there, all I need to do is manipulate the data in a different manner.
I have a routine that looks like this for instance.
This is a paraphrase example:
Private Sub getReportValues(ByRef fr As frmCustomReport, ByRef ReportInfo As ReportValues)
ReportInfo.eHeaderColor = Microsoft.Win32.Registry.CurrentUser.CreateSubKey("Software\FE Jupiter\MSSMonitor").GetValue("Report Equipment Header Color", "DCDCDC") 'Gainsboro
ReportInfo.mHeaderColor = Microsoft.Win32.Registry.CurrentUser.CreateSubKey("Software\FE Jupiter\MSSMonitor").GetValue("Report Monitor Header Color", "FFF8DC") 'Cornsilk
fr.btnEquipColor.PickedColor = System.Drawing.ColorTranslator.FromHtml("#" & Microsoft.VisualBasic.Conversion.Hex("&HFF" & ReportInfo.eHeaderColor))
The problem lays with the (fr as frmCustomReport) I want to make it a system.windows.forms.form but then I would lose the ability to use its objects. I should also mention that fr is a modal dialog and that I don't want a really hacky controlcollection work around for this. Does anyone have a good direction on this?
Note Also!!! The controls I want to access on both forms are almost identical. The only diffrence is layout and some added functionality.
Without a little more information,it is a little hard to give a concrete example. This will work depending on the amount of interaction you need to do. Create a subroutine that accepts the Base Class as an argument, take a look at the Name value and base your conditional logic off of that using CType to cast the Form to the proper type.
Private Sub clickOtherFormsButton(Value As Form)
If Value.Name = "Form3" Then
Dim formObject As Form3 = CType(Value, Form3)
formObject.Button1.PerformClick()
ElseIf Value.Name = "Form2" Then
Dim formObject As Form2 = CType(Value, Form2)
formObject.Button1.PerformClick()
End If
End Sub

How can I load a 'future' forms controls before I use them? Explained in detail:

I am performing a migration on a vb6 program, to vb.net. The basic knowledge you need to understand this question is that there are two forms that need to talk to each other, frmInput1 and frmInput2. I have the following code (behind frmInput1) that checks if a textbox on frmInput2 has a certain value, seemingly before it has loaded:
If frminput2.lblInputMac.Text <> "(no filename)" Then
Dim calc As CalculationCaster = New CalculationCaster
Call calc.FillMac()
cmdNext.Enabled = False
frminput2.FraInner.Enabled = True
I get the following error on the If line when i run it:
"Object reference not set to an instance of an object."
Which i assume means that the object in frmInput2 has not been loaded yet. How can i load frmInput2 before i show it?
Thanks
Nick
frminput2 is probably the implicit global instance of the type frminput2.
If you define a form type in VB6 called MyForm, the platform automatically creates an implicit global variable of the same name MyForm. Whenever you refer to this variable in code, it automatically loads an instance of the form for you.
It's rather as if you had this code.
Public Function MyForm() As MyForm
Static f As MyForm
If f Is Nothing Then
f = New MyForm
End If
Return f
End Function
dim frm1 as new frmInput1
dim frm2 as new frmInput2
At this point, you should be able to communicate between forms without them being displayed. You should not reference forms without explicitly instantiating them.
Create an instance of the form.
Dim f As New frmInput2
Then you can use any properties, methods, or controls on the form.
If f.lblInputMac.Text <> "(no filename)" Then
...
End If