Avatar billede mcbyte Nybegynder
16. februar 2005 - 00:20 Der er 45 kommentarer og
1 løsning

Data i ListBox.Items bliver ødelagt

Hej

Jeg har et problem som jeg simpelthen ikke kan gennemskue, og kan ikke finde nogen som helst løsning nogle steder.

Det er således at jeg har implementeret min egen FontListBox som nedarver fra den oprindelige ListBox control, men med nogle ændringer i OnMeasureItem, OnResize og OnDrawItem for at kunne vise de data jeg ønsker.

Datane er fonte - dvs. navnet på fonten efterfulgt af en tekststreng vist med den font.

Jeg benytter denne FontListBox til to formål - enten at vise de i systemet installerede fonte eller at vise fonte liggende i en brugerspecificeret mappe.

Når jeg viser systemfonte så virker det perfekt. Alt tegnes korrekt og ingen problemer der.

MEN når jeg indlæser fonte fra et custom bibliotek, via PrivateFontCollection, så sker der underlige ting og sager. Hvis jeg resizer mit vindue (og derved også controllen), eller overlapper med andre vinduer (umiddelbart så er det ved Paint at det mystiske sker), så bliver mine data, altså ListBox.Items ødelagt - og jeg får ArgumentException i navnefeltet på fontene i Items.

Det giver for mig ingen mening, og har prøvet alverdens mulige løsninger og hacks, men intet hjælper. Det pudsige er at den tegner controlen ganske fint til at starte med, men så på et vilkårligt tidspunkt hvis man f.eks. resizer controlen, så dør datane.

Håber at der er nogle der har forslag til hvorledes jeg kan løse dette - evt. hvordan jeg kan finde kilden til skidtet - ja hvad som helst!

Ps. Bare sig til hvis i har brug for nærmere præcisering eller kodestykker.

Med venlig hilsen
Thomas René Sidor
Avatar billede burningice Nybegynder
16. februar 2005 - 12:01 #1
er der på noget som helst tidspunkt du ændrer på din items-collection efter at du har loadet alle fontene ind fra en mappe?

Går ud fra at du enumererer igennem den en del gange i forbindelse med tegningen af din ListBox. En mulighed var at lave en Clone af din ItemsCollection inden du enumererer igennem den for at være sikker på at den hele tiden er konsistens.
Avatar billede mcbyte Nybegynder
16. februar 2005 - 12:16 #2
Ja, du har ret i at jeg ittererer igennem mine items ret ofte - åbenlyst når jeg skal tegne dem. Jeg er dog imidlertid også nødsaget til at fjerne alle items og genindsætte dem hver gang jeg resizer controlen - for ellers bliver OnMeasureItem ikke kaldt, og derved kan controlen tegnes forkert.

Til dette har jeg prøvet lidt forskelligt - tænkte nemlig det samme som dig - så derfor prøvede jeg at have en konsistent kopi af mine items liggende som jeg så bruger til at indsætte fra. Således at det ikke er de oprindelige items der indsættes, men derimod nogle tilsvarende items.

Og her er noget endnu mere pudsigt - også de items i min 'backup' collection ødelægges - så umiddelbart virker det ikke som en fejl i ListBox contolen, men noget der ganske enkelt sker med de Font objekter jeg laver ud fra filer...

Hmm, jeg ved ikke om det er helt sort det jeg skriver - men jeg værdsætter din hjælp.
Avatar billede burningice Nybegynder
16. februar 2005 - 12:30 #3
med mindre at du laver en decideret DeepClone, er det de samme fontoboejcter der ligger i din Items Collection og "backup'en", da det kun er referencerne du kopierer.

Det virker da underligt at du skal fjerne alle dine objecter og indsætte dem igen. Kan du ikke bare kalde OnMeasureItem manuelt?
Avatar billede mcbyte Nybegynder
16. februar 2005 - 12:36 #4
Backuppen og de oprindelige items er adskilt. De oprettes på to forskellige måder og to forskellige tidspunkter. Så der burde ikke være noget problem med at det blot er referencer der kopieres.

Mht. manuelt kald af OnMeasureItem - så virker det ikke umiddelbart til at det kan lade sig gøre. Eller anyway - det MeasureItemEventArg argument kaldet kræver, kan jeg ikke lige gennemskue hvor jeg skulle få fra i fald jeg ønsker at kalde OnMeasureItem(MeasureItemEventArgs) manuelt.
Avatar billede burningice Nybegynder
16. februar 2005 - 13:09 #5
så din OnMeasureItem bliver kun kaldt når at du tilføjer items, og ikke når du resizer kontrollen?
Avatar billede mcbyte Nybegynder
16. februar 2005 - 13:16 #6
Jo OnMeasureItem bliver kaldt for hvert Item hver gang jeg resizer, da jeg for hver OnResize genindsætter alle elementerne... sådan:

protected override void OnResize(EventArgs e)
{
    Object[] items = new object[Items.Count];
    Items.CopyTo(items, 0);
    Items.Clear();

    foreach (Object item in items)
    {
        Items.Add(item);
    }

}
Avatar billede burningice Nybegynder
16. februar 2005 - 13:24 #7
men hvis du nu bare lader dine items være, så bliver OnMeasureItem ikke kaldt?
Avatar billede mcbyte Nybegynder
16. februar 2005 - 13:28 #8
Lige præcis... OnMeasureItem kaldes hun ved indsættelse...
Avatar billede mcbyte Nybegynder
16. februar 2005 - 13:29 #9
Men det er ikke umiddelbart koden til FontListBox'en den er gal med... For hvis jeg deaktivere alle mine overrides så crasher den stadig (og controlen bliver selvfølgelig tegnet forkert).
Avatar billede burningice Nybegynder
16. februar 2005 - 13:32 #10
okay okay.. prøv at paste hele koden her, for det lyder lidt mistænksomt
Avatar billede burningice Nybegynder
16. februar 2005 - 13:39 #11
umiddelbart kan jeg dog hellerikke forstå hvorfor at du vil ændre højden på dine items når at listboxen bliver reziset... det skal jo have samme størrelse
Avatar billede mcbyte Nybegynder
16. februar 2005 - 13:40 #12
Hvilken del af koden vil du have? Umiddelbart er problemet jo ikke OnResize, OnMeasureItem og OnDrawItem - da samme fejl opstår hvis disse fjernes.
Avatar billede mcbyte Nybegynder
16. februar 2005 - 13:42 #13
Nej, de skal ikke have samme størrelse - pointen er at jeg gerne vil vise en streng - og i fald denne streng, tegnet med den givne font, er for lang til at kunne vises i sin fulde længde, da brydes den op - hvorved højden på Item'et naturligvis ændres. Så som bredden på ListBox'en ændres, så ændres Item's højde.
Avatar billede burningice Nybegynder
16. februar 2005 - 13:50 #14
dette burde løse dit measureitemproblem

protected override void OnResize(EventArgs e)
        {
            base.OnResize (e);

            for (int i = 0; i < this.Items.Count; i++)
            {
                this.OnMeasureItem(new MeasureItemEventArgs(this.CreateGraphics(), i));
            }
        }
Avatar billede mcbyte Nybegynder
16. februar 2005 - 13:56 #15
Hmm, ja - det ser ellers korrekt nok ud - men virker desværre ikke.
Avatar billede burningice Nybegynder
16. februar 2005 - 13:56 #16
lad mig se hele din kode til listboxen.. den kan vel ikke være så lang igen?
Avatar billede mcbyte Nybegynder
16. februar 2005 - 13:58 #17
Nej den er ikke så lang... her:

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Windows.Forms;

namespace Font_Visualizer
{
    /// <summary>
    /// Summary description for FontListBox.
    /// </summary>
    public class FontListBox : ListBox
    {
        public bool AntiAlias
        {
            get { return _AntiAlias; }
            set { _AntiAlias = value; }
        }

        private bool _AntiAlias = false;

        public string DrawString
        {
            get { return _DrawString; }
            set { _DrawString = value; }
        }

        private string _DrawString = "";

        private Font verdana = new Font("Verdana", 8);

        public FontListBox()
        {
            this.DrawMode = DrawMode.OwnerDrawVariable;
            this.ScrollAlwaysVisible = true;
            //base.SetStyle(ControlStyles.DoubleBuffer, false);

        }




        protected override void OnMeasureItem(MeasureItemEventArgs e)
        {
            if (Site != null)
                return;
            if (e.Index > -1)
            {
                Font fn = (Font) Items[e.Index];

                SizeF sf = e.Graphics.MeasureString(_DrawString, fn, Width);

                e.ItemHeight = (int) sf.Height + (int) verdana.GetHeight() + 10;
                e.ItemWidth = Width;
            }

        }


        protected override void OnResize(EventArgs e)
        {
            Object[] items = new object[Items.Count];
            Items.CopyTo(items, 0);
            Items.Clear();

            foreach (Object item in items)
            {
                Items.Add(item);
            }

        }
       



        protected override void OnDrawItem(DrawItemEventArgs e)
        {
            if (_AntiAlias)
            {
                e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
                e.Graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
            }

            if (Site != null)
                return;
            if (e.Index > -1)
            {
                Font fn = Items[e.Index] as Font;

                if (fn != null)
                {
                    if ((e.State & DrawItemState.Focus) == 0)
                    {
                        RectangleF rec = new RectangleF(e.Bounds.Left, e.Bounds.Top + verdana.GetHeight(), e.Bounds.Width, e.Bounds.Height);
                        e.Graphics.FillRectangle(new SolidBrush(BackColor), e.Bounds);
                        e.Graphics.DrawString(fn.Name, verdana, new SolidBrush(this.ForeColor), e.Bounds);
                        e.Graphics.DrawString(_DrawString, fn, new SolidBrush(this.ForeColor), rec);
                        e.Graphics.DrawRectangle(new Pen(SystemColors.Highlight), e.Bounds); //Border
                    }
                    else
                    {
                        RectangleF rec = new RectangleF(e.Bounds.Left, e.Bounds.Top + verdana.GetHeight(), e.Bounds.Width, e.Bounds.Height);
                        e.Graphics.FillRectangle(new SolidBrush(SystemColors.Highlight), e.Bounds);
                        e.Graphics.DrawString(fn.Name, verdana, new SolidBrush(this.ForeColor), e.Bounds);
                        e.Graphics.DrawString(_DrawString, fn, new SolidBrush(this.ForeColor), rec);
                    }
                }
            }
        }
    }
}
Avatar billede mcbyte Nybegynder
16. februar 2005 - 14:00 #18
Men som sagt, så tror jeg at problemet ligger et helt andet sted eftersom at deaktivering af de 3 overrides resulterer i samme problem.
Avatar billede burningice Nybegynder
16. februar 2005 - 14:23 #19
jeg får på ingen måde nogen fejl, og det med resize ser ud til at virke fint.. teksten hopper i hvert ned på næste linje hvis boxen bliver for smal

Jeg bruger denne onrezise

protected override void OnResize(EventArgs e)
        {
            if (this.Items.Count > 0)
            {
                Graphics g = this.CreateGraphics();

                for (int i = 0; i < this.Items.Count; i++)
                {
                    this.OnMeasureItem(new MeasureItemEventArgs(g, i));
                }
            }
        }

og tilføjer fontobjecter her:

private void button1_Click(object sender, System.EventArgs e)
        {
            Font f = new Font(FontFamily.GenericSerif, 10f);
            this.listBox1.Items.Add(f);
        }
Avatar billede mcbyte Nybegynder
16. februar 2005 - 14:25 #20
Ok, så prøv at tilføje en font, fra en fil, som du ikke har installeret i systemet. Mit virker nemlig også perfekt hvis det er fonts fra InstalledFontCollection() jeg bruger... men lige så snart jeg læser fonte ind som ikke er installeret, så dør den pludselig.
Avatar billede mcbyte Nybegynder
16. februar 2005 - 14:28 #21
Og din OnResize virker stadig ikke for mig - sure, den bryder linierne, men den gør ikke pladsen højere i fald man ikke kan se hele linien efter bruddet...
Avatar billede burningice Nybegynder
16. februar 2005 - 14:38 #22
hvordan vælger du din font?
Avatar billede mcbyte Nybegynder
16. februar 2005 - 14:43 #23
private void displayFonts()
{

    FontFamily[] ffs = getFontCollection().Families;
    Font f = null;

    foreach (FontFamily ff in ffs)
    {
        f = null;

        try
        {
            f = checkFontStyle(ff);
            if (f != null)
                fontList.Items.Add(f);
        }
        catch
        {
            errors++;
            log("Unable to display font: " + ff.Name);
        }
    }


}

private FontCollection getFontCollection()
{
    FontCollection fc;
    if (menuItemWindows.Checked)
    {
        fc = new InstalledFontCollection();
    }
    else
    {
        PrivateFontCollection pfc = new PrivateFontCollection();
        string[] files = Directory.GetFiles(folderBrowserDialog.SelectedPath);
        foreach (string file in files)
        {
            if (file.EndsWith(".ttf") || file.EndsWith(".fon"))
            {
                pfc.AddFontFile(file);
            }
        }

        fc = pfc as FontCollection;
    }
    return fc;
}

private Font checkFontStyle(FontFamily ff)
{
    Font printFont = null;
    string style = comboBox1.SelectedItem.ToString();
    int size = (int) numSize.Value;

    switch (style)
    {
        case "Regular":
            if (ff.IsStyleAvailable(FontStyle.Regular))
                printFont = new Font(ff, size, FontStyle.Regular);
            break;

        case "Bold":
            if (ff.IsStyleAvailable(FontStyle.Bold))
                printFont = new Font(ff, size, FontStyle.Bold);
            break;

        case "Italic":
            if (ff.IsStyleAvailable(FontStyle.Italic))
                printFont = new Font(ff, size, FontStyle.Italic);
            break;
        case "Bold + italic":
            if (ff.IsStyleAvailable(FontStyle.Bold | FontStyle.Italic))
                printFont = new Font(ff, size, FontStyle.Bold | FontStyle.Italic);
            break;
    }


    if (printFont == null)
        throw(new ArgumentException());

    return printFont;

}
Avatar billede mcbyte Nybegynder
16. februar 2005 - 14:44 #24
har lige fjernet alt det unødvendige GUI halløj...
Avatar billede burningice Nybegynder
16. februar 2005 - 14:46 #25
og hvordan ser din PrivateFontCollection ud? Jeg mener... hvordan får du konverteret en normal fon-fil til et gyldigt Font-object
Avatar billede mcbyte Nybegynder
16. februar 2005 - 14:47 #26
Avatar billede burningice Nybegynder
16. februar 2005 - 15:02 #27
okay.. nu fik jeg endelig genskabt din fejl... lader til at v;re en fejl i selve objectet der g'r at man ikke kan bruge dens Name/property
Avatar billede mcbyte Nybegynder
16. februar 2005 - 15:03 #28
Ja - så langt er jeg også. Undskyld hvis jeg ikke har udtrykt mig klart nok, men det er det jeg har prøvet at komme frem til hele tiden :) Men en løsning - findes den?
Avatar billede burningice Nybegynder
16. februar 2005 - 15:08 #29
private void button1_Click(object sender, System.EventArgs e)
        {
            System.Drawing.Text.PrivateFontCollection pfc = new System.Drawing.Text.PrivateFontCollection();
            pfc.AddFontFile("FREE3OF9.TTF");

            FontFamily fam = new FontFamily("Free 3 of 9", pfc);
            Font f = new Font(fam, 10F);
            this.listBox1.Items.Add(f);
        }

femte gang jeg udfører ovenstående kode får jeg en fejl på fn-objectet
Avatar billede mcbyte Nybegynder
16. februar 2005 - 15:10 #30
Jah, hvilket ikke giver nogen mening umiddelbart?
Avatar billede burningice Nybegynder
16. februar 2005 - 15:15 #31
det er okay... det er jo svært at snakke om et problem når kun den ene oplever det :)

jeg har nu kommet så langt, at det ikke er antallet af font-objecter det kommer an på, men antallet af gange OnDrawItem-funktionen bliver kørt. Ved godt det lyder åndsvagt, men hvis jeg f.eks. kun tilføjer et font-object, og hiver rundt med formen et par gange, så den skal gentegne kommer fejlen også
Avatar billede mcbyte Nybegynder
16. februar 2005 - 15:21 #32
Well, det er også min oplevelse - det pudsige er blot at hvis det er system fonte, så virker det perfekt....
Avatar billede burningice Nybegynder
16. februar 2005 - 15:22 #33
det her giver jo ingen mening overhovedet

protected override void OnDrawItem(DrawItemEventArgs e)
        {
            if (_AntiAlias)
            {
                e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
                e.Graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
            }

            if (Site != null) return;

            if (e.Index > -1)
            {
                Font fn = (Font)Items[e.Index];
                string name;

                try
                {
                    name = fn.Name;
                }
                catch
                {
                    name = "Error";
                }

                if (fn != null)
                {
                    RectangleF rec = new RectangleF(e.Bounds.Left, e.Bounds.Top + verdana.GetHeight(), e.Bounds.Width, e.Bounds.Height);

                    if ((e.State & DrawItemState.Focus) == 0)
                    {
                        e.Graphics.FillRectangle(new SolidBrush(BackColor), e.Bounds);
                        e.Graphics.DrawString(name, verdana, new SolidBrush(this.ForeColor), e.Bounds);
                        e.Graphics.DrawString(_DrawString, fn, new SolidBrush(this.ForeColor), rec);
                        e.Graphics.DrawRectangle(new Pen(SystemColors.Highlight), e.Bounds); //Border
                    }
                    else
                    {
                        e.Graphics.FillRectangle(new SolidBrush(SystemColors.Highlight), e.Bounds);
                        e.Graphics.DrawString(name, verdana, new SolidBrush(this.ForeColor), e.Bounds);
                        e.Graphics.DrawString(_DrawString, fn, new SolidBrush(this.ForeColor), rec);
                    }
                }
            }
        }

der kommer til at stå Error på de mest underlige steder, og nogle gange ændrer placeringen af Error-navnene sig også
Avatar billede mcbyte Nybegynder
16. februar 2005 - 15:44 #34
Jah, det er ikke rigtigt til at debugge efter min mening... har prøvet så mange forskellige ting de sidste par dage....
Avatar billede burningice Nybegynder
16. februar 2005 - 15:52 #35
har du prøvet at oprette en tråd inde på microsoft's usenet om en possible bug ved brug af Font's der ikke er installerede på systemet
Avatar billede mcbyte Nybegynder
16. februar 2005 - 15:54 #36
Jeg har smidt en tråd i microsoft.public.dotnet.framework.windowsforms men intet svar fået.
Avatar billede burningice Nybegynder
16. februar 2005 - 15:58 #37
ved brug af dette i OnResize får jeg rent faktisk i i Items.Add-funktionen

Object[] items = new object[Items.Count];
Items.CopyTo(items, 0);
Items.Clear();

foreach (Object item in items) Items.Add(item);

Så du har ret i at Font-objectet bliver ødelagt på en måde.

Jeg vil skyde på at der er bug i noget Interop, evt. mister .Net sin handle til det native Font-object
Avatar billede mcbyte Nybegynder
16. februar 2005 - 16:04 #38
Jah, det lyder meget sandsynligt (har btw postet en ny post i nyhedsgruppen).

Jeg har oplevet at få fejl i alle metoderne, OnResize, OnMeasureItem og OnDrawItem - så ja, det må være .Net frameworket der fejler...
Avatar billede burningice Nybegynder
16. februar 2005 - 16:05 #39
det er fordi at du bruger dit Font-object i alle de metoder
Avatar billede mcbyte Nybegynder
16. februar 2005 - 16:08 #40
Jo, det er jeg klar over... men rart at få bekræftiget at det ikke bare er mig der er helt på herrens mark her...
Avatar billede burningice Nybegynder
16. februar 2005 - 16:09 #41
prøv at stil spørgsmålet her

microsoft.public.dotnet.framework.drawing
Avatar billede mcbyte Nybegynder
16. februar 2005 - 16:20 #42
Ok, har jeg gjort nu. Håber at der kommer et svar - for er gået godt og grundigt i stå pt.
Avatar billede burningice Nybegynder
16. februar 2005 - 16:25 #43
:) sådan... så fandt jeg ud af det... jo, det er semi-bug ;)

problemet ligger i, at Font-objecter bliver ved med at have en forbindelse til det PrivateFontCollection-object de til at starte med blev loaded ind i. Det betyder at når GC'en kan se at når dit pfc-object

PrivateFontCollection pfc = new PrivateFontCollection();

går ud af scope og bliver fjernet fra rammen, bliver alle dine font-objecter også nedlagt. Rimelig crappy, og løsningen på det er at sørge for dit pfc-object ikke går ud af scope, evt. ved at oprette den som field i den form Listboxen skal være i.
Avatar billede mcbyte Nybegynder
16. februar 2005 - 16:33 #44
Helt officielt så elsker jeg dig af hele mit hjerte nu! ;)

Smid et svar så du kan få dine velfortjente point :)

Meget pudsigt - har tænkt på at det kunne være noget i den retning, altså at jeg havde et objekt som kom ud af scope, men havde ingen anelse om at fontobjekterne rent praktisk var knyttet til deres FontCollection...

Flot fundet ud af!
Avatar billede burningice Nybegynder
16. februar 2005 - 16:37 #45
du er ikke den første der har bøvlet med det åbenbart ;)

http://www.csharp-station.com/ShowPost.aspx?PostID=1352
Avatar billede mcbyte Nybegynder
16. februar 2005 - 16:38 #46
Tydeligvis ikke :) Men endnu engang tak for hjælpen...
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
IT-kurser om Microsoft 365, sikkerhed, personlig vækst, udvikling, digital markedsføring, grafisk design, SAP og forretningsanalyse.

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester