Refactor for Maintainance - vb.net

Public Sub ButtonVisibilityOnTab()
Select Case ctl.Parent.Name
Case "tabGeneral"
ctl.btnAdd.Visible = True
Case "tabSecond"
ctl.btnAdd.Visible = False
Case "tabThird"
ctl.btnAdd.Visible = False
Case Else
ctl.btnAdd.Visible = False
End Select
End Sub
How can I optimize/refactor the above code.
I have an app with 3 tabs and a common user control between them. I want to enable the visibility for a add button I have only the first tab.
How can I optimize the code so that in the future if can avoid another case if the user control is reused.
Should I pass the bool value with the tab control name to accomplish that.
or would an array of controls be logical as a pass in value for the method..

AS your code is written, it can be simplified to
Public Sub ButtonVisibilityOnTab()
ctl.btnAdd.Visible = (ctl.Parent.Name.ToLower().Equals("tabgeneral"))
End Sub
Or more generically,
Public Sub ButtonVisibilityOnTab(CompareTo as String)
ctl.btnAdd.Visible = (ctl.Parent.Name.ToLower().Equals(CompareTo.ToLower()))
End Sub

If you have a lot of these statement to do, you could create a setting class.
Class Settings
Public Property CanAdd As Boolean
End Class
This class would be inside each tabs and each tabs would set their own values. Then, your button visibility function would look at the current tab settings.
Public Sub ButtonVisibilityOnTab()
ctl.btnAdd.Visible = ctl.Parent.Setting.CanAdd
End Sub
You could then implement on all your tab an interface that handle the settings. Your function would then look like.
Public Sub ButtonVisibilityOnTab()
ctl.btnAdd.Visible = CType(ctl.Parent, ISomeInterface).Setting.CanAdd
End Sub

This is what worked for me, and I started passing the control Name as a parameter.
Private Sub ButtonVisibilityOnTab(controlName As String, blnShowOrNot As Boolean)
If _ctl.Parent.Name = controlName Then
_ctl.btnAdd.Visible = blnShowOrNot
Else
_ctl.btnAdd.Visible = Not (blnShowOrNot)
End If
End Sub

Related

vba Class Property Values, setting default values, storing values to string

another stumped newb here, trying to wrap my head around a problem.
I'm trying to use a class module, instead of public variables, with property get and set.
I want to set these values in a userform with text boxes, and update a listbox in the userform as they are entered, preferably on a text box event _afterupdate,
When the user hits the save button I'd like the profile attributes to be stored to a range.
No doubt this is going to be a laughable mess, but I've been stumped for days, and I'm coming here hat in hand. I just can't figure it out.
Class Mod Example:
Private mProfileName As String
Private mStartDate As Date
Private mEndDate As Date
Private mOngoing As Boolean
Sub Class_Initialise()
'Set default values for properties
mLastName = "Enter Last Name"
mStartDate = "Enter Date"
mEndDate = Date
mOngoing = True
End Sub
'********************************
'The relevant property procedures:
'********************************
Property Get ProfileName() As String
ProfileName = mProfileName
End Property
Property Let ProfileName(Value As String)
mProfileName = Value
End Property
Property Get EndDate() As Date
EndDate = mEndDate
End Property
Property Let EndDate (Value As Date)
mEndDate = Value
End Property
Property Get Ongoing() As Boolean
Ongoing = mOngoing
End Property
Property Let Ongoing(Value As Boolean)
If mEndDate = Date Then
mOngoing = True
End If
End Property
In the Userform I currently have:
Option Explicit
'hopefully not needed:
'Private mTextBoxUpdated As Boolean
'Private mListBoxUpdated As Boolean
'Private mEnteredText As String
'Private mIndexText As String
Private DictThisForm As Dictionary
Private ProfileData As clsProfileData
Private Sub UserForm_Initialize()
Debug.Print "UserForm Intialised"
Set DictThisForm = New Dictionary
Debug.Print "DictThisForm Created"
Set ProfileData = New clsProfileData
Call UserForm_UpdateListBox
And this laughable mess: (not even close to working)
Sub UserForm_UpdateListBox()
With lbxListBox1
.Clear
.ColumnCount = 2
.AddItem
'.List(0, 1) = "Profile Name",ProfileData.ProfileName '
'another attempt
'the below throws a Type Mismatch Error
.AddItem ProfileData.ProfileName, "Profile Name"
.AddItem ProfileData.StartDate, "Start Date"
.AddItem ProfileData.EndDate, "End Date"
.AddItem ProfileData.OnGoing, "Ongoing?"
And (with privately declared module level variables)
Private Sub tbxProfileName_AfterUpdate()
ProfileData.FirstName = tbxProfileName.Text
enter code here
Call UserForm_UpdateListBox
End Sub
At the moment I'm just trying to test the class module and see if I can get the properties values into a variable, or better, get those property values into a range on a hidden page, maybe via a dictionary, and update the list from there? Getting the property values into a listbox seems to be an unwieldy mess...
Sub testClassProfile()
Dim getPropertyAsStringVar As String
Dim NewProfile As clsProfileData
Set NewProfile = New clsProfileData
getPropertyAsStringVar = NewProfile.FirstName
Debug.Print getPropertyAsStringVar
End Sub
Current Debug.Print output is "" ie zip. No default values.
Any advice greatly appreciated.
Please let me know if I'm asking too much at once and I'll try to narrow the scope of the question just to my current issue, I thought the context might be helpful...
Not really an "answer", but it's too hard to respond to your comment with another comment!
Here is an example of how you can pass a class to a userform. Might help!
The TestModule Code
Option Explicit
Public Sub test()
'//Basic declaration of a UserForm
Dim testUF As UserForm1
Set testUF = New UserForm1
'//Now we create a test class - see the Property Get/Set for the string.
Dim testC1 As Class1
Set testC1 = New Class1
testC1.TestString = "Hello"
'//In the UserForm, I've created a property that accepts a class. Assign the
"TestClass" instance tothe UserForm Instance
testUF.Class1 = testC1
testUF.Show
End Sub
...And here is the userform. Note the property that I've added to accept a class instance.
Option Explicit
Private classInstance As Class1
Public Property Get Class1() As Variant
Set Class1 = classInstance
End Property
Public Property Let Class1(ByVal vNewValue As Variant)
Set classInstance = vNewValue
End Property
Private Sub UserForm_Click()
Me.Caption = classInstance.TestString
End Sub
And the class:
Option Explicit
Private mtest As String
Public Property Get TestString() As String
TestString = mtest
End Property
Public Property Let TestString(ByVal vNewValue As String)
mtest = vNewValue
End Property
When you run the "Test" code, you will see that instances of a class and a userform (which is actually just a "Class" as well) are created, and one instance is passed to another. It is (behind the scenes) passed "by reference", so any changes made to TestCl within the userform would be retained outside of the class, allowing it to be modified by the UserForm, and then returned back to the controlling code for use later.
The signature for the Initialize method is
Private Sub Class_Initialize()
... that's not what you have so it will not be run when an object based on the class is created. To make sure it's correct, inside your class code module select "Class" from the left-hand drop-down at the top, then select the required method from the right-hand selection.
Edit: this looks off -
Property Get Ongoing() As Boolean
Ongoing = mOngoing
End Property
Property Let Ongoing(Value As Boolean)
If mEndDate = Date Then
mOngoing = True
End If
End Property
The Ongoing property doesn't really depend on the backing field or need to be set (since its value depends only on mEndDate, so you can drop the Let and just use a Get something like this -
Property Get Ongoing() As Boolean
Ongoing = (mEndDate <= Date)
End Property

Changing Picture Box, BackgroundImage from an user control using another user control

I have some buttons from User Control(UserCtrl2) and want to dynamically change UserCtrl1,PictureBox BackgroundImage. From my code below, UserCtrl2 PictureBox BackgroundImage property did changed but winform is still showing the previous BackgroundImage.
I have tried the following methods,
me.refresh in UserCtrl1, still nothing happen.
Not sure how to implement {Get and Set} or dispose function.
Thanks in advance for any advise or reference.
Here is my code for UserCtrl1:
Public Class UserCtrl1
Public Sub UserCtrl1_Task(LEDno As UShort, LEDState As Boolean)
Select Case LEDno
Case 0 : Exit Sub
Case 1
If LEDState Then
PicBox_A.BackgroundImage = My.Resources.ResourceManager.GetObject("Blue_ON")
Else
PicBox_A.BackgroundImage = My.Resources.ResourceManager.GetObject("Blue_OFF")
End If
End Select
End Sub
End Class
Here is my Code for UserCtrl2:
Public Class UserCtrl2
Private ButtonClick(4) As Boolean
Private Sub Btn_A_Click(sender As Object, e As EventArgs) Handles Btn_A.Click
Dim InputControl = New UserCtrl1
If Not ButtonClick(0) Then
ButtonClick(0) = True
Btn_A.BackgroundImage = My.Resources.ResourceManager.GetObject("Switch1_ON")
InputControl.UserCtrl1_Task(1, True)
Else
ButtonClick(0) = False
Btn_A.BackgroundImage = My.Resources.ResourceManager.GetObject("Switch1_OFF")
InputControl.UserCtrl1_Task(1, False)
End If
End Sub
End Class

Call sub on class property change?

I have a custom class which is based off of a TreeNode and this has an Enum property on it called status, setup as shown below.
Public _staus As enumStatus
Public Enum enumStatus
None
Yes
No
End Enum
Basically when I change this property I want to call a sub routine which just changes the text colour of the item - this sub is contained in the class and is simply a select case statement updating the Me.ForeColor property.
This works correctly if I class myClass.ChangeColourBasedOnStatus but how can I make it automatically do this when a property is changed.
I've tried looking at event handlers but I just can't find an explanation that I understand and can get to work.
Any advice is greatly appreciated. :)
Make the field private and add a Property to access it. In the example below I assume that you are using text from a control, but you could modify it to use the enum or other types.
Private _staus As enumStatus
Public Enum enumStatus
None
Yes
No
End Enum
Public Property Status As enumStatus
Get
Return _staus
End Get
Set(value As enumStatus)
_staus = value
ChangeColor(TextBox1.Text)
End Set
End Property
Private Sub ChangeColor(SomeText As String)
Select Case SomeText
Case "" : Me.ForeColor = Color.Black
Case "Stop" : Me.ForeColor = Color.Red
Case "Go" : Me.ForeColor = Color.Green
End Select
End Sub

Show/hide different custom tabs, individually

I have my custom Ribbon tabs and I want to show or hide some of them upon given event. But I want to show/hide them individually, so the tabs should not be dependent on each other or anything like that.
I've been trying the examples from this documentation. and this one but no success.
This is the ribbon XML for the tabs:
<tab id="t1" label="CustomTab" getVisible="GetVisible" tag="xtab">
<!-- some other elements -->
</tab>
<tab id="t2" label="CustomTab_2" getVisible="GetVisible" tag="xtab_2">
<!-- some other elements -->
</tab>
and the VB code:
Private isVisible As Boolean = False
Public Sub GetVisible(control As Office.IRibbonControl, ByRef returnedVal As Boolean)
returnedVal = isVisible
End Sub
Private Sub RefreshRibbon(Tag As String)
ribbon.Invalidate()
End Sub
Public Sub show_xtab(ByVal control As Office.IRibbonControl)
isVisible = True
Call RefreshRibbon(Tag:="xtab")
End Sub
Public Sub hide_xtab(ByVal control As Office.IRibbonControl)
isVisible = False
Call RefreshRibbon(Tag:="xtab")
End Sub
Here I'm trying just with one of them, to make at least that one to work (and then I'm gonna take care of passing the Tag attribute dynamically). But this does not work.
However, if I change the GetVisible method to the following:
Public Function GetVisible(control As Office.IRibbonControl)
Return isVisible
End Function
it will work, but both of the tabs simultaneously. And I want to control them separately.
Any suggestions or tutorials ?
Update: tried some solution from suggestions from comments
XML is still the same. VB code:
Public MyTag as String
Sub GetVisible(control As Office.IRibbonControl, ByRef visible As Boolean)
If control.Tag Like MyTag Then
visible = True
Else
visible = False
End If
End Sub
Private Sub RefreshRibbon(Tag As String)
MyTag = Tag
ribbon.Invalidate()
End Sub
Public Sub show_xtab(ByVal control As Office.IRibbonControl)
Call RefreshRibbon(Tag:="xtab")
End Sub
Public Sub show_xtab_2(ByVal control As Office.IRibbonControl)
Call RefreshRibbon(Tag:="xtab_2")
End Sub
but still no success...
Okay, it seems pretty stupid and I don't know why it works like that, but the solution is the following - I just changed GetVisible from Sub to Function and removed the visible argument, so I directly return True or False, like this:
Public Function GetVisible(control As Office.IRibbonControl)
If control.Tag Like MyTag Then
Return True
Else
Return False
End If
End Function
The correct callback signature procedure type is Sub and If you remove "As Boolean" from the GetVisible callback signature it works as expected.
Signatures here: https://learn.microsoft.com/en-us/previous-versions/office/developer/office-2007/aa722523(v=office.12)?redirectedfrom=MSDN

How do you handle PropertyChange for a boolean value?

I'm creating a program in which I have a publicly defined boolean value
Public boolOverallStatus As Boolean = True
and I need to execute some code whenever the boolean value changes. In previous applications, an actual form item change handled this, but it can be changed by several different subs.
How would I handle this? I'm looking through msdn, but it's rather confusing.
In a nutshell: How to execute code when the event of a boolean value changing occurs.
Make it a property instead.
Private _boolOverallStatus As Boolean = True
Property boolOverallStatus As Boolean
Get
Return _boolOverallStatus
End Get
Set(ByVal value as Boolean)
If value <> _boolOverallStatus Then
_boolOverallStatus = value
'// handle more code changes here.'
End If
End Set
End Property
I use the following pattern which resembles what Microsoft do for most of their Changed events.
Class MyClass
Public Property OverallStatus As Boolean
Get
Return _OverallStatus
End Get
Set (value As Boolean)
If _OverallStatus = value Then Exit Property
_OverallStatus = value
OnOverallStatusChanged(EventArgs.Empty)
End Set
End Property
Private _OverallStatus As Boolean = False
Protected Overridable Sub OnOverallStatusChanged(e As EventArgs)
RaiseEvent OverallStatusChanged(Me, e)
End Sub
Public Event OverallStatusChanged As EventHandler
End Class
In VB, you can handle the event using the WithEvents and Handles keywords:
Class MyParent
Private WithEvents myObject As New MyClass()
Private Sub myobject_OverallStatusChanged(sender As Object, e As EventArgs) Handles myObject.OverallStatusChanged
' TODO: Write handler.
End Sub
End Class
The OnOverallStatusChanged function is useful for inheriting classes to get first shot at responding to the change.
Class MyOtherClass
Inherits MyClass
Protected Overrides Sub OnOverallStatusChanged(e As EventArgs)
' TODO: Do my stuff first.
MyBase.OnOverallStatusChanged(e)
End Sub
End Class
Use public properties instead of public variables. You can then put logic in the Set method of the property to execute whenever the property is.. well set.
http://msdn.microsoft.com/en-us/library/65zdfbdt%28v=VS.100%29.aspx
Private number As Integer = 0
Public Property MyNumber As Integer
' Retrieves number.
Get
Return number
End Get
' Assigns to number.
Set
CallLogicHere()
number = value
End Set
End Property
You could also define an event, which is fired each time the status changes. The advantage is, that changes can be handled by the parts of the application that depend on this status. Otherwise the logic would have to be implemented together with the status.
Other parts of the application can subscribe the event with AddHandler.
Public Class OverallStatusChangedEventArgs
Inherits EventArgs
Public Sub New(newStatus As Boolean)
Me.NewStatus = newStatus
End Sub
Private m_NewStatus As Boolean
Public Property NewStatus() As Boolean
Get
Return m_NewStatus
End Get
Private Set
m_NewStatus = Value
End Set
End Property
End Class
Module modGlobals
Public Event OverallStatusChanged As EventHandler(Of OverallStatusChangedEventArgs)
Private m_boolOverallStatus As Boolean = True
Public Property BoolOverallStatus() As Boolean
Get
Return m_boolOverallStatus
End Get
Set
If Value <> m_boolOverallStatus Then
m_boolOverallStatus = Value
OnOverallStatusChanged(Value)
End If
End Set
End Property
Private Sub OnOverallStatusChanged(newStatus As Boolean)
RaiseEvent OverallStatusChanged(GetType(modGlobals), New OverallStatusChangedEventArgs(newStatus))
End Sub
End Module