I have an abstract user control(baseModule) that has a property that I plan on using a bitwise comparison on to determine what export types are supported by that module. In the designer of a module derived from baseModule, I am presented with a combobox with the ability to just select a single value (Html, Xml, etc.) I would like to be presented with a drop-down checked listbox so I could select which values I want.
How can I accomplish this inside of VS2008? I've seen other properties support this. Please refer to the code below for a better explanation of what I mean in the poorly asked question above.
Public Class ExportTypes
Public Enum ExportType
Html = 1
Xml = 2
Xls = 4
Txt = 8
Pdf = 16
Rtf = 32
End Enum
End Class
Public Class baseModule
Private _SupportedExportTypes As ExportType = 0
Public Property SupportedExportTypes() As ExportType
Get
Return _SupportedExportTypes
End Get
Set(ByVal Value As ExportType)
_SupportedExportTypes = Value
End Set
End Property
End Class
You will probably want to implement a UITypeEditor. Check this walkthrough, and exchange the created control in the custom type editor (in the EditValue method override) to a CheckedListBox, and handle assigning and retrieving the enum values to and from the listbox. Then decorate the property in your user control with an EditorAttribute point out your type editor, and you should be good to go.
Related
VS2013, code first EF6, VB
Elsewhere on SO I found a post that led me to add this to my Context class:
Public Sub New()
Me.Configuration.LazyLoadingEnabled = False
End Sub
However, in order to load a secondary table into my context for a view to find data to list in a For Each loop I had to add:
Dim myQuery = db.Questions.Include("PossibleAnswers").Where(Function(x) x.QuestionID = 6).Single()
Without that query in one form or another, my view does not find any data in the property 'PossibleAnswers' and nothing is displayed during the For Each loop. But with the query above, the For Each finds the PossibleAnswers data.
I checked the value of
db.Configuration.LazyLoadingEnabled
just before my view was called and it was false. But I still had to make a query with the Include() method to force the data to be brought into the context.
This is the full definition of the table in question:
Public Class Question
Public Enum qType
TrueFalse
MultipleChoice
ShortAnswer
End Enum
Public Property QuestionID As Integer
Public Property Text As String
Public Property Type As qType
Public Property PossibleAnswers As New List(Of qAnswer)
Public Property UsedBySurveys As New List(Of qSurvey)
End Class
Can anybody suggest what I may not be understanding about this?
Thanks.
Best Regards,
Alan
If you want all results from your context to automatically load the navigation properties, you should remove the Me.Configuration.LazyLoadingEnabled = False line from your context class.
You can also explicitly set lazy loading to false for an instance of your context: ie.
Using db As New MyContext
db.Configuration.LazyLoadingEnabled = True
Dim myQuery = db.Questions.Where(Function(x) x.QuestionID = 6).Single()
End Using
With Lazy Loading set to false, you have to explicitly tell the EF to include navigation properties along with the result set. You do that by using the .Include function (as you did). Calling the .Include function is called Eager Loading.
If you set the lazy loading property to true, the navigation properties will be pulled back from the database automatically. Here's a quick run down of Lazy Loading from MSDN:
http://msdn.microsoft.com/en-us/library/vstudio/dd456846(v=vs.100).aspx
I had an idea to use custom attributes on the properties in a class for databinding purposes in a winforms interface. For example, setting and changing the backcolor, forecolor, and tooltip on a textbox with invalid data. I find that I can bind up the control properties of txtTest for backcolor, etc., to a custom attribute such as BackColorAttr decorating a property in the class such as Name, with no problem. The property value itself is bound to the Text of the textbox, two-way binding of that works just fine, and the initial backcolor, forecolor, etc., are set from the initial values of the custom attributes just the way I had hoped. I'm doing all this through a BindingHelper class that reduces all the coding to a couple of generic methods.
Where I'm stumped is manipulating the values of the custom attributes at a later time. Changing the backcolor to red, for example. Nothing I've tried seems to work. Has anybody tried something like this, or have some guidance as to how I might proceed?
I dont quite follow the first part or what binding has to do with colors or Attributes, but thats not how Attributes work. They are not Property wrappers and Properties, Methods and Types have no idea of the Attributes associated with them (and vice-versa). They are meta data compiled into the assembly. As such, you cant change the value in any meaningful way.
Test class and test Attribute:
Public Class BarState
Inherits Attribute
Public Property State As String
Public Sub New(t As String)
State = t
End Sub
End Class
Public Class Foo
<BarState("red")>
Public Property Name As String
End Class
Since State is a property, test if we can set it:
Dim f As New Foo
' get props for the Type
Dim pi As PropertyInfo = f.GetType.GetProperty("Name")
Dim attr = pi.GetCustomAttributes(GetType(BarState), False)
If attr.Length > 0 Then
' get prop info for the State property on the Attr Type
Dim pa As PropertyInfo = attr(0).GetType.GetProperty("State")
' change it
CType(attr(0), BarState).State = "GREEN"
' or
'pa.SetValue(attr(0), "GREEN", Nothing)
' print it (prints "GREEN" but it does not persist)
Console.WriteLine(CType(attr(0), BarState).State)
End If
'get the attr again as you might do next time thru
attr = pi.GetCustomAttributes(GetType(BarState), False)
' print the value (Print "red")
Console.WriteLine(CType(attr(0), BarState).State)
The first print will be "GREEN" but that is only for this instance - it does not persist. The next time you get it, it reverts to "red". Since an Attribute is a Type, we can try to Reflection to change the value using pa.SetValue(attr(0), "GREEN", Nothing) which is commented out. It still wont persist because "red" is compiled into the assembly which is what your starting point will always be.
It might seem like you could keep a Dictionary or collection of the attribute instances for all the properties on all the types. That could work except, they all look alike, so you would have to create a hash to track which Attribute instance goes with what Property on what Type.
And you'd have to keep that collection in sync with the underlying instance objects. The Attribute instance wont know the instance it came from is gone and so the state setting should revert, so your Attribute manager would need to handle that.
You might look into "weavers" which use attributes to tag things (like a value range) then rewrite the assembly to weave in range checks for the tagged properties. Sort of sounds like what you are after I dont know what else they might do along the lines of what you describe.
I'm using a Code First Entity Framework approach, and in my OnModelCreating function I have the following code:
With modelBuilder.Entity(Of FS_Item)()
.HasKey(Function(e) e.ItemKey)
.Property(Function(e) e.ItemRowVersion).IsConcurrencyToken()
.HasMany(Function(e) e.ItemInventories) _
.WithRequired(Function(e) e.Item).HasForeignKey(Function(e) e.ItemKey)
End With
Elsewhere I have a Web API Get implementation with some diagnostic code I'm looking at in a debugger:
Public Function GetValue(ByVal id As String) As FS_Item
GetValue = If(data.FS_Item.Where(Function(i) i.ItemNumber = id).SingleOrDefault(), New FS_Item())
Dim c = GetValue.ItemInventories.Count
End Function
I expect that c should get a non-zero value by looking up rows in the FS_Inventory view where ItemKey matches the retrieved FS_Item row's ItemKey. But I'm getting 0 even though there are matching rows. Am I calling .HasMany, .WithRequired and .HasForeignKey properly?
Note that .WithRequired is operating on the return value from the previous line whereas the other lines are operating on the With block expression.
Edit This model for FS_Item has been requested. Here it is:
Partial Public Class FS_Item
Public Property ItemNumber As String
Public Property ItemDescription As String
Public Property ItemUM As String
Public Property ItemRevision As String
Public Property MakeBuyCode As String
' Many many more properties
Public Property ItemRowVersion As Byte()
Public Property ItemKey As Integer
Private _ItemInventories As ICollection(Of FS_ItemInventory) = New HashSet(Of FS_ItemInventory)
Public Overridable Property ItemInventories As ICollection(Of FS_ItemInventory)
Get
Return _ItemInventories
End Get
Friend Set(value As ICollection(Of FS_ItemInventory))
_ItemInventories = value
End Set
End Property
End Class
Edit Learned something interesting. If I change Dim c = GetValue.ItemInventories.Count to this:
Dim c = data.FS_ItemInventory.ToList()
Dim correctCount = GetValue.ItemInventories.Count
Then correctCount gets the value of 3. It's like it understands the association between the objects, but not how to automatically query them as I'm used to coming from LINQ-to-SQL. Is EF different somehow in this regard?
Edit I have determined that I can make the associated objects load using this explicit loading code:
data.Entry(GetValue).Collection(Function(e) e.ItemInventories).Load()
What I want to understand now is what exactly determines whether an entity will load lazily or not? From all indications I can find, it should have loaded lazily. I even tried changing the declaration of ItemInventories to this, but then I got a NullReferenceException when trying to access it:
Public Overridable Property ItemInventories As ICollection(Of FS_ItemInventory)
It turns out that code which I thought was unrelated had disabled lazy loading. I have this in the constructor of FSDB:
DirectCast(Me, IObjectContextAdapter).ObjectContext.ContextOptions.ProxyCreationEnabled = False
Thanks to EF 4 - Lazy Loading Without Proxies I see that this will also disable lazy loading. The reason that code had been added was due to another error:
Type
'System.Data.Entity.DynamicProxies.FS_Item_64115A45C642902D6044AFA1AFD239E7DCB82FD000A10FE4F8DE6EA26A2AB418'
with data contract name
'FS_Item_64115A45C642902D6044AFA1AFD239E7DCB82FD000A10FE4F8DE6EA26A2AB418:http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies'
is not expected. Consider using a DataContractResolver or add any
types not known statically to the list of known types - for example,
by using the KnownTypeAttribute attribute or by adding them to the
list of known types passed to DataContractSerializer.
And according to Serialization of Entity Framework objects with One to Many Relationship, the easy solution for that was to disable proxies.
If, in code, I wanted to do something like the following, what would my class definition need to look like? (Keep in mind the fruit/language thing is just an example)
dim myfruit as new fruit()
myfruit.name = "apple"
myfruit.name.spanish = "manzana"
Here is the class I have, just not sure how to add the "sub property".
Public Class Fruit
Private _name As String
Public Property name() As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
End Class
In general, for you to have a "sub property", you'd need to make your Property a class itself. This would mean the subproperty is actually a property on the class exposed by the top level property.
Effectively, you'd change the name property from a string to a "Translations" class or similar, ie:
Public Class Fruit
Public Property Name As New Translations
End Class
Public Class Translations
Public Property Primary As String
public Property Spanish As String
End Class
However, this will likely break the code you're displaying, as the second line would need to have a different syntax, ie:
myfruit.Name.Primary = "green"
myfruit.Name.Spanish = "verde"
However, if the goal here is to just handle translation of your user interface, there are other options. For details, see Introduction to International Applications Based on the .NET Framework on MSDN.
I initially thought Reed´s answer was what I was after. In my application, I wanted to use the "sub-property" to set a property on a Form Label. (I was trying to emit only the Label properties I wanted available to a Custom Control.)
I tried this:
Public Class Fruit
Private _name As New Translations
Public Property Name As Translations
Get
Return _name
End Get
Set(value As Translations)
_name = value
_PrimaryCaps = _name.Primary.ToUpper
End Set
End Property
'Private variable is automatically added for unexpanded property
Public Property PrimaryCaps As String
End Class
Public Class Translations
Public Property Primary As String
Public Property Spanish As String
End Class
Then
Dim myFruit As New Fruit
myFruit.Name.Primary = "Apple"
myFruit.Name.Spanish = "Manzana"
Dim primaryCaps As String = myFruit.PrimaryCaps
Weirdly - to me at least - this doesn't work; myFruit.PrimaryCaps returns nothing rather than the hoped-for "APPLE". It appears that the Set for Name is never executed. (Placing the _PrimaryCaps assignment above the Get Return does work, however.)
(I realize that a PrimaryCaps property could be added to the Translations class but, again, this doesn't help if you're wanting to set a foreign variable from within an instance of Fruit.)
I don't know if this is "by-design", whether I've simply misunderstood the intended functionality or what. One thing I did alight on after further research was that this structure isn't very common at all in .NET; for example setting a control's size is done as follows:
oControl.Size = New Drawing.Size(20, 15)
rather than simply setting, say, the Width property directly:
oControl.Size.Width = 20
(The latter won't compile: "Expression is a value and therefore cannot be the target of an assignment.")
If anyone has any more insight than I on this, I'd love to hear it. I know this could simply be done by using an instance of Fruit, for example, but that's not the point.
Solution
I did some googling and found this forum post, and here is what I needed to do:
Imports System.ComponentModel
<EditorBrowsable(EditorBrowsableState.Always), Browsable(True), Bindable(True), _
DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)> _
Overrides Property Text() As String
Get
Return ControlText.Text
End Get
Set(ByVal value As String)
ControlText.Text = value
End Set
End Property
I should state that I am really new to creating custom controls, so I do not know all of the ins-and-outs of the whole process.
I am creating a custom control that functions similarly to a checkbox, but uses two images as checked/unchecked. I am trying to add a Text property, but it gives me this warning:
Property Text() As String
Get
Return ControlText.Text
End Get
Set(ByVal value As String)
ControlText.Text = value
End Set
End Property
"property 'Text' shadows an overridable method in the base class 'UserControl'. To override the base method, this method must be declared 'Overrides'."
Ok, so that is no problem. I change my declaration to Overrides Property Text() As String, but when I go to test it out 'text' is not listed under properties. Is there additional steps I need take to get my result?
More details
My control consists of 2 (or 3, depending on how you look at it):
PictureBox - Displays a checked/unchecked image
Label - The Text that is being displayed on the control
PictureCheckBox - This is the actual control's name
The PictureBox is docked to the left of the PictureCheckBox, and the Label is docked on the right side:
EDIT Scratched the bit about Text being non-virtual. It is in fact virtual / overridable.
But I'm curious, why do you want to do this. In your specific example you're just calling into the base property so it doesn't appear to do anything.
Where are you expecting this value to be shown and how are you setting it?
Text is a non-virtual / overridable method on Control. There is no way for you to override the property. If you want to re-define the property you can use the Shadows keyword.
You should make sure it's a public property
Public Overridable Property Text() As String
Get
Return ControlText.Text
End Get
Set(ByVal value As String)
ControlText.Text = value
End Set End Property
This might be a stupid question, but some of them still needs to be asked just to make sure:
Have you compiled since you made the changes? Using hotkeys in Visual Studio, press [Ctrl]+[Shift]+[b] to compile the entire solution.