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
Related
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
I created Standard Report Designer edit by end-users from this documentation XtraReports
I want to restrict some of its controls like creating new Label, line, tables & Data Source. End-users just want to edit content in that XtraReports. How to restrict it ?
Here, you need to either Override toolbox or need to Override XRControl drag and drop behaviour to restrict user from using selected XRControls.
you can define custom XRControl as below:
<DefaultBindableProperty("Number"), ToolboxBitmap(GetType(XRNumericLabel))> _
Public Class XRNumericLabel
Inherits XRLabel
Private myNumber As Integer
<SRCategory(ReportStringId.CatData), DefaultValue(0), Bindable(True)> _
Public Overridable Property Number() As Integer
Get
Return myNumber
End Get
Set(ByVal value As Integer)
myNumber = Value
End Set
End Property
<Browsable(False), EditorBrowsable(EditorBrowsableState.Never), Bindable(False)> _
Public Overrides Property Text() As String
Get
Return myNumber.ToString()
End Get
Set(ByVal value As String)
Dim i As Integer
If Integer.TryParse(Value, i) Then
myNumber = i
Else
Throw New ArgumentException("This text can't be converted to a number!")
End If
End Set
End Property
End Class
Create custom XRControl Step by step tutorial .
Hide component tray.
I think I have read every article google returns when I search wpf mvvm-light data validation and I dont know which way to go. I am aware of josh smith, Karl Shifflett's, and MVVM LIGHT's own demo techniques for data validation. What I see is that most validation requires me to fully "re-abstract" my model in my view model. Meaning that I have to create a property in my viewmodel for each property of my model that I want to validate (and in some cases convert all these into string values for binding/validation). This seems like a lot or redundancy when all I want to do is mark most fields as required.
I am using LINQ to entity framework(with self tracking) for my model classes which come from a SQL server DB. As a result I would prefer to keep my business data validation/rules within my viewmodels. I write a simple service interface to get the data from the model and pass it to my viewmodel.
Most of the examples I can find are from as far back as 2008 (ie josh smith). Are these techniques still valid or are there more up to date best practices for mvvm data validation with .NET 4.5 etc.
So I am asking:
1) What methods do you suggest I use
2) What methods work best in a LINQ to EF with MVVM-Light Environment.
3) EDIT: I want to provide feedback to user as they enter data, not just when they submit form
thanks
The way I do this (not necessarily correct) is to do my validation in the ViewModel (where the CRUD operations typically take place), then if there are validation errors, abort saving/adding any data and use Messenger.Default.Send to send a custom message type to my view. Then I alert the user via a DialogBox or otherwise.
I've experimented with Binding ValidationRules in the past but found by far the most reliable and consistent method to be simple if statements.
I eventually ended up using the following. I changed my model to use LINQ to self tracking entities (see this article for info about STE http://msdn.microsoft.com/en-us/library/vstudio/ff407090%28v=vs.100%29.aspx).
LINQ to STE creates an OnPropertyChanged event that implements the iNotifyPropertyChanged interface.
I just created a public partial class for the matching model object (linq entity generated code) I wanted and added an event handler for the OnPropertyChanged event. I then used the IDataErrorInfo interface to validate and throw errors as I needed. This allows me to validate the fields as they change which gets reflected to the user. This also allows you to perform more advanced validation logic that may need to requery the database (i.e. to look for if a username is already used etc.) or throw a dialog box
Also, having the data validation in the model allows me to still have validation if i perform direct "batch" operations that bypass the UI.
I then used an HasErrors and HasChanges property and used them to create a Boolean value that gets attached to the relay commands, disabling the crud command buttons if errors are present.
I will post some simple code to outline what I just described, comment if you want more detail.
Here is the Entity Framework extension of the model class:
Imports System.ComponentModel
Partial Public Class client
Implements IDataErrorInfo
#Region "Properties / Declarations"
'Collection / error description
Private m_validationErrors As New Dictionary(Of String, String)
Private _HasChanges As Boolean = False
''Marks object as dirty, requires saving
Public Property HasChanges() As Boolean
Get
Return _HasChanges
End Get
Set(value As Boolean)
If Not Equals(_HasChanges, value) Then
_HasChanges = value
OnPropertyChanged("HasChanges")
End If
End Set
End Property
'Extends the class with a property that determines
'if the instance has validation errors
Public ReadOnly Property HasErrors() As Boolean
Get
Return m_validationErrors.Count > 0
End Get
End Property
#End Region
#Region "Base Error Objects"
'Returns an error message
'In this case it is a general message, which is
'returned if the list contains elements of errors
Public ReadOnly Property [Error] As String Implements System.ComponentModel.IDataErrorInfo.Error
Get
If m_validationErrors.Count > 0 Then
Return "Client data is invalid"
Else
Return Nothing
End If
End Get
End Property
Default Public ReadOnly Property Item(ByVal columnName As String) As String Implements System.ComponentModel.IDataErrorInfo.Item
Get
If m_validationErrors.ContainsKey(columnName) Then
Return m_validationErrors(columnName).ToString
Else
Return Nothing
End If
End Get
End Property
#End Region
#Region "Base Error Methods"
'Adds an error to the collection, if not already present
'with the same key
Private Sub AddError(ByVal columnName As String, ByVal msg As String)
If Not m_validationErrors.ContainsKey(columnName) Then
m_validationErrors.Add(columnName, msg)
End If
End Sub
'Removes an error from the collection, if present
Private Sub RemoveError(ByVal columnName As String)
If m_validationErrors.ContainsKey(columnName) Then
m_validationErrors.Remove(columnName)
End If
End Sub
#End Region
Public Sub New()
Me.HasChanges = False
End Sub
#Region "Data Validation Methods"
''handles event and calls function that does the actual validation so that it can be called explicitly for batch processes
Private Sub ValidateProperty(ByVal sender As Object, ByVal e As PropertyChangedEventArgs) Handles Me.PropertyChanged
If e.PropertyName = "HasChanges" Then
Exit Sub
End If
IsPropertyValid(e.PropertyName)
HasChanges = True
End Sub
Public Function IsPropertyValid(sProperty As String) As Boolean
Select Case sProperty
''add validation by column name here
Case "chrLast"
If Me.chrLast.Length < 4 Then
Me.AddError("chrLast", "The last name is too short")
Return True
Else
Me.RemoveError("chrLast")
Return False
End If
Case Else
Return False
End Select
End Function
#End Region
End Class
then in the view model I included the following code to bind thecommand and evaluate whether or not it can be executed.
Public ReadOnly Property SaveCommand() As RelayCommand
Get
If _SaveCommand Is Nothing Then
_SaveCommand = New RelayCommand(AddressOf SaveExecute, AddressOf CanSaveExecute)
End If
Return _SaveCommand
End Get
End Property
Private Function CanSaveExecute() As Boolean
Try
If Selection.HasErrors = False And Selection.HasChanges = True Then
Return True
Else
Return False
End If
Catch ex As Exception
Return False
End Try
End Function
Private Sub SaveExecute()
''this is my LINQ to Self Tracking Entities DataContext
FTC_Context.SaveChanges()
End Sub
the following is how I bound my button (has custom styling in WPF)
<Button Content="" Height="40" Style="{DynamicResource ButtonAdd}" Command="{Binding SaveCommand}" Width="40" Cursor="Hand" ToolTip="Save Changes" Margin="0,0,10,10"/>
so, when there are no validation errors and the current client record "isDirty" the save button automatically becomes enabled, and disabled if any of those two conditions fail. This way I now have a simple way of validating any type of column/data I want for the entity, and I can provide user feedback as they enter data in the form, and only enable CRUD command buttons once all my "conditions" have been met.
This was quite a battle to figure out.
Ok - I'm pulling my hair out over what I thought was a simple scenario: create a custom Label for bilingual use that contained two additional properties (EnglishText, FrenchText). Currently its structured like this:
Public Class myCustomLabel
Inherits System.Windows.Controls.Label
Public myEnglishTextProperty As DependencyProperty = DependencyProperty.Register("myEnglishText", GetType(String), GetType(myCustomLabel), New PropertyMetadata("English", New PropertyChangedCallback(AddressOf TextChanged)))
Public myFrenchTextProperty As DependencyProperty = DependencyProperty.Register("myFrenchText", GetType(String), GetType(myCustomLabel), New PropertyMetadata("Francais", New PropertyChangedCallback(AddressOf TextChanged)))
Public Sub New()
'This OverrideMetadata call tells the system that this element wants to provide a style that is different than its base class.
'This style is defined in themes\generic.xaml
DefaultStyleKeyProperty.OverrideMetadata(GetType(myCustomLabel), New FrameworkPropertyMetadata(GetType(myCustomLabel)))
End Sub
Public Property myEnglishText() As String
Get
Return MyBase.GetValue(myFrenchTextProperty)
End Get
Set(ByVal value As String)
MyBase.SetValue(myFrenchTextProperty, value)
End Set
End Property
Public Property myFrenchText() As String
Get
Return MyBase.GetValue(myFrenchTextProperty)
End Get
Set(ByVal value As String)
MyBase.SetValue(myFrenchTextProperty, value)
End Set
End Property
Private Sub TextChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
If DesignerProperties.GetIsInDesignMode(Me) = True Then
Me.Content = myEnglishText
Else
If myUser.Language = "E" Then
Me.Content = myEnglishText
Else
Me.Content = myFrenchText
End If
End If
End Sub
End Class
My test window grid xaml is simple:
<Grid>
<my:myCustomLabel myEnglishText="English Text" myFrenchText="English Text" Height="25" Width="100" Background="Aqua" Foreground="Black"/>
</Grid>
This seems to work in the development environment - changing the English and French texts change the in the design preview and it works when the app runs and the test window is opened. But only the first time - if I open the test window a second time I receive the following message:
'myEnglishText' property was already
registered by 'myCustomLabel'.
I understand now that if I change the dependency property declarations to shared then this problem goes away - but that leads to a host of other problems like the callback function being required to be shared as well - and thus unable to update the Content (which needs to be instantiated with the class). All I really want is the content property to be updated in design time when the english and french labels are changed.
Is there a way around this? Or maybe are dependency properties overkill for what I need?
You are registering your dependency properties as instance variables, and during the instance constructor. So they are getting registered again every time you instantiate the control, which causes an error the second time. As you have found out, dependency properties need to be static (Shared) members:
Public Shared myEnglishTextProperty As DependencyProperty =
DependencyProperty.Register("myEnglishText", GetType(String), GetType(myCustomLabel),
New PropertyMetadata("English", New PropertyChangedCallback(AddressOf TextChanged)))
You probably need to call OverrideMetadata in your shared constructor (type initialiser) rather than your instance constructor as well.
Regarding your issue with the callback needing to be shared: yes, it will be, but one of the arguments to the callback is the label instance. So you can just cast that to label and call an instance method on that:
private static void TextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MyLabel)d).TextChanged();
}
private void TextChanged()
{
// your code here
}
(forgive C# syntax)
Is the reason you don't want the callback method to be shared because you're accessing the "me" instance? If that's all it is, make it shared and use the "d" parameter. I don't know VB well enough to show you the code, but just create a variable of type myCustomLabel and assign "d" to it (with a cast). Then use that variable (say "lbl") instead:
If DesignerProperties.GetIsInDesignMode(lbl) = True Then
lbl.Content = myEnglishText
Else
If myUser.Language = "E" Then
lbl.Content = myEnglishText
Else
lbl.Content = myFrenchText
End If
End If
Also, there's a slight bug in your example code. Try using this:
Public Property myEnglishText() As String
Get
Return MyBase.GetValue(myEnglishTextProperty)
End Get
Set(ByVal value As String)
MyBase.SetValue(myEnglishTextProperty, value)
End Set
End Property
Instead of this:
Public Property myEnglishText() As String
Get
Return MyBase.GetValue(myFrenchTextProperty)
End Get
Set(ByVal value As String)
MyBase.SetValue(myFrenchTextProperty, value)
End Set
End Property
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