2010-05-11 17 views
7

मेरे पास एक ऐप है जो बाइनरी लॉग फ़ाइल से रिकॉर्ड लोड करता है और उन्हें वर्चुअल TListView में प्रदर्शित करता है। फ़ाइल में संभावित रूप से लाखों रिकॉर्ड हैं, और डिस्प्ले को उपयोगकर्ता द्वारा फ़िल्टर किया जा सकता है, इसलिए मैं एक ही समय में मेमोरी में सभी रिकॉर्ड्स लोड नहीं करता हूं, और ListView आइटम इंडेक्स 1-से-1 संबंध नहीं हैं फ़ाइल रिकॉर्ड ऑफ़सेट (उदाहरण के लिए सूची आइटम 1 फ़ाइल रिकॉर्ड 100 हो सकता है)। मैं ListView के OnDataHint ईवेंट का उपयोग केवल सूची दृश्यों के लिए रिकॉर्ड्स लोड करने के लिए करता हूं, क्योंकि सूची वास्तव में दिलचस्पी लेती है। जैसे ही उपयोगकर्ता स्क्रॉल करता है, ऑनडाटाइंट परिवर्तन द्वारा निर्दिष्ट सीमा, मुझे नए रिकॉर्ड में नहीं होने वाले रिकॉर्ड मुक्त करने की अनुमति देता है, और नए रिकॉर्ड आवंटित करता है जैसी जरूरत थी।टीवीर्टुअलस्ट्रिंगट्री - गैर-दृश्य नोड्स और मेमोरी खपत को रीसेट करना

यह ठीक काम करता है, गति सहनशील है, और स्मृति पदचिह्न बहुत कम है।

मैं वर्तमान में TListView के लिए एक स्थानापन्न के रूप TVirtualStringTree का मूल्यांकन कर रहा हूँ, मुख्य रूप से, क्योंकि मैं विस्तृत करने/संक्षिप्त रिकॉर्ड कि कई पंक्तियों अवधि क्षमता जोड़ना चाहते हैं (मैं इसे TListView साथ incrementing/decrementing आइटम गतिशील गिनती द्वारा हेराफेरी कर सकते हैं, लेकिन यह वास्तविक पेड़ का उपयोग करने के रूप में सीधे आगे नहीं है)।

अधिकांश भाग के लिए, मैं TListView तर्क को बंद करने में सक्षम हूं और मुझे जितनी भी आवश्यकता है, सबकुछ काम कर रहा है। मैंने देखा कि टीवीर्टुअलस्टिंगट्री का वर्चुअल प्रतिमान काफी अलग है, हालांकि। इसमें एक ही प्रकार की ऑनडाटाहिंट कार्यक्षमता नहीं है जो TListView करता है (मैं ऑनस्क्रॉल ईवेंट का उपयोग नकली में कर सकता हूं, जो मेरी मेमोरी बफर लॉजिक को काम जारी रखने की अनुमति देता है), और मैं आवंटित रिकॉर्ड्स के साथ नोड्स को जोड़ने के लिए ऑनइंस्टॉलिज़ नोड इवेंट का उपयोग कर सकता हूं ।

हालांकि, एक बार पेड़ नोड शुरू होने के बाद, यह देखता है कि यह पेड़ के जीवनकाल के लिए शुरू हुआ है। यह मेरे लिए अच्छा नहीं है। चूंकि उपयोगकर्ता चारों ओर स्क्रॉल करता है और मैं स्मृति से रिकॉर्ड हटा देता हूं, मुझे उन गैर-दृश्य नोड्स को पेड़ से पूरी तरह से हटाए बिना, या उनके विस्तार/पतन राज्यों को खोए बिना रीसेट करने की आवश्यकता होती है। जब उपयोगकर्ता उन्हें वापस देखता है, तो मैं रिकॉर्ड्स को फिर से आवंटित कर सकता हूं और नोड्स को फिर से शुरू कर सकता हूं। असल में, मैं जितना संभव हो सके TListView की तरह टीवीर्टुअलस्ट्रिंग ट्री कार्य करना चाहता हूं, जहां तक ​​इसका वर्चुअलाइजेशन संबंधित है।

मैंने देखा है कि टीवीर्टुअल स्टिंगट्री के पास रीसेट नोड() विधि है, लेकिन जब भी मैं इसका उपयोग करने की कोशिश करता हूं तो मुझे कई त्रुटियों का सामना करना पड़ता है। मुझे इसका गलत इस्तेमाल करना होगा। मैंने अपने रिकॉर्ड बफर में प्रत्येक नोड के अंदर डेटा पॉइंटर को संग्रहीत करने का भी सोचा, और मैं आवंटित और मुक्त मेमोरी, तदनुसार उन पॉइंटर्स को अपडेट करें। अंत प्रभाव इतना अच्छा काम नहीं करता है, या तो।

इससे भी बदतर, मेरी सबसे बड़ी टेस्ट लॉग फ़ाइल में ~ 5 मिलियन रिकॉर्ड हैं। यदि मैं एक बार में कई नोड्स के साथ टीवीर्टुअल स्टिंगट्री शुरू करता हूं (जब लॉग डिस्प्ले unfiltered है), इसके नोड्स के लिए पेड़ का आंतरिक ओवरहेड 260 एमबी मेमोरी लेता है (बिना किसी रिकॉर्ड के आवंटित किए गए)। जबकि TListView के साथ, उसी लॉग फ़ाइल को लोड करना और इसके पीछे सभी मेमोरी तर्क, मैं केवल कुछ एमबी का उपयोग कर दूर हो सकता हूं।

कोई विचार?

उत्तर

1

आपको शायद वीएसटी पर स्विच नहीं करना चाहिए जब तक कि आपके पास वीएसटी की कम से कम कुछ विशेषताओं का उपयोग न हो, जो एक मानक सूची बॉक्स/सूचीदृश्य नहीं है। लेकिन वस्तुओं की एक फ्लैट सूची की तुलना में एक बड़ी मेमोरी ओवरहेड है।

मुझे TVirtualStringTree का उपयोग करने में केवल वास्तविक लाभ दिखाई नहीं देता है, जो कि कई पंक्तियों का विस्तार करने वाली वस्तुओं को विस्तार और संक्षिप्त करने में सक्षम होने के लिए है। आप

बारे में मुख्य रूप से, क्योंकि मैं विस्तृत करने/संक्षिप्त रिकॉर्ड कि कई पंक्तियों अवधि (मैं incrementing/decrementing आइटम गतिशील गिनती द्वारा TListView साथ इसे ठगना कर सकते हैं क्षमता जोड़ना चाहते हैं, लेकिन इस के रूप में आगे के रूप में सीधे नहीं है एक असली पेड़ का उपयोग कर)।

लेकिन आप आइटम गिनती को बदले बिना आसानी से कार्यान्वित कर सकते हैं। यदि आप सूची बॉक्स के Style को lbOwnerDrawVariable पर सेट करते हैं और OnMeasureItem ईवेंट को कार्यान्वित करते हैं तो आप केवल पहली या सभी पंक्तियों को आकर्षित करने के लिए आवश्यक ऊंचाई समायोजित कर सकते हैं। एक्सपेंडर त्रिकोण या पेड़ दृश्य के छोटे प्लस प्रतीक को मैन्युअल रूप से बनाना आसान होना चाहिए। विंडोज एपीआई फ़ंक्शन DrawText() या DrawTextEx() का उपयोग (वैकल्पिक रूप से शब्द-लपेटा हुआ) टेक्स्ट मापने और खींचने के लिए किया जा सकता है।

संपादित करें:

क्षमा करें, मैं पूरी तरह से तथ्य यह है कि आप सूचीदृश्य उपयोग कर रहे हैं अभी, नहीं एक लिस्टबॉक्स याद किया। वास्तव में, सूची सूची में विभिन्न ऊंचाइयों के साथ पंक्तियों का कोई तरीका नहीं है, इसलिए इसका कोई विकल्प नहीं है। आप अभी भी शीर्ष पर एक मानक शीर्षलेख नियंत्रण के साथ एक सूची बॉक्स का उपयोग कर सकते हैं, लेकिन यह अब आप सब कुछ का समर्थन नहीं कर सकते जो आप अब सूचीदृश्य कार्यक्षमता से उपयोग कर रहे हैं, और यह गतिशील रूप से दिखाए जाने और सूचीदृश्य पंक्तियों को छिपाने से सही होने के लिए जितना अधिक काम हो सकता है ढहने और विस्तार का अनुकरण करें।

+0

मैं वर्तमान में एक टीएलिस्टबॉक्स में बनाम रिपोर्ट मोड में एक TListView का उपयोग करता हूं। TListView vsReport में चर-ऊंचाई आइटम का समर्थन नहीं करता है। –

+0

मैं इसे अभी उत्तर के रूप में चिह्नित कर रहा हूं, लेकिन केवल इसलिए कि यह मुझे VST पर स्विच न करने के लिए कहता है। –

0

आपको रीसेट नोड का उपयोग नहीं करना चाहिए क्योंकि यह विधि अमान्यनेट को आमंत्रित करती है और नोड को फिर से शुरू करती है, जिससे अपेक्षाकृत विपरीत प्रभाव पड़ता है। मुझे नहीं पता कि Vode को वास्तव में नोड को हटाए बिना NodeDataSize में निर्दिष्ट स्मृति आकार को मुक्त करना संभव है या नहीं। लेकिन क्यों नोडडेटा आकार को सूचक के आकार (Delphi, VirtualStringTree - classes (objects) instead of records) पर सेट न करें और डेटा को स्वयं प्रबंधित करें? एक विचार है...

+0

यही वह है जो मैं अभी करता हूं। असल में, मैं वर्तमान में NodeDataSize = 0 का उपयोग कर रहा हूं, लेकिन ऐप अभी भी 200+ एमबी मेमोरी का उपयोग करता है। ऐसा इसलिए है क्योंकि टीवीर्टुअल नोड का न्यूनतम आकार 44 बाइट्स है, इसलिए 5 मिलियन नोड्स (44 x 5000000) = 220000000 = 214843.75 Kb = 209.81 एमबी से गुणा किया गया है, और यह केवल टीवीर्टुअल नोड मेमोरी के लिए है, जिसमें ओवरहेड शामिल नहीं है वीसीएल मेमोरी मैनेजर उस के शीर्ष पर उपयोग करता है। –

+0

मुझे लगता है कि वीएसटी में यह संभव है कि बाल नोड्स उनके माता-पिता नोड की तरह दिखते हैं, इसलिए यह उपयोगकर्ता के लिए अतिसंवेदनशील है कि वे बच्चे नोड्स हैं। आप पांच हजार शीर्ष-स्तरीय नोड्स जोड़ सकते हैं जिनमें प्रत्येक के पास एक हजार बच्चे नोड्स होते हैं। –

1

अगर मैं इसे सही ढंग से समझते हैं, TVirtualStringTree की स्मृति आवश्यकता होना चाहिए:

nodecount * (sizeof (TVirtualNode) + YourNodeDataSize + DWORD पंक्ति-गद्दी)

स्मृति पदचिह्न को कम करने के लिए, आप कर सकते थे शायद नोड्स को केवल पॉइंटर्स के साथ मेमोरी-मैप किए गए फ़ाइल में ऑफ़सेट करने के लिए प्रारंभ करें। नोड्स को रीसेट करना जो पहले ही शुरू हो चुका है, इस मामले में आवश्यक नहीं लगता है - मेमोरी पदचिह्न नोडकाउंट * (44 + 4 + 0) होना चाहिए - 5 मिलियन रिकॉर्ड के लिए, लगभग 230 एमबी।

आईएमएचओ आप पेड़ के साथ कोई बेहतर नहीं हो सकते हैं लेकिन मेमोरी-मैप की गई फ़ाइल का उपयोग करने से आप फ़ाइल से डेटा को सीधे और अधिक मेमोरी आवंटित किए बिना डेटा को पढ़ने और डेटा को कॉपी करने की अनुमति देंगे।

आप डेटा प्रस्तुत करने के लिए एक फ्लैट दृश्य के बजाय पेड़ संरचना का उपयोग करने पर भी विचार कर सकते हैं। इस तरह आप मांग पर माता-पिता नोड के बच्चे नोड्स को प्रारंभ कर सकते हैं (जब अभिभावक नोड का विस्तार किया जाता है) और जब यह ध्वस्त हो जाता है तो पैरेंट नोड को रीसेट कर रहा है (इसलिए अपने सभी बच्चे नोड्स को मुक्त करना)। दूसरे शब्दों में, एक ही स्तर पर बहुत सारे नोड्स न रखने का प्रयास करें।

+0

असल में, मैं पहले से ही एक मेमोरी मैप की गई फ़ाइल का उपयोग करता हूं, लेकिन फ़ाइल के संभावित आकार (1+ जीबी तक हो सकता है) के कारण, और जिस तरह से डिस्प्ले फ़िल्टरिंग लागू की जाती है, मैं पूरी फाइल को स्मृति में मैप नहीं करता एक बार में। कोड में वृक्ष संरचना को कार्यान्वित करने के लिए, इससे बहुत मदद नहीं मिलेगी क्योंकि अधिकांश लॉग फ़ाइल आइटम में बच्चे नोड्स नहीं होंगे। यही कारण है कि वर्तमान में एक सूची दृश्य का उपयोग प्रदर्शन के लिए किया जा रहा है। सहायक बच्चे नोड्स एक सुविधा को समायोजित करना है जहां लॉग फ़ाइल में बहु-पंक्ति लॉग प्रविष्टियां संग्रहीत की जाती हैं, अलग-अलग रिकॉर्ड के रूप में जो मैं आसान प्रबंधन के लिए मेमोरी में एक साथ विलय कर दूंगा –

0

"DeleteChildren" को आज़माएं। यहां बताया गया है इस प्रक्रिया की टिप्पणी का कहना है:

// Removes all children and their children from memory without changing the vsHasChildren style by default. 

कभी इसका इस्तेमाल किया, लेकिन जैसा कि मैंने इसे पढ़ा, तो आप उपयोग कर सकते हैं कि OnCollapsed घटना में स्मृति नोड्स कि सिर्फ अदृश्य हो गया के लिए आवंटित मुक्त करने के लिए। और फिर ऑनएक्सपैडिंग में उन नोड्स को फिर से उत्पन्न करें ताकि उपयोगकर्ता कभी नहीं जानता कि नोड स्मृति से दूर चला गया है।

लेकिन मुझे यकीन नहीं है, मुझे इस तरह के व्यवहार की आवश्यकता नहीं थी।

+0

DeleteChildren() मेरे पास चिंताओं का समाधान नहीं करता है। अधिकांश नोड्स में कोई भी बच्चा नोड्स शुरू नहीं होता है। मैं उन लोगों के लिए समर्थन जोड़ने की उम्मीद कर रहा था जो चाहते थे। स्क्रॉल करने के दौरान मुझे अदृश्य बनने वाले शीर्ष-स्तरीय नोड्स के लिए स्मृति मुक्त करने की आवश्यकता है। –

+0

टीवीर्टुअल ट्री में सभी दृश्यमान नोड्स को एक टीवीर्टुअल नोड की आवश्यकता होती है क्योंकि नोड्स से संबंधित सामान प्राप्त करने के लिए उपयोग की जाने वाली सभी घटनाएं (उदा: सेलटेक्स्ट) एक PVirtualNode को नोड की पहचान करने का एकमात्र तरीका मानती हैं। यदि यह आपके लिए एक समस्या है तो आप TVTNodeMemoryManager (और हो सकता है कि इसे मेमोरी मैप किए गए फ़ाइल द्वारा समर्थित किया जाए) को देखना चाहें या मागी द्वारा सुझाए गए अनुसार अपने प्रारंभिक ListView समाधान पर वापस जाएं। और आपकी विशेष जरूरतों पर विचार करते हुए हमेशा "100% कस्टम नियंत्रण" समाधान होता है! विकल्पों को हैक करने के बाद अपनी खुद की शक्ति तेज हो सकती है। –

+0

हालांकि कस्टम नियंत्रण अच्छा होगा, मेरे पास एक विकसित करने के लिए उस तरह का अतिरिक्त समय नहीं है। मैं TVTNodeMemoryManager को देखूंगा, लेकिन मुझे नहीं लगता कि इससे बहुत मदद मिलेगी क्योंकि पेड़ के काम करने के लिए सभी भौतिक टीवीर्टुअल नोड आइटम को आवंटित करना होगा। –

1

अपनी आवश्यकता को पूरा करने के लिए "एकाधिक लाइनों को फैलाए गए रिकॉर्ड को विस्तार/संक्षिप्त करने के लिए", मैं बस एक ड्रॉग्रिड का उपयोग करता हूं। इसे जांचने के लिए, एक फॉर्म पर एक ड्रॉग्रिड खींचें, फिर निम्नलिखित डेल्फी 6 कोड में प्लग करें। आप अनिवार्य रूप से कोई ओवरहेड के साथ 5,000,000 मल्टीलाइन रिकॉर्ड (या जो भी मात्रा आप चाहते हैं) को संकुचित और विस्तारित कर सकते हैं। यह एक साधारण तकनीक है, बहुत अधिक कोड की आवश्यकता नहीं है, और आश्चर्यजनक रूप से अच्छी तरह से काम करता है।


unit Unit1; 

interface 

uses 
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, Grids, StdCtrls; 

type 
    TForm1 = class(TForm) 
    DrawGrid1: TDrawGrid; 
    procedure DrawGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); 
    procedure DrawGrid1SelectCell(Sender: TObject; ACol, ARow: Integer; var CanSelect: Boolean); 
    procedure DrawGrid1TopLeftChanged(Sender: TObject); 
    procedure DrawGrid1DblClick(Sender: TObject); 
    procedure FormCreate(Sender: TObject); 
    private 
    procedure AdjustGrid; 
    end; 

var 
    Form1: TForm1; 

implementation 

{$R *.dfm} 

// Display a large number of multi-line records that can be expanded or collapsed, using minimal overhead. 
// LinesInThisRecord() and RecordContents() are faked; change them to return actual data. 

const TOTALRECORDS = 5000000; // arbitrary; a production implementation would probably determine this at run time 

// keep track of whether each record is expanded or collapsed 
var isExpanded: packed array[1..TOTALRECORDS] of boolean; // initially all FALSE 

function LinesInThisRecord(const RecNum: integer): integer; 
begin // how many lines (rows) does the record need to display when expanded? 
result := (RecNum mod 10) + 1; // make something up, so we don't have to use real data just for this demo 
end; 

function LinesDisplayedForRecord(const RecNum: integer): integer; 
begin // how many lines (rows) of info are we currently displaying for the given record? 
if isExpanded[RecNum] then result := LinesInThisRecord(RecNum) // all lines show when expanded 
else result := 1; // show only 1 row when collapsed 
end; 

procedure GridRowToRecordAndLine(const RowNum: integer; var RecNum, LineNum: integer); 
var LinesAbove: integer; 
begin // for a given row number in the drawgrid, return the record and line numbers that appear in that row 
RecNum := Form1.DrawGrid1.TopRow; // for simplicity, TopRow always displays the record with that same number 
if RecNum > TOTALRECORDS then RecNum := 0; // avoid overflow 
LinesAbove := 0; 
while (RecNum > 0) and ((LinesDisplayedForRecord(RecNum) + LinesAbove) < (RowNum - Form1.DrawGrid1.TopRow + 1)) do 
    begin // accumulate the tally of lines in expanded or collapsed records until we reach the row of interest 
    inc(LinesAbove, LinesDisplayedForRecord(RecNum)); 
    inc(RecNum); if RecNum > TOTALRECORDS then RecNum := 0; // avoid overflow 
    end; 
LineNum := RowNum - Form1.DrawGrid1.TopRow + 1 - LinesAbove; 
end; 

function RecordContents(const RowNum: integer): string; 
var RecNum, LineNum: integer; 
begin // display the data that goes in the grid row. for now, fake it 
GridRowToRecordAndLine(RowNum, RecNum, LineNum); // convert row number to record and line numbers 
if RecNum = 0 then result := '' // out of range 
else 
    begin 
    result := 'Record ' + IntToStr(RecNum); 
    if isExpanded[RecNum] then // show line counts too 
    result := result + ' line ' + IntToStr(LineNum) + ' of ' + IntToStr(LinesInThisRecord(RecNum)); 
    end; 
end; 

procedure TForm1.AdjustGrid; 
begin // don't allow scrolling past last record 
if DrawGrid1.TopRow > TOTALRECORDS then DrawGrid1.TopRow := TOTALRECORDS; 
if RecordContents(DrawGrid1.Selection.Top) = '' then // move selection back on to a valid cell 
    DrawGrid1.Selection := TGridRect(Rect(0, TOTALRECORDS, 0, TOTALRECORDS)); 
DrawGrid1.Refresh; 
end; 

procedure TForm1.DrawGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); 
var s: string; 
begin // time to draw one of the grid cells 
if ARow = 0 then s := 'Data' // we're in the top row, get the heading for the column 
else s := RecordContents(ARow); // painting a record, get the data for this cell from the appropriate record 
// draw the data in the cell 
ExtTextOut(DrawGrid1.Canvas.Handle, Rect.Left, Rect.Top, ETO_CLIPPED or ETO_OPAQUE, @Rect, pchar(s), length(s), nil); 
end; 

procedure TForm1.DrawGrid1SelectCell(Sender: TObject; ACol, ARow: Integer; var CanSelect: Boolean); 
var RecNum, ignore: integer; 
begin 
GridRowToRecordAndLine(ARow, RecNum, ignore); // convert selected row number to record number 
CanSelect := RecNum <> 0; // don't select unoccupied rows 
end; 

procedure TForm1.DrawGrid1TopLeftChanged(Sender: TObject); 
begin 
AdjustGrid; // keep last page looking good 
end; 

procedure TForm1.DrawGrid1DblClick(Sender: TObject); 
var RecNum, ignore, delta: integer; 
begin // expand or collapse the currently selected record 
GridRowToRecordAndLine(DrawGrid1.Selection.Top, RecNum, ignore); // convert selected row number to record number 
isExpanded[RecNum] := not isExpanded[RecNum]; // mark record as expanded or collapsed; subsequent records might change their position in the grid 
delta := LinesInThisRecord(RecNum) - 1; // amount we grew or shrank (-1 since record already occupied 1 line) 
if isExpanded[RecNum] then // just grew 
else delta := -delta; // just shrank 
DrawGrid1.RowCount := DrawGrid1.RowCount + delta; // keep rowcount in sync 
AdjustGrid; // keep last page looking good 
end; 

procedure TForm1.FormCreate(Sender: TObject); 
begin 
Caption := FormatFloat('#,##0 records', TOTALRECORDS); 
DrawGrid1.RowCount := TOTALRECORDS + 1; // +1 for column heading 
DrawGrid1.ColCount := 1; 
DrawGrid1.DefaultColWidth := 300; // arbitrary 
DrawGrid1.DefaultRowHeight := 12; // arbitrary 
DrawGrid1.Options := DrawGrid1.Options - [goVertLine, goHorzLine, goRangeSelect] + [goDrawFocusSelected, goThumbTracking]; // change some defaults 
end; 

end. 

+0

आप ग्रिड के रोकाउंट को समायोजित कर रहे हैं कि कितनी पंक्तियों को जोड़ने/निकालने की आवश्यकता है। मैंने अपने मूल संदेश में कहा कि मुझे पहले से ही उस तकनीक के बारे में पता था: "मैं इसे गतिशील रूप से आइटम गिनती बढ़ाने/घटाने के द्वारा TListView के साथ झुका सकता हूं"। यही वह मार्ग है जिसे मुझे लेना है। –

+0

@ रेमी: लेकिन आपने यह भी कहा कि "सीधे आगे नहीं" था। एक ड्रॉग्रिड के साथ, विस्तार/पतन जितना आसान हो सके उतना आसान है, साथ ही आपके पास चिंता करने के लिए कभी भी लोडिंग/आवंटन/मुक्त/नोड/माता-पिता/बच्चे/आदि नहीं हैं। यदि सूचीदृश्य काम करता है लेकिन गन्दा है, और ड्रॉग्रिड काम करता है और सरल है, तो ड्रॉग्रिड के साथ जाएं। नमूना कोड आज़माएं सुनिश्चित करें। यदि आप सूचीदृश्य में समान कार्यक्षमता के लिए आवश्यक कोड पर साइड-बाय-साइड की तुलना करते हैं, तो मुझे लगता है कि अधिकांश कोडर ड्रग्रिड समाधान को पालन/रखरखाव/संशोधित/आदि के लिए आसान पाते हैं। –

+0

दुर्भाग्यवश, मेरे डेटा लोडिंग और मेमोरी प्रबंधन की गतिशील प्रकृति के कारण, यह एक आसान समाधान नहीं है कि मैं एक सूची दृश्य या ड्राग्रिड का उपयोग करता हूं (एक कस्टम नियंत्रण होने के नाते, एक ड्रॉग्रिड में अतिरिक्त आंतरिक ओवरहेड होता है जो मानक सूची दृश्य नहीं करता है)। –

संबंधित मुद्दे