I have a class with second-level properties that is bound to controls on my form using a BindingSource. That part works fine: the data is displayed and changes are propagated to the bound object.
Now I need to enumerate all databindings. My approach is to loop over the Bindings property of the BindingManagerBase. Unfortunatly, the collection does not contain the second-level properties.
Could somebody kindly explain why I don't see the second-level properties? Alternatively, if there is a different approach to iterate over alle bindings including the second-level properties I would be happy, too.
To illustrate my problem I created a minimal example. To use the following code add two textboxes to an empty form and add the code to the form's Load-event:
Dim bs = New BindingSource()
bs.DataSource = GetType(OuterClass)
TextBox1.DataBindings.Add("Text", bs, "OuterValue")
TextBox2.DataBindings.Add("Text", bs, "Inner.InnerValue")
Dim mgr As BindingManagerBase = BindingContext(bs)
For Each b As Binding In mgr.Bindings
Debug.Print(b.BindingMemberInfo.BindingField)
Next
The Debug.Print will only print "OuterValue" because that is the only binding in mgr.Bindings which is the problem I have.
The used classes in the above code are defined as follows:
Public Class OuterClass
Property OuterValue As String
Property Inner As InnterClass
End Class
Public Class InnterClass
Property InnerValue As String
End Class
Sascha
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.
I'm trying to create a control (Parent Class) that uses a custom grid (Child Class). The grid has a series of constructors and methods for populating itself based on property values in the [parent] control.
The only way I found to make these property values available to the grid is by making them Shared but that's causing me all kinds of issues.
REQUIREMENTS
Properties in the control (parent) must be accessible to the grid (child).
Properties in the control must be visible in the design-time properties explorer.
The grid class must only be instantiable by the parent class.
As a side note: please indicate if your answer will allow me to share properties/methods back and forth between child and parent. That would be nice, but just a bonus.
Thanks ;)
EDIT - A VERY simple example based on my situation:
Partial Public Class catContent
Inherits System.Web.UI.UserControl
Protected Sub Page_Load(sender, e) Handles Me.Load
Page.Controls.Add(New CategoryResultGrid(category))
End Sub
Private Shared _product As String = String.Empty
Shared Property Product() As String
Get
Return _product
End Get
Set(ByVal value As String)
_product = value.Trim()
End Set
End Property
Private Class CategoryResultGrid
Inherits GridView
Sub New(ByVal category As String)
'How do I access "Product" here without sharing it?
End Sub
End Class
End Class
Do NOT use shared, it will break your app as soon as you put more than one of you custom control in your app.
If you only want the Grid to exist in the context of the Parent control, then consider exposing it similar to how the ListView control exposes its Items collection.
If you want your Grid to access fields in the (parent) Control there are several ways to do that. You could pass an instance of the Parent to the Grid, you can let the Grid use the standard Control methods to get its parent reference, or you can implement the Grid as an inner class of the Parent.
I'm having some issues and it's all explained in some simplified code
I have a structure
Public Structure myStruct
Public Property name As String
Public Property span As Double
Public Property offs As Double
End Structure
Which is instantiated in a Singleton object as follow
Public myValues(10) As myStruct
Then on a form, I'm using the structure as the DataSource for a SourceBinder and the binder as DataSource for a DataGridView.
On loading the form, I get all the values into the binder
For i As Integer = 0 To 9
binder.Add(singleton.getRef.myValues(i))
Next i
All the values are shown on the grid.
User is supposed to be able to change the values, which should reflect on myValues but no matter, what code I put on CellValueChanged or CurrentChanged. I can't make it reflect the changes.
Using breakbpoints on those events, I noticed that grid.row().cell().values and binder.current never changes.
I tried also placing a button and directly changing myValues and reseting the binder binder.ResetBindings(False) and nothing happens.
As far as I saw all ReadOnly properties are set to false.
I've also tried setting VirtualMode to true on the grid and catching CellValuePushed and CellValueNeeded events but those are never called. (that was on a guessing base)
I'm really out of ideas on this one, if anyone can help me!
thanks.
Try using a class instead:
Public Class myStruct
Public Property name As String
Public Property span As Double
Public Property offs As Double
End Class