vb.net - custom templates issue with databinding expressions in repeater control - vb.net

I'm new here but I've been browsing stackoverflow for a while when looking for answers.
Here's the problem : I'm trying to implement a custom repeater with multiple conditional templates to avoid as much tests in the templates as possible
As for now my ascx code looks like that :
<custom:Repeater runat="server">
<headerTemplate>...</headerTemplate>
<templates>
<custom:template match="[filter1]"><contents>[filter1] is true for <%# Container.DataItem.ID%></contents></custom:template>
<custom:template match="[filter2]"><contents>[filter2] is true for <%# Container.DataItem.ID%></contents></custom:template>
</templates>
</custom:Repeater>
Besides that this syntax is too verbose, the following code works
<custom:template match="[filter1]"><contents><%# TypeOf Container is IDataItemContainer%></contents></custom:template>
But this code fails
<custom:template match="[filter1]"><contents>[filter1] is true for <%# Container.DataItem.ID%></contents></custom:template>
and I get the error message: : 'DataItem' is not a member of 'System.Web.UI.Control'.
It seems like vb tries to parse the template content at compile time instead of on databinding
Here are my vb classes
<ParseChildren(True, "contents")>
Public Class Template : Inherits WebControl : Implements ITemplate
Private _match As String
<PersistenceMode(PersistenceMode.Attribute)>
Public Property match() As String
Get
Return _match
End Get
Set(ByVal value As String)
'TODO compile filter as an expression
_match = value
End Set
End Property
Private _source As ITemplate
<PersistenceMode(PersistenceMode.Attribute)>
Public Property contents() As ITemplate
Get
Return Nothing
End Get
Set(ByVal value As ITemplate)
_source = value
End Set
End Property
Public Sub InstantiateIn(container As System.Web.UI.Control) Implements System.Web.UI.ITemplate.InstantiateIn
_source.InstantiateIn(container)
End Sub
End Class
<ParseChildren(True)>
Public Class ApplyTemplate : Inherits Repeater
_template as List(Of Template)
<PersistenceMode(PersistenceMode.InnerProperty), TemplateContainerAttribute(GetType(Template))>
Public Overloads Property templates() As List(Of Template)
Get
Return Nothing
End Get
Set(value As List(Of Template))
_templates = value
End Set
End Property
End Class
How can I add databinding expressions in my templates' content ?
Thanks in advance.
Max.
PS: Would it be possible (and how ;)) to compact the ascx code so it would look like this :
<custom:Repeater runat="server">
<headerTemplate>...</headerTemplate>
<custom:template match="[filter1]">[filter1] is true for <%# Container.DataItem.ID%></custom:template>
<custom:template match="[filter2]">[filter2] is true for <%# Container.DataItem.ID%></custom:template>
...
</custom:Repeater>

At last I had time to work on this again and thanks to this link (http://www.codeproject.com/Articles/21521/Creating-a-Templated-User-Control) I was able to implement my own custom repeater as I wanted to.
So now the aspx code looks like this
<custom:Repeater runat="server" >
<headerTemplate>...</headerTemplate>
<separatorTemplate on="1,9">some advertisement snippet</separatorTemplate>
<separatorTemplate every="2"><hr/></separatorTemplate>
<itemTemplate match="[filter1]"><source>[filter1] is true for <%# Container.DataItem.ID%></source></itemTemplate>
<itemTemplate match="[filter2]"><source>[filter2] is true for <%# Container.DataItem.ID%></source></itemTemplate>
...
</custom:Repeater>
The inner source tag is still necessary to allow code blocks in the template if needed.
If anybody knows how to remove it and still keep the feature with Metadata attributes, please comment.
So for the vb code here are the classes skeleton
Public Class Repeater
Inherits System.Web.UI.UserControl ' I use a UserControl so i can add support any html-bound attributes like class, style, data-*... without hardcoding them in the class
Private templates As New List(Of TemplateItem)
<PersistenceMode(PersistenceMode.InnerProperty)>
Public WriteOnly Property itemTemplate() As TemplateItem
Set(value As TemplateItem)
value.Container = Me 'to bind the container to the template instead of passing the reference on instanciation
templates.Add(value)
End Set
End Property
Public Overrides Sub DataBind()
If Not IsNothing(datasource) Then 'this to avoid nested controls Databind method to be called twice
[... iterate on dataitems]
[... template selection on best matching filters]
templates.instantiateFor(dataitem) ' to instanciate the template for the current item
[... iteration done]
MyBase.DataBind() ' to bind the newly created controls
End If
End Sub
...
End Class
'
<ParseChildren(True, "source")>
Public Class TemplateItem
Inherits Control
Friend Container As Repeater
'handle as many attributes as you want here
Private _matchExpression As String
<PersistenceMode(PersistenceMode.Attribute)>
Public Property match() As String
Get
Return _matchExpression
End Get
Set(value As String)
_matchExpression = value
End Set
End Property
'Now the simple part for parsing the inner code has a template
'I use the standard RepeaterItem class as the template container because i don't need more features for now
Private _source As ITemplate
<PersistenceMode(PersistenceMode.InnerDefaultProperty)>
<TemplateContainer(GetType(RepeaterItem))>
Public Property source() As ITemplate
Get
Return _source
End Get
Set(value As ITemplate)
_source = value
End Set
End Property
'now the part to instantiate as template for a given item
Public Sub instantiateFor(Item As Object, Optional itemIndex As Integer = -1)
Dim instantiator = New RepeaterItem(itemIndex, ListItemType.Item)
instantiator.DataItem = Item
_source.InstantiateIn(instantiator)
Container.Controls.Add(instantiator)
End Sub
End Class
Et voilà... at last !!
Hope this will help some

Related

How to hide a public property without making it private or removing it

Premise
Suppose that we have a class in a dll with a simple public property "OldP". In a newer version of this dll, I want to change the name of this property in "NewP". I can't simply replace the property name, because this dll is used in a lot of program and I can't change them...
I want to hide this property and show only the newer property. How is this possible?
"Browsable" attempt
Before asking this, I searched similar questions on StackOverflow and read something about Browsable, but it doesn't work. Here is my code:
Public Class Example
Private _p As Integer
<System.Obsolete("Use NewP instead", False)>
<System.ComponentModel.Browsable(False)>
Public Property OldP() As Integer
Get
Return _p
End Get
Set(ByVal value As Integer)
_p = value
End Set
End Property
Public Property NewP() As Integer
Get
Return _p
End Get
Set(ByVal value As Integer)
_p = value
End Set
End Property
End Class
When I create a new Example object, I can still see "OldP":
Where I'm wrong?
You cannot completely prevent the old property from being used, but you can hide it from IntelliSense by adding this directive: <System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)>. The OldP property will no longer be suggested, but still be accessible normally.
Beside that, I recommend you change the OldP property so that it refers not to the _p object, but to the NewP property to make future management easier, so that you don't have to worry about the deprecated version in the future.

Remove Properties and Events from UserControl vb.net

I´m devoloment my own userControl with vb.net. I´m new with this task.
I want to remove default properties.
After google, I found several topics, like this:
Removing certain properties in a user control, i.e. forcing one value and not editable in Design mode
So, I´m trying to use it, but doesn´t works for me. I don´t know what I missing or doing wrong.
Public Class MyControlDesigner
Inherits System.Windows.Forms.Design.ControlDesigner
Protected Overrides Sub PreFilterProperties(ByVal properties As System.Collections.IDictionary)
MyBase.PreFilterProperties(properties)
properties.Remove("BackColor")
properties.Remove("ForeColor")
properties.Remove("Font")
End Sub
End Class
<DesignerAttribute(GetType(MyControlDesigner))> _
Public Class MyUserControl
' ...
End Class
To hide overwrite properties I follow this topic Hiding inherited properties and this works fine, for some of them.
<Browsable(False), EditorBrowsable(EditorBrowsableState.Never)> _
Public Shadows Property AutoScroll() As Boolean
Get
Return m_AutoScroll
End Get
Set(ByVal value As Boolean)
m_AutoScroll = value
End Set
End Property
But still, I have other properties that I don´t know how to hide or remove. Like Font, ForeColor, Margin etc...
Thanks advanced
Edit: Once I finish my control, I don´t want to see, all the properties like the picture, Only I want to show mine´s.
Edit: Add code from #Plutonix
I do not have access to that control/tool/property editor, but you can try to use a TypeConverter. This works with a control that inherits from UserControl to hide properties from a Property Grid, but it wont hide them from the VS IDE property editor.
The VS IDE uses reflection to get the property list and apparently ignores the TypeConverter. If your tool does something similar, this wont work - again, I dont have the tool to test it, but it is simple and worth a try.
I created an actual UserControl with a few controls on it. Then:
Imports System.ComponentModel
Public Class YControlConverter
Inherits TypeConverter
Public Overrides Function GetPropertiesSupported(context As ITypeDescriptorContext) As Boolean
Return True
End Function
Public Overrides Function GetProperties(context As ITypeDescriptorContext,
value As Object,
attributes() As Attribute) As PropertyDescriptorCollection
Dim propNames() As String = {"backcolor", "forecolor",
"autoscroll", "autoscrollminsize",
"autoscrollmargin", "autoscrolloffset",
"autoscrollposition"}
Dim pdc As PropertyDescriptorCollection = TypeDescriptor.GetProperties(context.Instance)
' collection to store the ones we want:
Dim myPDCList As New List(Of PropertyDescriptor)
For Each pd As PropertyDescriptor In pdc
If propNames.Contains(pd.Name.ToLowerInvariant) = False Then
myPDCList.Add(pd)
End If
Next
Return New PropertyDescriptorCollection(myPDCList.ToArray())
End Function
End Class
Then decorate your usercontrol with the TypeConverter:
<TypeConverter(GetType(YControlConverter))>
Public Class YControl
This basically runs thru the PropertyDescriptorCollection for the control and filters out the unwanted properties before returning the new collection. If it works, just add the names to the propNames array that you want to hide. View in a PropertyGrid:
As you can see, all the AutoScroll... properties are removed as well as BackColor. The others are gone as well. If the editor will use your TypeConverter instead of reflection, it should work.
--
How to test your TypeConverter using a PropertyGrid. Using a form with a property grid and a button, in the button click:
Dim yp As New YControl
PropertyGrid1.SelectedObject = yp
If the AutoScroll... properties are missing from the prop grid, your TypeConverter works! If they still show in the other tool, it is using reflection like VS.

Overriding a Custom Control's Text Property to use a Textbox's Text causes the Textbox to display the Control's name by default

Using VB.net (2005), I'm creating a custom control (a folder picker) that contains a textbox and button. Recently I've added the following:
<Browsable(True), DefaultValue(""), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)> _
Public Overrides Property Text() As String
Get
Return Me.uiPath.Text
End Get
Set(ByVal value As String)
Me.uiPath.Text = value
End Set
End Property
The problem I'm having is that now when the control is created, the textbox always defaults to showing the name of the control (instead of being blank). It seems the Text property is being changed after Sub New is called, however, I've checked the designer, and I cannot see where it is doing this.
I know that changing the name of the property would resolve the issue, but I would like to know why this is happening (I'm guessing it has something to do with the standard designer?), and if there is a way to resolve (or work around) it, while still using the Text property?
I don't know if it makes any difference, but I am using a custom designer:
Public Class FolderPickerDesigner
Inherits Windows.Forms.Design.ControlDesigner
Public Overrides ReadOnly Property SnapLines() As IList
Get
Dim SnapLinesList As ArrayList = TryCast(MyBase.SnapLines, ArrayList)
If (Me.Control IsNot Nothing) AndAlso (Me.Control IsNot Nothing) Then
Dim FP As FolderPicker = CType(Me.Control, FolderPicker)
SnapLinesList.Add(New SnapLine(SnapLineType.Baseline, FP.uiPath.Bottom - 5, SnapLinePriority.Medium))
End If
Return SnapLinesList
End Get
End Property
End Class
Any help / ideas would be appreciated. If this doesn't / can't be resolved, I'll mark the most helpful post as the answer...
Set(ByVal value As String)
If Me.DesignMode And (Environment.StackTrace.Contains("System.Windows.Forms.Design.ControlDesigner.InitializeNewComponent")) Then Return
BaseT.Text = value
End Set

VB.NET Property as StringDictionary?

I'm trying to create a new property with a type of StringDictionary but when I use the property like a stringdictionary variable I am getting an error.
The goal is to NOT use a variable, I need to use a property for this. Behind the scenes I am saving the stringdictionary value to a global user-ID indexed collection. Here's the code that creates the property and attempts to get and set:
Public Property MaxLenDict As StringDictionary()
Get
Return GetFromCollection("MaxLenDict")
End Get
Set(Value As StringDictionary())
SaveToCollection("MaxLenDict", Value)
End Set
End Property
Public Sub ExampleSub()
If MaxLenDict("hello world") = "" Then MaxLenDict.Add("Hello World", "I'm Here")
End Sub
Get this error in ExampleSub "StringDictionary cannot be converted to string" in the IF statement on this code:
MaxLenDict("hello world")=""
So how do I successfully make and use a property as a stringdictionary?
Your property is of type StringDictionary() (an array!), not StringDictionary.
I’m not sure that using StringDictionary is advised in the first place, though. The class is simply a remnant from pre-generics versions of .NET. Use Dictionary(Of String, String) instead.

How to create global object in VB NET

I'm triying to build a language dictionary in VB NET in order to be able to have several languages in the application.
This dictionary has a init method that loads from database the entire dictionary and stores it in memory.
In the rest of the classes of the project, I added the reference and I can use directly the object without create a new instance because their methods are shared.
My question is how i have to load the dictionary content in order that the rest of classes only accessing to Language.GetWord method obtains the properly record of the collection, same way as My.Settings class.
When My.Settings is used from any class the values are loaded. I'm looking for the same effect.
This is the code of the class:
Public Class LanguageProvider
Private Shared CurrentLanguage As Collection
Public Shared ReadOnly Property GetWord(ByVal Label As String)
Get
Try
Return CurrentLanguage.Item(Label)
Catch ex As Exception
Return Label
End Try
End Get
End Property
Public Shared Sub LoadLanguage(ByVal CultureId As Integer)
Dim SQLController As New SqlDataAccessController
SQLController.SQLSentence = "SELECT DNF_CULTURE_LANGUAGE_LABELS.LABEL, DNF_CULTURE_LANGUAGE_LABELS.VALUE FROM DNF_CULTURE_LANGUAGE_LABELS WHERE DNF_CULTURE_LANGUAGE_LABELS.CULTUREID = " & CultureId & " ORDER BY LABEL;"
SQLController.OpenConnection()
Dim dr As SqlClient.SqlDataReader
dr = SQLController.GetData()
CurrentLanguage = New Collection
While dr.Read()
CurrentLanguage.Add(dr.Item("Value"), dr.Item("Label"))
End While
dr.Close()
SQLController.CloseConnection()
End Sub
End Class
Function LoadLanguage must be called when the application loads, in order to access once to database.
After that property GetWord must access to the collection with the words and return the result. The problem is that the instances are not the same and the dictionary is not loaded when a class uses it.
Thanks.
Something along the lines of
Public Shared ReadOnly Property GetWord(ByVal Label As String)
Get
Try
If CurrentLanguage Is Nothing then
LoadLanguage(theDefaultCultureIdYouWant)
' Now CurrentLanguage is not Nothing any more,
' so LoadLanguage won't be called again
end if
Return CurrentLanguage.Item(Label)
Catch ex As Exception
Return Label
End Try
End Get
End Property
Of course, you have to provide a value for theDefaultCultureIdYouWant, depends on what you want to happen if the user just calls GetWord without explicitly mentioning a specific culture.