Moi,
Tein usercontrollin jossa datagridview, mutta en pysty muokkaamaan dgv:n sarakkeita design tilassa.
Löysin tämän ohjeen ja käänsin sen vb:lle, mutta kun klikkaan designerissä usercontrollin Columns propertyä, saan virheilmoituksen : "object reference not set to an instance of an object".
Tässä vb koodi.
Imports System.ComponentModel
Imports System.Drawing.Design
Public Class uc_test
<Editor(GetType(MyColumnEditor), GetType(UITypeEditor))>
<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>
Public ReadOnly Property Columns As DataGridViewColumnCollection
Get
Return Me.DataGridView1.Columns
End Get
End Property
End ClassPublic Class MyColumnEditor
Inherits UITypeEditor
Public Overrides Function GetEditStyle(context As ITypeDescriptorContext) As UITypeEditorEditStyle
Return UITypeEditorEditStyle.Modal
End Function
Public Overrides Function EditValue(context As ITypeDescriptorContext, provider As IServiceProvider, value As Object) As Object
'Return MyBase.EditValue(context, provider, value)
Dim field = context.Instance.GetType().GetField("datagridview1",
System.Reflection.BindingFlags.NonPublic Or
System.Reflection.BindingFlags.Instance)
Dim datagridview1 = DirectCast(field.GetValue(context.Instance), DataGridView)
datagridview1.Site = DirectCast(context.Instance, Control).Site
Dim columnsProperty = TypeDescriptor.GetProperties(datagridview1)("Columns")
Dim tdc = New TypeDescriptionContext(datagridview1, columnsProperty)
Dim editor = DirectCast(columnsProperty.GetEditor(GetType(UITypeEditor)), UITypeEditor)
Dim result = editor.EditValue(tdc, provider, value)
datagridview1.Site = Nothing
Return result
End Function
End ClassPublic Class TypeDescriptionContext
Implements ITypeDescriptorContext
Private EditingObject As Control
Private EditingProperty As PropertyDescriptor
Public Sub New(ByVal obj As Control, ByVal prop As PropertyDescriptor)
EditingObject = obj
EditingProperty = prop
End Sub
Public ReadOnly Property Container As IContainer Implements ITypeDescriptorContext.Container
Get
Return EditingObject.Container
End Get
End Property
Public ReadOnly Property Instance As Object Implements ITypeDescriptorContext.Instance
Get
Return EditingObject
End Get
End Property
Public ReadOnly Property PropertyDescriptor As PropertyDescriptor Implements ITypeDescriptorContext.PropertyDescriptor
Get
Return EditingProperty
End Get
End Property
Public Sub OnComponentChanged() Implements ITypeDescriptorContext.OnComponentChanged
End Sub
Public Function GetService(serviceType As Type) As Object Implements IServiceProvider.GetService
Return EditingObject.Site.GetService(serviceType)
End Function
Public Function OnComponentChanging() As Boolean Implements ITypeDescriptorContext.OnComponentChanging
Return True
End Function
End ClassOnko koodissa jokin virhe, vai mikä maksaa..?
Virheilmoituksessa kerrotaan myös millä rivillä virhe tapahtuu (Stack trace). Koita myös debugata, itse virhe tulee siitä että käytät esimerkiksi objektin propertyä, objektin ollessa null.
Virheilmoitus tulee siis designerissä kun klikkaan usercontrollin columns-propertyä.
Kesti hetken kun selvitin miten design-time koodia debugataan. Jos jotain kiinnostaa niin tuolta löytyy esimerkki.
No, debuggauksessa kuitenkin selvisi, että virhe tulee riviltä:
Dim datagridview1 = DirectCast(field.GetValue(context.Instance), DataGridView)
,koska edellisellä rivillä luotu field = nothing
Dim field = context.Instance.GetType().GetField("datagridview1",
System.Reflection.BindingFlags.NonPublic Or
System.Reflection.BindingFlags.Instance)Miksi field = nothing...? sitä en ymmärrä.(enkä kovin paljon muutakaan tuosta koodista)
Lähde purkamaan debugilla tuota context.instancea, ja selvitä löytyykö sieltä mitään. Eli oletko esimerkiksi nimennyt datagridview componenttisi muulla nimellä kuin datagridview1.
Ei onnistu. yritin, mutta en ymmärrä puoliakaan mitä tuossa pitäisi tapahtua.
Voisitko Groovyb(tai joku muu) kokeilla tuota omalla koneella. (Tai ehkäpä jo tiedät miksei tuo toimi.)
Vai onko kenties muita keinoja muokata usercontrollin dgv:n sarakkeita formilla design aikana (siten, että muutokset pysyisivät myös ajon aikana)?
Kun laitat breakpointin field -riville, ja watchiin vaikka tuo context.Instance.GetType(), mitä palautuu? Palautuuko mitään?
Eli missä vaiheessa null reference on - context? .Instance? .GetType()? jne
HAHAA! Keksin sen!
Usercontrol luokkaan pitää laittaa property joka palauttaa kyseisen dgv:n ja sitten MyColumnsEditoriin .GetField sijaan .GetProperty.
Tämä kylläkin aiheuttaa taas uuden ongelman
josta Harald Coppoolse mainitsi keskustelussa josta koodin lainasin kirjoitti:
Checked it, and it works. Be aware though that the columns that you add during design are added as members to the form, not to the user control. Luckily I don't need them in this situation. So I can set GenerateMember to false
En osaa nyt sanoa auttaako tuo GenerateMember=false minua, mutta.. .mihin laitan/mistä muutan tuon GenerateMemberin?
ari kood kirjoitti:
(19.09.2017 09:20:48): HAHAA! Keksin sen! Usercontrol luokkaan...
Muistaakseni GenerateMember löytyy design-puolelta ihan kyseisen komponentin Properties-listasta.
Designer yrittää tunkea sarakkeita kahdesti datagridviewiin (rivit 10 ja 15). Kun kommentoin tuon toisen rivin pois, toimii juttu kuin pitääkin.
Private Sub InitializeComponent()
Me.components = New System.ComponentModel.Container()
Dim Uc_test1 As WindowsApplication21.uc_test
'
'
Uc_test1 = New WindowsApplication21.uc_test()
'
'
Uc_test1.Columns.AddRange(New System.Windows.Forms.DataGridViewColumn() {Me.Column1, Me.Column2})
'
'
Uc_test1.DGV.AutoGenerateColumns = False
Uc_test1.DGV.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize
Uc_test1.DGV.Columns.AddRange(New System.Windows.Forms.DataGridViewColumn() {Me.Column1, Me.Column2})
'
'
'
'Column1
'
Me.Column1.HeaderText = "Column1"
Me.Column1.Name = "Column1"
'
'Column2
'
Me.Column2.HeaderText = "Column2"
Me.Column2.Name = "Column2"
'
'
Me.Controls.Add(Uc_test1)
End SubTämän johtuu nyt nähtävästi tuosta kun minulla on kaksi propertya periaatteessa samasta asiasta. Ja molempien on pakko olla DesignerSerializationVisibility.Content, muuten ei toimi kuin pitäisi. Ja molempia tarvitaan;
- DGV, jotta MyColumnsEditor löytää dgv:n, ja että voin asettaa esim. datasourcen tai pääsen hämmentämään muitakin propertyja.
- Columns, jotta pääsen muokkaamaan sarakkeita.
Public Class uc_test
<Browsable(True)>
<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>
Public ReadOnly Property DGV As DataGridView
Get
Return Me.DataGridView1
End Get
End Property
<Editor(GetType(MyColumnEditor), GetType(UITypeEditor))>
<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>
Public ReadOnly Property Columns As DataGridViewColumnCollection
Get
Return Me.DataGridView1.Columns
End Get
End Property
End Classuc_test.Datagridview1 componentille on siis annettu datasource DGV-propertyn alta. Kaikki datasourcen sarakkeet ilmestyvät oikein dgv:lle. Mutta nyt kun haluan piilottaa osan sarakkeista, ei se onnistu DGV-propertyn kautta (Object reference not set to an instance of an object.). Sarakkeiden piilottaminen onnistuu itse tehdyn usercontrollin Columns-propertyn kautta, mutta aina kun teen muutoksia columns-propertyyn, ilmestyy tuo toinen rivi designer koodiin eikä ajo enää onnistu. Ja aina kun teen muutoksia DGV-propertyyn, palautuu alkuperäiset sarakkeet näkyville.
Mikä eteen? Nyt meni taas sormi suuhun.
edit: ehkä sittenkin pitäisi saada tuo .getfield toimimaan. Mutta tarvitsen kuitenkin tuon DGV-propertyn.
Luovutin (jo 21.9), en löytänyt ratkaisua. Teen tarvittavat muutokset koodilla form load eventissä.
Aihe on jo aika vanha, joten et voi enää vastata siihen.