Event handling for an array of objects [duplicate] - arrays

I have a user form that displays line-by-line validation errors (in text box) that I want to supplement with user form labels that act as hyperlinks that users can click to go directly to the cell with issues.
I have code that builds labels on the fly and have added a click event through class modules but I cannot get it the click event in the class module to fire.
I did modify this code from working code that builds this type of label and click event on the fly, but that code loads labels at userform initiation and places each class object into a collection. I don't know if that is necessary to build into my solution, but I played with it and could not get it to work.
Here is my procedure to place label on the userform if needed. It runs inside another procedure if validation is needed. Userform is than shown, filled out with message (and this one label that gets created for now), if validation is needed.
Sub PlaceLinkLabel(SayWhat As String, WhichSheet As String, WhichRange As String)
Dim lblNew As MSForms.Label
Set lblNew = frmValidationMessage.Controls.Add(bstrProgID:="Forms.Label.1", Name:=SayWhat, Visible:=True)
With lblNew
With .Font
.Size = 10
.Name = "Comic Sans MS"
End With
.Caption = SayWhat
.Top = 55
.Height = 15
.Left = 465
.Width = 100
End With
Dim clsLabel As UserFormLabelLinks
Set clsLabel = New UserFormLabelLinks
Set clsLabel.lbl = lblNew
With clsLabel
.WhichRange = WhichRange
.WhichSheet = WhichSheet
End With
'not sure if this is needed or not
'Dim pLabels As Collection
'Set pLabels = New Collection
'pLabels.Add clsLabel
End Sub
Here is UserFormLabelLinks class module:
Option Explicit
Private WithEvents pLabel As MSForms.Label
Private sWhichRange As String
Private sWhichSheet As String
Public Property Set lbl(value As MSForms.Label)
Set pLabel = value
End Property
Public Property Get WhichSheet() As String
WhichSheet = sWhichSheet
End Property
Public Property Let WhichSheet(value As String)
sWhichSheet = value
End Property
Public Property Get WhichRange() As String
WhichRange = sWhichRange
End Property
Public Property Let WhichRange(value As String)
sWhichRange = value
End Property
Private Sub pLabel_Click()
MsgBox "hi" 'when i click label, this does not fire
'Application.Goto ThisWorkbook.Worksheets(WhichSheet).Range(WhichRange), True
'ActiveWorkbook.FollowHyperlink ("#" & WhichSheet & "!" & WhichRange)
End Sub

The MSForms.Label object is going out of scope as soon as PlaceLinkLabel exits, as does the UserFormLabelLinks object reference; thus you're creating a label, but it's a fire-and-forget thing that you can't programmatically access as soon as End Sub is reached, hence the events never fire.
You need a private field to hold on to the UserFormLabelLinks object reference (and thus keep the MSForms.Label reference around via the encapsulated pLabel field):
Option Explicit
Private clsLabel As UserFormLabelLinks
Then remove this line in the procedure:
Dim clsLabel As UserFormLabelLinks
In other words, promote that local variable to a field, to keep it around after the procedure has completed.

Another approach that worked:
Placing
Private pLabels As Collection atop the module where PlaceLinkLabel is stored
and using
If pLabels Is Nothing Then Set pLabels = New Collection
pLabels.Add clsLabel
at the end of PlaceLinkLabel module

Related

Pass data from mainwindow > tabitem > scroller > frame > content

I asked this question already but now I have a better understanding of what I'm trying to do. I have a WPF application with a mainwindow class. From there, tabitems can be created. For example, I have an account tab. My problem is that I want to use the account.xaml for the "edit account' and the "add account" button. How can I tell account.xaml.vb whether it is in edit mode or add mode? Similarly - how do I tell account.vb which account it is editing from a dialogue box that I show when "edit account" is clicked?
Here is the tab being created in mainwindow.xaml.vb
Private Sub btn_AddAccount_Click(sender As Object, e As
RoutedEventArgs) Handles btn_AddAccount.Click
Dim tab_NewAccount As New C1TabItem()
Dim frame_NewAccount As New Frame()
Dim scroller_NewAccount As New ScrollViewer()
Dim str_Name As String = "Add Account"
Dim str_NavigationLink As String = "PM_AddAccount.xaml"
Dim account As New PM_AddAccount
account.mode = 1
'Add and name new tab
tab_NewAccount.Header = tabcontrol.Items.Count + 1 & ". " & str_Name
tab_NewAccount.CanUserClose = True
tabcontrol.Items.Add(tab_NewAccount)
'Add frame to the tab and include new page
With frame_NewAccount
.NavigationService.Navigate(New Uri(str_NavigationLink, UriKind.Relative))
.HorizontalAlignment = HorizontalAlignment.Stretch
.VerticalAlignment = VerticalAlignment.Top
.Margin = New Thickness(0, 0, 0, 0)
End With
With scroller_NewAccount
.CanContentScroll = vbTrue
.VerticalScrollBarVisibility = ScrollBarVisibility.Auto
.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto
.Content = frame_NewAccount
End With
tab_NewAccount.Content = scroller_NewAccount
' Set new tab as active tab
tabcontrol.SelectedIndex = tabcontrol.Items.IndexOf(TAB)
End Sub
Edit:
So I tried giving Account.xaml.vb a public property and then setting that in mainwindow.xaml.vb However, the value isn't getting set - I realize that the account variable in mainwindow.xaml.vb isn't connected anything but frame.content = account doesnt work either.
Class Account
Public Property mode As AccountMode
Get
Return mode
End Get
Set(value As AccountMode)
value = mode
End Set
End Property
Public Enum AccountMode
None = 0
Add = 1
Edit = 2
End Enum
End Class
Define an enum called AccountMode
Public Enum AccountMode
None = 0
Add = 1
Edit = 0
End Enum
Add a property to account.xaml.vb called Mode And set it appropriately as needed.
In regard to how to tell it which account it is editing, that question is vague. Typically you would be binding your control to a data source and that data source will have some kind of key or identifier that makes it unique from all other records. For example, if you are binding your data from a database there would be a Primary Key and/or Identity Field.
Your data would be bound to a model class like this:
Public Class Account
[Key]
Public Property Id As Integer
Public Property FirstName As String
Public Property LastName As String
End Class
Likewise, Instead of have a mode in your Account class you could incorporate logic that detects you are adding a new Account if the Account.Id = 0. Otherwise if Account.Id>0 then you are editing an existing account.

Update a data bound list box

I have a section of code that fires when a check box is checked/unchecked.
The check box is going to be used to narrow down a listbox that is data bound. One item in the data is a link to an png image which shows correctly when the application first initializes.
In the code below, I get a "Initialization of 'System.Windows.Media.Imaging.BitmapImage' threw an exception" exception every time.
Private Sub chkShowCurrent_Click(sender As Object, e As RoutedEventArgs) Handles chkShowCurrent.Click
Dim o As ObjectDataProvider
Dim xDoc = XDocument.Load(sFileName)
If chkShowCurrent.IsChecked = False Then
'MsgBox("false")
End If
If chkShowCurrent.IsChecked = True Then
'MsgBox("true")
Dim OnlyCurrent = From job In xDoc.Root.Descendants("Job")
Where job.Element("Status").Value = "a-current"
Order By job.Element("Status").Value, job.Element("Rush").Value, Convert.ToDateTime(job.Element("DateOut").Value)
Select job
lstJobs.ItemsSource = OnlyCurrent
End If
lstJobs.Items.Refresh()
o = FindResource("jobs")
o.Refresh()
End Sub
I had originally thought that the lstjobs.items.refresh was the issue, but the exception doesn't popup until after End Sub has been reached. I have tried adding a breakpoint to the code at the checkbox code and step through the code, but the answer still eludes me. Can anyone tell me where my error is?
**** Edit for more info below ****
Public Property Artwork() As String
Get
Return _Artwork
End Get
Set(ByVal value As String)
If value = String.Empty Then value = "images\Untitled.png"
If My.Computer.FileSystem.FileExists("\\ARTSTATION\Users\Public\XML Job Board\" & value) Then
value = "\\ARTSTATION\Users\Public\XML Job Board\" & value
Else
value = "\\ARTSTATION\Users\Public\XML Job Board\images\Untitled.png"
End If
_Artwork = value
End Set
The xml element is formated as image\GUID.png, thus necessitating the need to add the full path at runtime.
I had originally explored this as the issue and changed the XML to contain the full path and commented out the need for adding the full path in the properties, but this yielded the same results.

Reference a cell in another workbook. And make my query not refresh if values are equal

It says I have too few parameters expected 15... It is something with my before and after refresh.
Class 1
Public WithEvents qt As QueryTable
Private Sub qt_AfterRefresh(ByVal Success As Boolean)
Application.Worksheets("RawDataLines").Range("A1") = Application.Worksheets("RawDataLines").Range("C1")
Application.Run "'Operation Get Ipads.xls'!Assembly1_Button"
End Sub
Class 2
Public WithEvents qut As QueryTable
Private Sub qut_BeforeRefresh(Cancel As Boolean)
Worksheets("RawDataLines").Range("C1") = _
"='H:\Departments\Manufacturing\Production Links\DashBoard Breakdown\[MASTER_LIVE_STATUS_DATA.xls]Sheet1'!R1C1"
If Application.Worksheets("RawDataLines").Range("C1") = Application.Worksheets("RawDataLines").Range("A1") Then
Cancel = True
End If
End Sub
Initialize:
Dim T As New Class1
Dim H As New Class2
Sub Initialize_It()
Set T.qt = ThisWorkbook.Sheets(3).QueryTables(1)
Set H.qut = ThisWorkbook.Sheets(3).QueryTables(1)
End Sub
Private Sub qt_BeforeRefresh(ByVal Success As Boolean)
the argument is usually Cancel and this is what your code refers to. Change it to Cancel.
You can refer to the current workbook (where this code is running) as ThisWorkbook. Assuming it is the MASTER one, then you can use:
ThisWorkbook.Worksheets("sheet1").Range("A1")
The Workbooks collection refers only to open workbooks, so you will need to temporarily open the other workbook:
Dim wbOther As Workbook
Set wbOther = Workbooks.Open("full path and filename.xlsx", False)
the False argument indicates that you do not wish to Update Links when opening the book (if appropriate).
When you have finished with the other book, use:
wb.Close False 'False says that you do not need to Save Changes
Set wb = Nothing
It is possible to get a single value from another workbook without opening and closing it, but if that book also has links this may cause an issue:
Debug.Print ExecuteExcel4Macro("'F:\Documents and Settings\student\My Documents\[AndysData7.xlsx]Staff List'!R6C4")
Note, the formula needs to use R1C1 notation. (Rather than Debug.Print you would store the value in a variable.) Note also that I am not necessarily recommending this approach, as it is undocumented, but thought I'd mention it in the context of the question.

How to have a global Dictionary in VB.NET/WPF application to save data from different windows?

I am new to VB.NET and WPF.
I am building a "Questionnaire" app. Users will be presented sequentially with different questions/tasks (windows). After they respond on each question/task and press a "submit" button a new window will open with a new question/task, and previous window will close. After each question, when the button is pressed, I need to store data to some global object. After all questions are answered the data of this object should be written out to the output file.
I figured out that Dictionary will be the best to store the results after each window.
I am not sure how, where to create this global Dictionary and how to access it. Should I use View Model? If yes, can you give an example? Or, should it be just a simple class with shared property? (something like this)
EDIT 2: I tried many different ways recommended online
GlobalModule:
Module GlobalModule
Public Foo As String
End Module
GlobalVariables:
Public Class GlobalVariables
Public Shared UserName As String = "Tim Johnson"
Public Shared UserAge As Integer = 39
End Class
Global properties:
Public Class Globals
Public Shared Property One As String
Get
Return TryCast(Application.Current.Properties("One"), String)
End Get
Set(ByVal value As String)
Application.Current.Properties("One") = value
End Set
End Property
Public Shared Property Two As Integer
Get
Return Convert.ToInt32(Application.Current.Properties("Two"))
End Get
Set(ByVal value As Integer)
Application.Current.Properties("Two") = value
End Set
End Property
End Class
Here is where I save the data to global variables/properties in the first window. I need to store data in this subroutine before closing an old window and opening a new window. I use MessageBox just for testing.
Private Sub btnEnter_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnEnter.Click
Dim instructionWindow As InstructionsWindow
instructionWindow = New InstructionsWindow()
Application.Current.Properties("number") = textBoxValue.Text
Globals.One = "2"
Globals.Two = 3
MessageBox.Show("GlobalVariables: UserName=" & GlobalVariables.UserName & " UserAge=" & GlobalVariables.UserAge)
GlobalVariables.UserName = "Viktor"
GlobalVariables.UserAge = 34
GlobalModule.Foo = "Test Foo"
'testing if it saved tha value
'MessageBox.Show(Application.Current.Properties("number"))
Application.Current.MainWindow.Close()
instructionWindow.ShowDialog()
End Sub
Next subroutine is where I am trying to retrieve the value from global Properties/variables in the second window, but message boxes come out empty. There might also the case that I am assigning values in a wrong way, or not reading them in a right way (casting?) :
Private Sub FlowDocReader_Initialized(ByVal sender As Object, ByVal e As System.EventArgs) Handles FlowDocReader.Initialized
' Get a reference to the Application base class instance.
Dim currentApplication As Application = Application.Current
MessageBox.Show(currentApplication.Properties("number"))
MessageBox.Show("One = " & Globals.One & " Two = " & Globals.Two)
MessageBox.Show("GlobalVariables: UserName=" & GlobalVariables.UserName & " UserAge=" & GlobalVariables.UserAge)
MessageBox.Show("GlobalModule.Foo = " & GlobalModule.Foo)
Dim filename As String = My.Computer.FileSystem.CurrentDirectory & "\instructions.txt"
Dim paragraph As Paragraph = New Paragraph()
paragraph.Inlines.Add(System.IO.File.ReadAllText(filename))
Dim document As FlowDocument = New FlowDocument(paragraph)
FlowDocReader.Document = document
End Sub
Thanks.
You can make public Dictionary property for form and put your dictionry to this property or make constructor with Dictionary argument.
You already have this dictionary Application.Properties
Look here, please.
First, you can define a dictionary (list of lists) as follows at the beginning of a form or in a module
Dim dic As New Dictionary(Of String, List(Of String))
As the user completes questions on a form, write the partucular form number and query results to a single record in the dic before going to the next form (place this code into the "Next" button):
'Assume q1response=3, q2response=4,..., qpresponse="text", etc.
Dim myValues As New List(Of String)
myValues.Add(formname)
myValues.Add(q1response)
myValues.Add(q2response)
.
.
myValues.Add(qpresponse)
dic.Add(username, myValues)
When a user is done, there will be multiple records in the dictionary, each of which starts with their name and is followed by question responses. You can loop through multiple dictionary records, where each record is for a user using the following:
For Each DictionaryEntry In dic 'this loops through dic entries
Dim str As List(Of String) = DictionaryEntry.Value
'here you can do whatever you want with results while you read through dic records
'username will be = str(0)
'formname will be str(1)
'q1 response on "formname" will be str(2)
'q2 response on "formname" will be str(3)
'q3 response on "formname" will be str(4)
...
Next
The trick is that there will be multiple dictionary records with results for one user, where record one can have results like "John Doe,page1,q1,q2,q3" and record 2 will be "John Doe,page2,q4,q5,q6." Specifically, the "str" in the above loop will be an array of string data containing all the items within each dictionary record, that is, in str(0), str(1), str(2),... This is the information you need to work with or move, save, analyze, etc.
You can always put all the code I provided in a class (which will be independent of any form) and dimension the sic is a Sub New in this class, with the updating .Add values lines in their own sub in this same class). Then just Dim Updater As New MyNewClassName. Call the Updater in each continue button using Call Updater.SubNameWithAddValues(q1,q2,...qp). It won't matter where you are in your program since you using a specific class. The one thing I noticed with my code is that you can only use the line that adds the "key" or the username once, so use it after the last query -so put it in a Sub Finished in your new class and call as Call Updater.Finished(username,q30,q31,last)

Infragistics UltraWinGrid EmptyDataText Equivalent?

We're using Infragistics UltraWinGrid as a base class for customized controls. One of the projects that will use this control to display search results has a requirement to display a user friendly message when no matches are located.
We'd like to encapsulate that functionality into the derived control - so no customization beyond setting the message to display is required by the programmer who uses the control. This would have to be done in generic fashion - one size fits all datasets.
Is there allowance in the UltraWinGrid for this type of usage already? If so, where would I find it hidden. :-)
If this functionality needs to be coded, I can think of an algorithm which would add a blank record to whatever recordset was set and place that into the grid. In your opinion, is this the best way to handle the solution?
I don't know if this will help, but here's to finishing up the thread. I didn't find a built in way, so I solved this problem as follows: In my class which inherits UltraGrid
Public Class MyGridPlain
Inherits Infragistics.Win.UltraWinGrid.UltraGrid
I added two properties, one to specify what the developer wants to say in the empty data case, and another to enable the developer to place their message where they want it
Private mEmptyDataText As String = String.Empty
Private mEmptyDataTextLocation As Point = New Point(30, 30)Public Shadows Property EmptyDataTextLocation() As Point
Get
Return mEmptyDataTextLocation
End Get
Set(ByVal value As Point)
mEmptyDataTextLocation = value
setEmptyMessageIfRequired()
End Set
End Property
Public Shadows Property EmptyDataText() As String
Get
Return mEmptyDataText
End Get
Set(ByVal value As String)
mEmptyDataText = value
setEmptyMessageIfRequired()
End Set
End Property
I added a method which will check for empty data and set the message if so. And another method which will remove the existing empty message.
Private Sub setEmptyMessageIfRequired()
removeExistingEmptyData()
'if there are no rows, and if there is an EmptyDataText message, display it now.
If EmptyDataText.Length > 0 AndAlso Rows.Count = 0 Then
Dim lbl As Label = New Label(EmptyDataText)
lbl.Name = "EmptyDataLabel"
lbl.Size = New Size(Width, 25)
lbl.Location = EmptyDataTextLocation
ControlUIElement.Control.Controls.Add(lbl)
End If
End SubPrivate Sub removeExistingEmptyData()
'any previous empty data messages?
Dim lblempty() As Control = Controls.Find("EmptyDataLabel", True)
If lblempty.Length > 0 Then
Controls.Remove(lblempty(0))
End If
End Sub
Last - I added a check for empty data to the grid's InitializeLayout event.
Private Sub grid_InitializeLayout(ByVal sender As Object, _
ByVal e As Infragistics.Win.UltraWinGrid.InitializeLayoutEventArgs) _
Handles MyBase.InitializeLayout
setEmptyMessageIfRequired()
End Sub

Resources