how to handle properties with this FxCopAnalyzer warning: "CA2227: Set "Images" as read-only by removing the setter for the property" - vb.net

What is this programme about:
This program was developed with the VB.Net language, the .NET Framework 4.8 and Visual Studio 2019 CE. The idea of this program is to run a rudimentary database. The list is similar to a classic Internet forum—there are threads, there are different numbers of postings in the threads and in each post there are different numbers of pictures and long texts. If the thread is selected using the ComboBox, all posts with their images and texts are displayed one below the other. When you click on a specific post, only its images are displayed.
The user can create threads and posts. For reasons of legibility, only extended Latin letters are allowed when writing the text (e.g. René, Chloë).
When you close the program, you will be asked whether you want to save the data. These data are read in when the program is loaded. If images are not found, their paths will be displayed in a window.
The user also has the option of searching through all threads and viewing the results with various sorting options. In this case, only the posts found are listed in the ListBox, and here, too, the user can select the posts individually.
The program also reads in user data when it starts. A user can log in and, depending on his role, has certain power to make decisions. A “normal” user can create threads and posts, but only an administrator or moderator can edit and delete texts. If you are not logged in, you can only read threads and posts.
What I would like to know from you
I use the FxCopAnalyzer, which is currently giving me 8 warnings. I am aware that it sometimes exaggerates a little or criticizes certain things that were done on purpose. But I'd like to let you guys have a look to make it better. What exactly is this supposed to mean: "CA2227: Set "Images" (Bilder) as read-only by removing the setter for the property". Because when I remove the setter, I get an error message.
I feel the same way with other properties in other classes. So I need to know, how to fix this, and I need a general solution. I appreciate.
Class structure
There is the class Forum, the class Class_Thread and the class Class_Post. Class_Thread inherits from forum. In the Class_thread there is a list(of Class_Post) which contains instances of Class_Post. ("The thread knows what posts it has"). In FormMain, a List(of Class_Thread) is created, in which the instances of Class_Thread are.
In Class_Post's constructor, the following parameters are transferred: 1) the heading of the posting, 2) the text, 3) the number, 4) images as List(Of System.Drawing.Bitmap), 5) the date, 6) the image paths As List(Of String).
Class_Post.vb
#Disable Warning CA1707 ' Bezeichner dürfen keine Unterstriche enthalten
Public Class Class_Post : Inherits Class_Forum
Private _ueberschrift As String = ""
Private _text_dazu As String = ""
Private _nummer As UInt16
Private _bilder As List(Of System.Drawing.Bitmap)
Private _erstelldatum_dieses_Posts As Date
Private _pfade_der_Bilder As List(Of String)
Public Property Ueberschrift As String
Get
Return _ueberschrift
End Get
Set(value As String)
_ueberschrift = value
End Set
End Property
Public Property Text_dazu As String
Get
Return _text_dazu
End Get
Set(value As String)
_text_dazu = value
End Set
End Property
Public Property Nummer As UShort
Get
Return _nummer
End Get
Set(value As UShort)
_nummer = value
End Set
End Property
Public Property Bilder As List(Of Bitmap)
Get
Return _bilder
End Get
Set(value As List(Of Bitmap))
_bilder = value
End Set
End Property
Public Property Erstelldatum_dieses_Posts As Date
Get
Return _erstelldatum_dieses_Posts
End Get
Set(value As Date)
_erstelldatum_dieses_Posts = value
End Set
End Property
Public Property Pfade_der_Bilder As List(Of String)
Get
Return _pfade_der_Bilder
End Get
Set(value As List(Of String))
_pfade_der_Bilder = value
End Set
End Property
Public Sub New(ByVal Ueberschrift As String, ByVal Text As String, ByVal nr As UInt16, ByVal uebergebene_Bilder As List(Of System.Drawing.Bitmap), ByVal _date As Date, ByVal Pfade As List(Of String))
Me.Ueberschrift = Ueberschrift
Me.Text_dazu = Text
Me.Nummer = nr
Me.Bilder = uebergebene_Bilder
Me.Erstelldatum_dieses_Posts = _date
Me.Pfade_der_Bilder = Pfade
End Sub
End Class
#Enable Warning CA1707 ' Bezeichner dürfen keine Unterstriche enthalten
This is in FormMain, where a new Instance of Class_Post is created This Sub is using a second Form from which the data is taken.
Private Sub Button_neueAntwort_Click(sender As Object, e As EventArgs) Handles Button_neueAntwort.Click
' doppelt abgesichert, falls die Variable SI aus irgendeinem Grunde nicht (-1) ist, obwohl sie das sollte.
If SI <> (-1) OrElse ComboBox1.SelectedItem IsNot Nothing Then
Using FA As New Form_Antwort
Dim DR As DialogResult = FA.ShowDialog(Me)
If DR = DialogResult.Yes Then
Dim neuerPost As New Class_Post(FA.Titel_des_Posts, FA.Beschreibung, CUShort(Liste_mit_allen_Threads(SI).Posts_in_diesem_Thread.Count + 1), FA.NeueListeBitmaps, Date.Now, FA.Liste_mit_Pfaden)
Liste_mit_allen_Threads(SI).Posts_in_diesem_Thread.Add(neuerPost)
alle_Posts_in_diesem_Thread_anzeigen()
End If
End Using
Else
MessageBox.Show("Bitte zuerst einen Thread auswählen.", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information)
End If
End Sub

Thanks to Jimi and Craig, I can come up with a solution here. The thing is, unfortunately I let the FxCopAnalyzer influence me a bit. It had suggested using getters and setters instead of the normal fields, and that's why the warning I reported about came up. But the solution looks very simple, just use properties:
Public Class Class_Post : Inherits Class_Forum
Public ReadOnly Property Ueberschrift As String = ""
Public ReadOnly Property Text_dazu As String = ""
Public ReadOnly Property Nummer As UInt16
Public ReadOnly Property Bilder As List(Of System.Drawing.Bitmap)
Public ReadOnly Property Erstelldatum_dieses_Posts As Date
Public ReadOnly Property Pfade_der_Bilder As List(Of String)
Public Sub New(ByVal Ueberschrift As String, ByVal Text As String, ByVal nr As UInt16, ByVal uebergebene_Bilder As List(Of System.Drawing.Bitmap), ByVal _date As Date, ByVal Pfade As List(Of String))
Me.Ueberschrift = Ueberschrift
Me.Text_dazu = Text
Me.Nummer = nr 'kann maximal 65535 werden
Me.Bilder = uebergebene_Bilder
Me.Erstelldatum_dieses_Posts = _date
Me.Pfade_der_Bilder = Pfade
End Sub
End Class
Edit:
Of course, this is only true until you decide to create a button where a moderator wants to delete an image. Then, ReadOnly has to be removed again and the FxCopAnalyzer complains again.

Related

Extracting property values from a dictionary

I am attempting to write a subroutine that will deserialize a dictionary from a .ser file (this bit works fine) and then repopulate several lists from this dictionary (this is the bit I cannot do).
The dictionary contains objects (I think) of a custom class I wrote called "Photo Job" which has properties such as ETA, notes, medium etc. (Declared as such)
Dim photoJobs As New Dictionary(Of String, PhotoJob)
In short, I want to be able to extract every entry of each specific property into an separate arrays (one for each property) and I can go from there.
Any help would be appreciated, I may be going about this completely the wrong way, I'm new to VB. The relevant code is below:
Photo Job Class:
<Serializable()> _Public Class PhotoJob
Private intStage As Integer 'Declare all local private variables
Private ID As String
Private timeLeft As Integer
Private material As String '
Private note As String
Private path As String
Private finished As Boolean = False
'Declare and define properties and methods of the class
Public Property productionStage() As Integer
Get
Return intStage
End Get
Set(ByVal Value As Integer)
intStage = Value
End Set
End Property
Public Property photoID() As String
Get
Return ID
End Get
Set(ByVal Value As String)
ID = Value
End Set
End Property
Public Property ETA() As Integer
Get
Return timeLeft
End Get
Set(ByVal Value As Integer)
timeLeft = Value
End Set
End Property
Public Property medium() As String
Get
Return material
End Get
Set(ByVal Value As String)
material = Value
End Set
End Property
Public Property notes() As String
Get
Return note
End Get
Set(ByVal Value As String)
note = Value
End Set
End Property
Public Property imagePath() As String
Get
Return path
End Get
Set(ByVal Value As String)
path = Value
End Set
End Property
Public Property complete() As Boolean
Get
Return finished
End Get
Set(value As Boolean)
finished = value
End Set
End Property
Public Sub nextStage()
If intStage < 4 Then
intStage += 1
ElseIf intStage = 4 Then
intStage += 1
finished = True
End If
End Sub
End Class
Subroutines involved in de/serialisation:
Private Sub BackupAllToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles BackupAllToolStripMenuItem.Click
Dim formatter As New BinaryFormatter
Dim backupFile As New FileStream(Strings.Replace(Strings.Replace(Now, ":", "_"), "/", ".") & ".ser", FileMode.Create, FileAccess.Write, FileShare.None)
formatter.Serialize(backupFile, photoJobs)
backupFile.Close()
MsgBox("Collection saved to file")
End Sub
Private Sub RestoreFromFileToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles RestoreFromFileToolStripMenuItem.Click
With OpenFileDialog 'Executes the following sets/gets/methods of the OpenFileDialog
.FileName = ""
.Title = "Open Image File"
.InitialDirectory = "c:\"
.Filter = "Serial Files(*.ser)|*ser"
.ShowDialog()
End With
Dim backupPathStr As String = OpenFileDialog.FileName
Dim deSerializer As New BinaryFormatter
Dim backupFile As New FileStream(backupPathStr, FileMode.Open)
photoJobs = deSerializer.Deserialize(backupFile)
backupFile.Close()
End Sub
From what I can see using the autos menu, the saving/restoring of the dictionary works just fine.
First, if you are using VS2010+, you can greatly reduce boilerplate code using autoimplemented properties:
<Serializable()>
Public Class PhotoJob
Public Property productionStage() As Integer
Public Property photoID() As String
Public Property ETA() As Integer
etc
End Class
That is all that is needed, all the boilerplate code is handled for you. Second, with this line:
photoJobs = deSerializer.Deserialize(backupFile)
Your deserialized photojobs will be a generic Object, not a Dictionary. You should turn on Option Strict so VS will enforce these kinds of errors. This is how to deserialize to Type:
Using fs As New FileStream(myFileName, FileMode.Open)
Dim bf As New BinaryFormatter
PhotoJobs= CType(bf.Deserialize(fs), Dictionary(Of String, PhotoJob))
End Using
Using closes and disposes of the stream, CType converts the Object returned by BF to an actual dictionary
To work with the Dictionary (this has nothing to do with Serialization) you need to iterate the collection to get at the data:
For Each kvp As KeyValuePair(Of String, PhotoJob) In PhotoJobs
listbox1.items.Add(kvp.value.productionStage)
listbox2.items.Add(kvp.value.ETA)
etc
Next
The collection is a made of (String, PhotoJob) pairs as in your declaration, and when you add them to the collection. They comeback the same way. kvp.Key will be the string key used to identify this job in the Dictionary, kvp.Value will be a reference to a PhotoJobs object.
As long as VS/VB knows it is a Dictionary(of String, PhotoJob), kvp.Value will act like an instance of PhotoJob (which it is).

Problems with extending s.ds.am.UserPrincipal class

I have been trying to extend the s.ds.am.UserPrincipal class in VS 2010 VB app as I require access to AD User properties that are not by default a part of the UserPrincipal. This is a new concept to me and I have found some useful code on here as well but I have run into 2 problems with my extensions that I cannot seem to figure out.
Problem 1: When I attempt to use my UserPrincipalEx to retrieve a user I receive the following error.
ERROR:
Principal objects of type MyApp.UserPrincipalEx can not be used in a query against this store
CODE TO PRODUCE ERROR:
Dim ctx As New PrincipalContext(ContextType.Domain, DomainController)
Dim user As UserPrincipalEx = UserPrincipalEx.FindByIdentity(ctx, samAccountName)
Problem 2: When I attempt to create a new user with my UserPrincipalEx I receive the following error. When I trace the PrincipalContext (ctx) into the class it appears to disconnect from the DomainController.
ERROR:
Unknown error (0x80005000)
CODE TO PRODUCE ERROR:
Dim ctx As New PrincipalContext(ContextType.Domain, DomainController, DirectoryOU)
Dim NewADUser As New UserPrincipalEx(ctx, newsamAccountName, newPassword, True)
CLASS:
Imports System.DirectoryServices.AccountManagement
Public Class UserPrincipalEx
Inherits UserPrincipal
Public Sub New(ByVal context As PrincipalContext)
MyBase.New(context)
End Sub
Public Sub New(ByVal context As PrincipalContext, ByVal samAccountName As String, ByVal password As String, ByVal enabled As Boolean)
MyBase.New(context, samAccountName, password, enabled)
End Sub
<DirectoryProperty("Title")> _
Public Property Title() As String
Get
If ExtensionGet("title").Length <> 1 Then
Return Nothing
End If
Return DirectCast(ExtensionGet("title")(0), String)
End Get
Set(ByVal value As String)
Me.ExtensionSet("title", value)
End Set
End Property
<DirectoryProperty("physicalDeliveryOfficeName")> _
Public Property physicalDeliveryOfficeName() As String
Get
If ExtensionGet("physicalDeliveryOfficeName").Length <> 1 Then
Return Nothing
End If
Return DirectCast(ExtensionGet("physicalDeliveryOfficeName")(0), String)
End Get
Set(ByVal value As String)
Me.ExtensionSet("physicalDeliveryOfficeName", value)
End Set
End Property
<DirectoryProperty("Company")> _
Public Property Company() As String
Get
If ExtensionGet("company").Length <> 1 Then
Return Nothing
End If
Return DirectCast(ExtensionGet("company")(0), String)
End Get
Set(ByVal value As String)
Me.ExtensionSet("company", value)
End Set
End Property
' Implement the overloaded search method FindByIdentity.
Public Shared Shadows Function FindByIdentity(ByVal context As PrincipalContext, ByVal identityValue As String) As UserPrincipalEx
Return DirectCast(FindByIdentityWithType(context, GetType(UserPrincipalEx), identityValue), UserPrincipalEx)
End Function
' Implement the overloaded search method FindByIdentity.
Public Shared Shadows Function FindByIdentity(ByVal context As PrincipalContext, ByVal identityType As IdentityType, ByVal identityValue As String) As UserPrincipalEx
Return DirectCast(FindByIdentityWithType(context, GetType(UserPrincipalEx), identityType, identityValue), UserPrincipalEx)
End Function
End Class
PROBLEM 1
I was able to solve Problem 1 by adding the following to the top of the class after the Imports
<DirectoryRdnPrefix("CN")> _
<DirectoryObjectClass("user")> _
PROBLEM 2
I have also been banging my head against this for to long and was finally able to resolve Problem 2. The DirectoryOU variable did not contain a correct path the intended AD OU. I was missing a comma and after staring at everything for 30 minutes I finally noticed the issue.

How do I pass a List through the PreviousPage property?

I'm trying to pass some values to another page and I have the values in List (Of String). However, I'm getting an error (at design-time in the IDE) saying: There is no member "X" of System.Web.UI.Page. I was able to get the Property through Intellisense. So I'm not sure why I'm getting the error.
Here's the code:
'Source Page (Default.aspx)-
Private locationDetails As List(Of String)
Public ReadOnly Property LocationsForDetail() As List(Of String)
Get
Return locationDetails
End Get
End Property
Private loadDetails As List(Of String)
Public ReadOnly Property LoadsForDetail() As List(Of String)
Get
Return loadDetails
End Get
End Property
Private optionDetails As List(Of String)
Public ReadOnly Property OptionsForDetail() As List(Of String)
Get
Return optionDetails
End Get
End Property
Protected Sub RadButton2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles RadButton2.Click
For Each facility As ListItem In CheckBoxList1.Items
If facility.Selected Then
locationDetails.Add(facility.Text)
End If
Next
For Each load As ListItem In CheckBoxList2.Items
If load.Selected Then
loadDetails.Add(load.Text)
End If
Next
optionDetails.Add(DropDownList2.SelectedValue)
optionDetails.Add(DropDownList3.SelectedValue)
optionDetails.Add(DropDownList4.SelectedValue)
optionDetails.Add(RadDatePicker1.SelectedDate.Value.ToShortDateString)
optionDetails.Add(RadDatePicker2.SelectedDate.Value.ToShortDateString)
Server.Transfer("Breakdown.aspx")
End Sub
'Target Page (Breakdown.aspx) -
Protected Sub populateChart()
Dim locations As List(Of String) = PreviousPage.LocationsForDetail 'get error here
Dim capacities As List(Of String) = PreviousPage.LoadsForDetail 'get error here
Dim rescOptions As List(Of String) = PreviousPage.OptionsForDetail 'get error here
bindData.populateChart(RadChart1, locations, capacities, rescOptions)
End Sub
Server.Transfer wouldn't do a postback to the page. I believe (however it's been a long time since I have used PreviousPage) that it only works when you the the PostBackURL property on a control.
It seems that even though the IDE marked it as an error, the code compiled and ran fine. I'm not sure if I'm lucky, the IDE is buggy, or it's some sort of undefined behavior.

Subclass/Superclass - If a subclass is cast as its superclass, is there a way to use the overloaded properties of the subclass?

Sorry if the title isn't very clear. This is a VB.NET (2010) question
I have a superclass called "Device" which has a number of subclasses that inherit it. Some of those subclasses also have subclasses. In particular, I have a class called "TwinCatIntegerDevice" which inherits "TwinCatDevice" which inherits "Device."
The relevant parts of Device look like this:
Public Class Device
Private _Setpoints As New List(Of Double)
Public Overridable Property Setpoints As List(Of Double)
Get
Return _Setpoints
End Get
Set(ByVal value As List(Of Double))
_Setpoints = value
_SetpointsTb.Clear()
For Each setpoint In value
Dim setpointTb As New TextBox
setpointTb.Text = setpoint.ToString
_SetpointsTb.Add(setpointTb)
Next
End Set
End Property
Private _SetpointsTb As New List(Of TextBox)
Public Overridable Property SetpointsTb As List(Of TextBox)
Get
Return _SetpointsTb
End Get
Set(ByVal value As List(Of TextBox))
_SetpointsTb = value
Me._Setpoints.Clear()
For Each setpoint In value
Me._Setpoints.Add(setpoint.Text)
Next
End Set
End Property
End Class
The TwinCatDevice class does not overload Setpoints or SetpointsTb.
The TwinCatIntegerDevice class does:
Public Class TwinCatIntegerDevice
Inherits TwinCatDevice
Private _Setpoints As New List(Of Integer)
Public Overloads Property Setpoints As List(Of Integer)
Get
Return _Setpoints
End Get
Set(ByVal value As List(Of Integer))
_Setpoints = value
_SetpointsTb.Clear()
For Each setpoint In value
Dim setpointTb As New TextBox
setpointTb.Text = setpoint.ToString
_SetpointsTb.Add(setpointTb)
Next
End Set
End Property
Private _SetpointsTb As New List(Of TextBox)
Public Overloads Property SetpointsTb As List(Of TextBox)
Get
Return _SetpointsTb
End Get
Set(ByVal value As List(Of TextBox))
_SetpointsTb = value
Me._Setpoints.Clear()
For Each setpoint In value
Me._Setpoints.Add(setpoint.Text)
Next
End Set
End Property
End Class
Now, the problem. I try to set the setpoints using a subroutine like so:
Private Sub FetchDeviceRecipe(ByRef device As Device, ByRef excelSheet As ExcelWorksheet, ByVal row As Integer)
Dim lastCol As Integer = NumberOfProcessSteps + 1
Try
For col = 2 To lastCol
Dim setpoint As New TextBox
setpoint.Text = excelSheet.Cells(row, col).Value
device.SetpointsTb.Add(setpoint)
Next
device.SetpointsTb = device.SetpointsTb
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
(I know that is terrible code :X, I'm a beginner)
Crucially, I'm passing the device as the Device superclass (so that I don't have to have a separate subroutine for each sub-type).
When I do this for a TwinCatIntegerDevice called "ThisDevice" after it has been passed to the subroutine:
MsgBox("As Device: " & CType(ThisDevice, Device).Setpoints.Count.ToString & vbNewLine & _
"As TwinCatDevice: " & CType(ThisDevice, TwinCatDevice).Setpoints.Count.ToString & vbNewLine & _
"As TwinCatIntegerDevice: " & CType(ThisDevice, TwinCatIntegerDevice).Setpoints.Count.ToString)
I get the following (9 is the correct number of setpoints in this case):
As Device: 9
As TwinCatDevice: 9
As TwinCatIntegerDevice: 0
Does anyone know why the TwinCatInteger device class apparently has a different variable for Setpoints when it is cast as its superclass Device?
I'm sorry if this seems a bit incoherent. Any help would be great! Even regarding form or anything else. I'm still trying to figure this whole VB.NET thing out.
I think what you want to do, is remove your "OverLoads" keyword on the "TwinCatIntegerDevice" class, and replace it with the keyword "Shadows".

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.