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 Class
Public 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 Class
Public 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 Class
Onko 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 Sub
Tä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 Class
uc_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.