Infragistics UltraWinGrid EmptyDataText Equivalent? - winforms

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

Related

Event handling for an array of objects [duplicate]

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

WF: Workflow Designer: List of InArgument added dynamically : How to get value during workflow execution

I have built a workflow designer that allows to enter a list of email addresses.Each email in the list needs to be an InArgument(Of String) so they can be individually edited/added using variables.
On my Activity, I have a property that is declared like so:
Public Property [To] As ObservableCollection(Of InArgument(Of String))
My designer is wired up and populating this collection properly.
However during the execution, I do not know how to get the run-time value for each InArgument that was added.
When we are executing the workflow and iterating for each InArgument added to the list, I attempted to get the value as shown below but that fails:
For Each toAddress As InArgument(Of String) In Me.To.ToList()
Dim emailToAddress As String = toAddress.Get(_Context)
Next
The error we get is “The argument of type '<type>' cannot be used. Make sure that it is declared on an activity” and type is a string in my case...
The error we get sort of make sense because we haven’t declared a property on the activity since it was added dynamically to the list and therefore cannot get the value using the syntax shown below:
The_Property_Name.Get(_Context)
Can someone help? I can't seem to find anything. Should I be doing a different approach?
I figured it out so I will answer my own question! All we need to do is explicitly add the collection items to the metadata by overriding CacheMetadata() method on the activity. This then makes it available to the workflow context.
First we add them to the context:
Protected Overrides Sub CacheMetadata(ByVal metadata As CodeActivityMetadata)
MyBase.CacheMetadata(metadata)
Dim i As Integer = 0
For Each item As InArgument(Of String) In Me.To
Dim runTimeArg As RuntimeArgument = New RuntimeArgument("TO" & i.ToString(), item.ArgumentType, item.Direction, False)
metadata.Bind(item, runTimeArg)
metadata.AddArgument(runTimeArg)
i = i + 1
Next
End Sub
Then when executing, we get the values like this
For Each item As InArgument(Of String) In Me.To
Dim email As String = _Context.GetValue(item)
Next

Tying ComboBox/NumericUpDown selections to an array?

I'm working on a VB project that has a lot of comboboxes and numericupdown items.
Lets say we have ComboBox1, 2, 3, 4, and 5; and we have NumericUpDown1, 2, 3, 4, 5.
When the user clicks the "Save" button, I want to save all of their selected combobox items and numericupdown numbers to a CSV file. Is there an elegant/automatic way to tie all of the .SelectedIndex and .Value for these items to an array so I can easily write the array out to a CSV?
The only way I know to do this so far is to manually associate each one with an array position:
Arr(0) = ComboBox1.SelectedIndex
Arr(1) = ComboBox2.SelectedIndex
...
Arr(5) = NumericUpDown1.Value
Arr(6) = NumericUpDown2.Value
...
etc.
This wouldnt be too bad, except I have a LOT of these items, and writing a line for each one seems silly. I'm new to VB, so this might be an obvious solution to some. Any ideas?
Having them bound to an array would be really handy because I also allow the user to Load a CSV file, which I would like to automatically populate the ComboBoxes and NumericUpDowns from the CSV values. The only way I know to do this is to manually move each array item to the respective combobox/numeric item when they click the Load file button:
ComboBox1.SelectedIndex = Arr(0)
ComboBox2.SelectedIndex = Arr(1)
...
NumericUpDown1.Value = Arr(5)
NumericUpDown2.Value = Arr(6)
...
etc.
Edit: Here is some application info as requested...
The CSV file that can be saved/loaded looks like this:
#"Device Info","123456","asdfgh","0000","1.0x","1"
000F,0000,0032,0000,00C8,0001,0078,0101,0000,0001,0000,0001
010F,0078,0000,0103,0001,0000,000A,0005,0007,0006,0000
0001,000A,000A,000A,000A,0005,0005,0005,0002
...etc
The header line just has serial number, version, and other misc info; it is automatically generated by the target device. All of the other lines are configuration setpoints that the target device reads in and automatically configures itself. I'm writing this PC program to be able to edit (and create from scratch) these configuration CSV files with a nice GUI interface. Each item is tied to a specific setpoint, such as 000F = Language, 0032 = System Frequency, 00C8 = System Voltage, etc. The easiest way I saw to make this configuration program was to use numeric entry and drop-down comboboxes that the user can select what they want. Each NUD and CBOX equates to one of the CSV file data fields.
You can use Controls.Find() to get a reference to the desired control based on an index value. Here's a quick example to demonstrate what I mean:
For i As Integer = 1 To 30
Dim matches() As Control = Me.Controls.Find("NumericUpDown" & i, True)
If matches.Length > 0 AndAlso TypeOf matches(0) Is NumericUpDown Then
DirectCast(matches(0), NumericUpDown).Value = i
End If
Next
You can incorporate code like that into the load/save routines.
I would use binary serialization. This eliminates the need to format strings or xml when saving control properties. Similar to Plutonix's solution, it only works on certain types of control. However, it can be modified to work on any type of control - but only supports a single property to be loaded for each control. It will work on all controls of type X instead of controls named xName. You can add further restrictions by grouping the controls to be serialized in a panel or other means.
Make a new Form called Form1. Add some NumericUpDowns, TextBoxes, and ComboBoxes. Put some values in the ComboBoxes at design time, otherwise calling loadState() in Form_Load will be meaningless. However, loadState() can be called whenever (i.e. after the comboboxes have been populated).
You will need to import these two namespaces:
Imports System.IO
Imports System.Runtime.Serialization.Formatters.Binary
In your class:
Private Shared stateFileName As String = "SavedState.bin"
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
saveState(Me)
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
loadState(Me)
End Sub
And these are the methods you will use to save and load states. The save state method:
Private Shared Sub saveState(instance As Form)
Dim controlProperties As Dictionary(Of String, Object) =
instance.Controls.OfType(Of Control).ToDictionary(Of String, Object)(
Function(c) c.Name,
Function(c)
' You can support different types of controls here too
If TypeOf c Is NumericUpDown Then
Return CType(c, NumericUpDown).Value
ElseIf TypeOf c Is ComboBox Then
Return CType(c, ComboBox).SelectedIndex
Else
' All other controls get their text property saved
' .Text is a property of Control
Return c.Text
End If
End Function)
Using myFileStream As Stream = File.Create(stateFileName)
Dim serializer As New BinaryFormatter
serializer.Serialize(myFileStream, controlProperties)
End Using
End Sub
The load state method:
Private Shared Sub loadState(instance As Form)
If File.Exists(stateFileName) Then
Using myFileStream As Stream = File.OpenRead(stateFileName)
Dim deserializer As New BinaryFormatter()
Dim controlProperties = CType(deserializer.Deserialize(myFileStream), Dictionary(Of String, Object))
For Each c As Control In instance.Controls
If controlProperties.ContainsKey(c.Name) Then
' You can support different types of controls here too
If TypeOf c Is NumericUpDown Then
CType(c, NumericUpDown).Value = CDec(controlProperties(c.Name))
ElseIf TypeOf c Is ComboBox Then
CType(c, ComboBox).SelectedIndex = CInt(controlProperties(c.Name))
Else
c.Text = controlProperties(c.Name).ToString()
End If
End If
Next
End Using
End If
End Sub
You should add exception handling, and note that a binary file is not meant to be edited by a human without the assistance of a machine.

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)

SubSonic and a dateTimePicker control on a Windows Form

Question #1: Latest working Version
I'm currently using SubSonic 2.1 built 491.
Is there a later build? Where can I get it?
I know 2.2 was released but it doesn't come with a Setup and I wouldn't know how to modify the App.Config/Web.Config to work with it.
Question #2: Issue with dateTimePicker control on Windows Form.
I keep getting System.FormatException trying to retrieve data From SubSonic to that control or saving data from that control to the Database through SubSonic.
For example, if I want to save the Time only, I can use the .Text property. To save the Date, I need to use the .Value property of the control.
I've tried all sorts of conversion such as Convert.ToDateTime(dateTimePicker.Value.ToString()) and others but I can't find a consistent pattern that would not throw an exception. Any help on this would be greatly appreciated.
Re Question 1 - I don't think any app.config / web.config change is required between SS2.1 and 2.2
For Question 2 - are you using the new data types in MSSQL2008 by any chance? If so, SS2.2 doesn't seem to handle them properly yet.
Just stumbled upon your post.
I'm not sure if It was a FormatException, but I got a Exception with a databound DateTimePicker, too.
The problem is that
DateTimePicker.MinimumDateTime = #1/1/1753#
while
DateTime.MinValue = #1/1/0001#
New DateTime = #1/1#0001#
So if bind a property to a DataGridView that returns a DateTime value earlier than #1/1/1753# or later then #12/31/9998# ( DateTimePicker.MaximumDateTime ) you get an exception.
I solved this with my own inherited DateTimePicker (sorry, but it's written in vb)
To use it you can just bind your Subsonic object to the value property. But you have to set the ShowCheckBox property to true and bind a bool value to the CheckedValue property (to indicate that the date is set) which is persisted in your db, too.
Now If an empty date is returned, the date is set to Now and the checkedValue to false, leading to a disabled DateTimePicker.
If you check the Checkbox or choose a date with the calender) the checkbox is set to true and the Checkbox is checked.
Attention: Do not bind the Checked property directly, bind the CheckedValue property.
Imports System.ComponentModel
Public Class MyDateTimePicker
Inherits System.Windows.Forms.DateTimePicker
<Bindable(True)> _
Public Overloads Property Value() As DateTime
Get
If Not MyBase.Checked And (MyBase.Value < DateTimePicker.MinimumDateTime Or MyBase.Value > DateTimePicker.MaximumDateTime) Then
Return DateTime.Now
Else
Return MyBase.Value
End If
End Get
Set(ByVal value As DateTime)
If ((value < DateTimePicker.MinimumDateTime Or value > DateTimePicker.MaximumDateTime) Or value = #1/1/1900#) Then
MyBase.Value = DateTime.Now
MyBase.Checked = False
Else
MyBase.Value = value
End If
End Set
End Property
Private _CheckedValue As Boolean
<Bindable(True)> _
Public Property CheckedValue() As Boolean
Get
Return _CheckedValue
End Get
Set(ByVal value As Boolean)
_CheckedValue = value
End Set
End Property
Protected Overrides Sub OnMouseUp(ByVal e As System.Windows.Forms.MouseEventArgs)
MyBase.OnMouseUp(e)
RefreshCheckedValue()
End Sub
Protected Overrides Sub OnKeyUp(ByVal e As System.Windows.Forms.KeyEventArgs)
MyBase.OnKeyDown(e)
RefreshCheckedValue()
End Sub
Private Sub RefreshCheckedValue()
CheckedValue = Me.Checked
If Not Me.DataBindings("CheckedValue") Is Nothing Then
Me.DataBindings("CheckedValue").WriteValue()
End If
End Sub
Protected Overrides Sub OnBindingContextChanged(ByVal e As System.EventArgs)
MyBase.OnBindingContextChanged(e)
Static checkedInitialized As Boolean
If Not checkedInitialized AndAlso Not Me.DataBindings("CheckedValue") Is Nothing Then
Me.Checked = Me.CheckedValue
checkedInitialized = True
End If
End Sub
End Class

Resources