Sıfırdan bir ticaret Uzman Danışmanı geliştirme (Bölüm 09): Kavramsal sıçrama (II)

Daniel Jose | 11 Ekim, 2022

Giriş

Önceki makalede, şablonların kayan pencerede kullanılmasına olanak sağlayan temel bir sistem oluşturmuştuk. Kodda birçok değişiklik yapmış olsak da, tam olarak bitirmedik. Bunu, açıklamayı basit tutmak ve anlayışı artırmak amacıyla kasıtlı olarak yaptık, çünkü kayan pencerelerde şablonların kullanımı oldukça basitken, nesnelerin kullanımı çok daha karmaşıktır. Bu nedenle, şimdi tamamen yeni bir işe hazır olun.

Ticaret arayüzünü kayan pencerede oluşturmak için kullandığımız nesnelerin kullanımındaki en büyük zorluk, aslında MetaTrader 5'in gerçek anlamda bu amaca yönelik tasarlanmış olmamasıdır. Bazı geliştiriciler, ticaret arayüzü penceresini oluşturmak için standart kütüphaneyi kullanabileceğimizi söyleyebilir. Ancak ben işleri karmaşıklaştırmayı seviyorum, birkaç makale önce de anlattığımız gibi herkesin kendi arayüzünü oluşturabilmesini sağlamak istiyorum. Tabi o makalede her şey basitti. Şimdi ise MetaTrader 5'teki kısıtlamaları anlamamız ve onları aşmamız gerekiyor.


Planlama

Temel bilgilerle başlayalım. Aşağıdaki kod beklediğimiz gibi davranacaktır:

#property copyright "Daniel Jose"
#property indicator_chart_window
#property indicator_plots 0
//+------------------------------------------------------------------+
int OnInit()
{
        long id = ChartID();
        string sz0 = (string)ObjectsTotal(id, -1, -1) + (string)MathRand();
        ObjectCreate(id, sz0, OBJ_CHART, 0, 0, 0);
        ObjectSetInteger(id, sz0, OBJPROP_XDISTANCE, 10);
        ObjectSetInteger(id, sz0, OBJPROP_YDISTANCE, 10);
        ObjectSetInteger(id, sz0, OBJPROP_XSIZE, 300);
        ObjectSetInteger(id, sz0, OBJPROP_YSIZE, 300);
        ObjectSetInteger(id, sz0, OBJPROP_PRICE_SCALE, false);
        ObjectSetInteger(id, sz0, OBJPROP_DATE_SCALE, false);
        
  
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
{
        return rates_total;
}
//+------------------------------------------------------------------+


Etkisi tam olarak beklendiği gibi olduğu için burada karmaşık bir şey yok. MQL5 biraz daha ileri gitmemize izin veriyor. Ancak sistemin başlangıçta tasarlandığının ötesinde bir şey yapmaya çalıştığımızda zorluklar ortaya çıkabilir. Yukarıdaki kodu aşağıdakine dönüştürürsek işler ilginçleşiyor.

int OnInit()
{
        long id = ChartID(), handle;
        string sz0 = (string)ObjectsTotal(id, -1, -1) + (string)MathRand(), sz1 = (string)MathRand();
        
        ObjectCreate(id, sz0, OBJ_CHART, 0, 0, 0);
        ObjectSetInteger(id, sz0, OBJPROP_XDISTANCE, 10);
        ObjectSetInteger(id, sz0, OBJPROP_YDISTANCE, 10);
        ObjectSetInteger(id, sz0, OBJPROP_XSIZE, 300);
        ObjectSetInteger(id, sz0, OBJPROP_YSIZE, 300);
        ObjectSetInteger(id, sz0, OBJPROP_PRICE_SCALE, false);
        ObjectSetInteger(id, sz0, OBJPROP_DATE_SCALE, false);
        
        handle = ObjectGetInteger(id, sz0, OBJPROP_CHART_ID);
        ObjectCreate(handle, sz1, OBJ_CHART, 0, 0, 0);
        ObjectSetInteger(handle, sz1, OBJPROP_XDISTANCE, 50);
        ObjectSetInteger(handle, sz1, OBJPROP_YDISTANCE, 50);
        ObjectSetInteger(handle, sz1, OBJPROP_XSIZE, 300);
        ObjectSetInteger(handle, sz1, OBJPROP_YSIZE, 300);
        ObjectSetInteger(handle, sz1, OBJPROP_PRICE_SCALE, false);
        ObjectSetInteger(handle, sz1, OBJPROP_DATE_SCALE, false);
        ChartRedraw(handle);    
  
        return INIT_SUCCEEDED;
}

Vurgulanan satırları koda ekledik. Grafik üzerinde çalıştırırsak, sonuç şu şekilde olacaktır:


Ne yaptık? Grafik üzerine grafik yerleştirdik. Buraya herhangi bir nesneyi de yerleştirebiliriz. MQL5 bunu da yapmamıza izin veriyor ancak bunun hem bir avantajı hem de bir dezavantajı vardır. Bunu yapmanın avantajını görmek adına koda bir değişiklik daha ekleyelim.

int OnInit()
{
        long id = ChartID(), handle;
        string sz0 = (string)ObjectsTotal(id, -1, -1) + (string)MathRand(), sz1 = (string)MathRand();
        
        ObjectCreate(id, sz0, OBJ_CHART, 0, 0, 0);
        ObjectSetInteger(id, sz0, OBJPROP_XDISTANCE, 10);
        ObjectSetInteger(id, sz0, OBJPROP_YDISTANCE, 10);
        ObjectSetInteger(id, sz0, OBJPROP_XSIZE, 300);
        ObjectSetInteger(id, sz0, OBJPROP_YSIZE, 300);
        ObjectSetInteger(id, sz0, OBJPROP_PRICE_SCALE, false);
        ObjectSetInteger(id, sz0, OBJPROP_DATE_SCALE, false);
        ObjectSetInteger(id, sz0, OBJPROP_SELECTABLE, true);
        ObjectSetInteger(id, sz0, OBJPROP_SELECTED, true);
        
        handle = ObjectGetInteger(id, sz0, OBJPROP_CHART_ID);
        ObjectCreate(handle, sz1, OBJ_CHART, 0, 0, 0);
        ObjectSetInteger(handle, sz1, OBJPROP_XDISTANCE, 50);
        ObjectSetInteger(handle, sz1, OBJPROP_YDISTANCE, 50);
        ObjectSetInteger(handle, sz1, OBJPROP_XSIZE, 300);
        ObjectSetInteger(handle, sz1, OBJPROP_YSIZE, 300);
        ObjectSetInteger(handle, sz1, OBJPROP_PRICE_SCALE, false);
        ObjectSetInteger(handle, sz1, OBJPROP_DATE_SCALE, false);
        ChartRedraw(handle);    
  
        return INIT_SUCCEEDED;
}

Vurgulanan satırları ekleyerek aşağıdaki sonucu elde ederiz:


Bu, nesnenin içerisindeki her şeyin nesnenin içerisinde kalacağı anlamına gelir, ki bu da kontrol mantığını büyük ölçüde basitleştirdiği için kayan pencereler kullanırken çok önemlidir. Ancak tam anlamıyla her şey mükemmel durumda değildir: MetaTrader 5 başlangıçta buna yönelik tasarlanmamıştı. Dolayısıyla, bir nesne diğerinin içerisinde bulunduğunda bir sorun ortaya çıkmaktadır - olayları dahili nesnelere gönderemiyoruz. Bunu anlamak adına koda birkaç değişiklik daha uygulayalım. Şimdi, son kod aşağıdaki gibi olacaktır:

#property copyright "Daniel Jose"
#property indicator_chart_window
#property indicator_plots 0
//+------------------------------------------------------------------+
int OnInit()
{
        long id = ChartID(), handle;
        string sz0 = (string)ObjectsTotal(id, -1, -1) + (string)MathRand(), sz1 = (string)MathRand();
        
        ObjectCreate(id, sz0, OBJ_CHART, 0, 0, 0);
        ObjectSetInteger(id, sz0, OBJPROP_XDISTANCE, 10);
        ObjectSetInteger(id, sz0, OBJPROP_YDISTANCE, 10);
        ObjectSetInteger(id, sz0, OBJPROP_XSIZE, 300);
        ObjectSetInteger(id, sz0, OBJPROP_YSIZE, 300);
        ObjectSetInteger(id, sz0, OBJPROP_PRICE_SCALE, false);
        ObjectSetInteger(id, sz0, OBJPROP_DATE_SCALE, false);
        ObjectSetInteger(id, sz0, OBJPROP_SELECTABLE, true);
        ObjectSetInteger(id, sz0, OBJPROP_SELECTED, true);
        
        handle = ObjectGetInteger(id, sz0, OBJPROP_CHART_ID);
        ObjectCreate(handle, sz1, OBJ_CHART, 0, 0, 0);
        ObjectSetInteger(handle, sz1, OBJPROP_XDISTANCE, 50);
        ObjectSetInteger(handle, sz1, OBJPROP_YDISTANCE, 50);
        ObjectSetInteger(handle, sz1, OBJPROP_XSIZE, 300);
        ObjectSetInteger(handle, sz1, OBJPROP_YSIZE, 300);
        ObjectSetInteger(handle, sz1, OBJPROP_PRICE_SCALE, false);
        ObjectSetInteger(handle, sz1, OBJPROP_DATE_SCALE, false);
        ObjectSetInteger(handle, sz1, OBJPROP_SELECTABLE, true);
        ObjectSetInteger(handle, sz1, OBJPROP_SELECTED, true);
        ChartRedraw(handle);    
  
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
{
        return rates_total;
}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        if (id == CHARTEVENT_OBJECT_CLICK) Print(sparam);
}
//+------------------------------------------------------------------+

Platformda çalıştırdığımızda sonuç şu şekilde görünecektir:


İç nesneye tıkladığımızda, aslında dış nesneye tıklama yapıldığına lütfen dikkat edin, işte işler bu noktada karmaşıklaşıyor. Ancak programcı olmak her zaman sorunları çözme konusunda uzman olmaya çalışmaktır: istediğimiz sonucu elde etmek için sorunlarla başa çıkarak onları çözmemiz gerekir. Elde edeceğimiz bilgileri kullanarak, ticaret arayüzümüzü işlevsel olarak kayan pencerede çalışacak şekilde yapılandıracağız.

Bu planlama sürecinde son bir aşama daha mevcuttur. Bu kısım modern bilgisayarlar için o kadar önemli olmasa da, yine de dikkate alınması gerekmektedir: işleme süresinin optimizasyonu. Buradaki sorun, işlemcinin gerçekleştirmesi gereken işlem sayısıyla ilgilidir. Şu anki kayan pencere sistemi, eylemlerimize yanıt olarak hareket edebilmesi gereken dört nesne içermektedir. Bu doğrultuda, kayan pencereye yerleştirilecek herhangi bir bilgi, kayan pencerenin kendi değişikliklerine tabi olacaktır. Böylece ticaret arayüzünün nesne sayısı artacaktır. Buna karşılık gelen ciddi bir hesaplama maliyeti olmamasına rağmen, kod tatsız hale gelecek ve kötü bir şekilde optimize edilmiş izlenimi verecektir. Basitçe bir kontrol sistemi ekleyerek bu sorunu çözebiliriz. Ancak çok daha zarif bir önerim var. Bizim için daha fazla zaman ve emek tüketecek gibi görünse de aslında sürdürülmesi ve işlenmesi gereken nesne sayısını azaltacaktır.


Uygulama

Kodun yeniden kullanımını desteklemek adına kayan pencere oluşturma sürecini birkaç adıma böleceğiz. İlk olarak, C_ChartFloating nesne sınıfında iki yeni fonksiyon oluşturalım:

//+------------------------------------------------------------------+
bool StageLocal01(string sz0, ENUM_TIMEFRAMES TimeFrame = PERIOD_CURRENT, int Scale = -1)
{
        m_LimitX = (int)ChartGetInteger(Terminal.Get_ID(), CHART_WIDTH_IN_PIXELS);
        m_LimitY = (int)ChartGetInteger(Terminal.Get_ID(), CHART_HEIGHT_IN_PIXELS);
        if (m_MaxCounter >= def_MaxFloating) return false;
        CreateBarTitle();
        CreateCaption(sz0);
        CreateBtnMaxMin();
        CreateRegion(TimeFrame, Scale);
	m_Win[m_MaxCounter].handle = ObjectGetInteger(Terminal.Get_ID(), m_Win[m_MaxCounter].szRegionChart, OBJPROP_CHART_ID);
                                
        return true;
}
//+------------------------------------------------------------------+
void StageLocal02(int x, int y, int w, int h)
{
        y = (y < 0 ? m_MaxCounter * def_SizeBarCaption : y);                            
        m_Win[m_MaxCounter].PosX        = -1;
        m_Win[m_MaxCounter].PosY        = -1;
        m_Win[m_MaxCounter].PosX_Minimized = m_Win[m_MaxCounter].PosX_Maximized = x;
        m_Win[m_MaxCounter].PosY_Minimized = m_Win[m_MaxCounter].PosY_Maximized = y;
        SetDimension(w, h, true, m_MaxCounter);
        SetPosition(x, y, m_MaxCounter);
        ChartRedraw(m_Win[m_MaxCounter].handle);
        m_MaxCounter++;
}
//+------------------------------------------------------------------+

Kayan pencere ekleyen yeni kod aşağıdaki gibi olacaktır:

bool AddIndicator(string sz0, int x = 0, int y = -1, int w = 300, int h = 200, ENUM_TIMEFRAMES TimeFrame = PERIOD_CURRENT, int Scale = -1)
{
	if (!StageLocal01(sz0, TimeFrame, Scale)) return false;
        ChartApplyTemplate(m_Win[m_MaxCounter].handle, sz0 + ".tpl");   
        m_Win[m_MaxCounter].szVLine = (string)ObjectsTotal(Terminal.Get_ID(), -1, -1) + (string)MathRand();
        ObjectCreate(m_Win[m_MaxCounter].handle, m_Win[m_MaxCounter].szVLine, OBJ_VLINE, 0, 0, 0);
        ObjectSetInteger(m_Win[m_MaxCounter].handle, m_Win[m_MaxCounter].szVLine, OBJPROP_COLOR, clrBlack);
        StageLocal02(x, y, w, h);

        return true;
}

Bu, halihazırdaki sistemi etkilemez, ayrıca onu daha iyi kullanmamıza olanak sağlar. Vurgulanan satırlara dikkat edin: şimdi IDE'mizi kullanmak için bir fonksiyon oluşturacağız. İlk hali aşağıda gösterilmektedir:

bool Add_RAD_IDE(string sz0, int x, int y, int w, int h)
{
        if (!StageLocal01(sz0, PERIOD_CURRENT, -1)) return false;
        StageLocal02(x, y, w, h);
        return true;
}

Vurgulanan satırların önceki kodda kullandığımız satırlarla aynı olduğuna dikkat edin. Yani kodu yeniden kullanıyoruz, sadece farklı olan şeyleri ayarlamamız gerekecek. Artık sistemimize IDE'yi yönetecek araçlara sahip olduğumuzu söyleyebiliriz. Bunu C_TemplateChart nesne sınıfını düzenleyerek yapıyoruz. Aşağıdaki kod, fonksiyonda tam olarak neyin değiştirileceğini göstermektedir. Böylece, gerekli tüm destek doğru şekilde çalışacağından, artık IDE için kayan pencere uygulamaya odaklanabiliriz.

void AddTemplate(void)
{

// .... Function code....

        if (h == 0)
        {
                SetBase(m_Params.Param[TEMPLATE], (bIsSymbol ? m_Params.Param[TEMPLATE] : _Symbol), timeframe, i, w);
                if (!ChartApplyTemplate(m_Info[m_Counter - 1].handle, m_Params.Param[TEMPLATE] + ".tpl")) if (bIsSymbol) ChartApplyTemplate(m_Info[m_Counter - 1].handle, "Default.tpl");
        }
        if (m_Params.Param[TEMPLATE] == def_NameTemplateRAD)
        {
                if ((h > 0) && (w > 0)) Add_RAD_IDE(m_Params.Param[TEMPLATE], 0, -1, w, h); else
                {
                        C_Chart_IDE::Create(GetIdSubWinEA());
                        m_Info[m_Counter - 1].szVLine = "";
                }
        }else
        {
                if ((w > 0) && (h > 0)) AddIndicator(m_Params.Param[TEMPLATE], 0, -1, w, h, timeframe, i); else
                {
                        m_Info[m_Counter - 1].szVLine = (string)ObjectsTotal(Terminal.Get_ID(), -1, -1) + (string)MathRand();
                        ObjectCreate(m_Info[m_Counter - 1].handle, m_Info[m_Counter - 1].szVLine, OBJ_VLINE, 0, 0, 0);
                        ObjectSetInteger(m_Info[m_Counter - 1].handle, m_Info[m_Counter - 1].szVLine, OBJPROP_COLOR, clrBlack);
                }
        }
}

Kodu mümkün olduğunca esnek olacak şekilde yapılandıracağız. Bu, sistemin Frankenstein'a dönüşmesini önleyecektir. Kod üzerinde değişiklikler yaparken şunu daima aklınızda bulundurun - kodu sıfırdan yeniden yazmaya ve aynı şeyleri birçok kez sınamaya gerek yoktur. Her zaman yalnızca bir kez sınama yapmaya çalışın. Böylece, yeni sınama yapmak zorunda kalana kadar önceki sınamaları mümkün olduğunca kullanabilir ve keşfedebilirsiniz. Bu sayede kod zaman içerisinde sürdürülebilir ve genişletilebilir kalarak sistem iyi bir temel üzerinde büyüyecektir.

Sistemi şimdi çalıştırırsak, grafikte bir şey gösterecektir. Ancak biz daha önce tasarladığımız arayüzün görüntülenmesini istiyoruz. Bu nedenle, kodda bir ek değişiklik yapmamız gerekiyor. Kod aşağıdaki gibi olacaktır:

bool Add_RAD_IDE(string sz0, int x, int y, int w, int h)
{
        if (!StageLocal01(sz0, PERIOD_CURRENT, -1)) return false;
        ChartApplyTemplate(m_Win[m_MaxCounter].handle, "\\Files\\Chart Trade\\IDE.tpl");
        StageLocal02(x, y, w, h);
        return true;
}

Çalıştırdığımızda sonuç aşağıdaki gibi görünecektir:


Kodda vurgulanan satırda yüklediğimiz şablondaki nesnelere erişim imkanı olsaydı her şey çok güzel olurdu. Ancak bu mümkün değildir. Burada önemli bir detay bulunmaktadır: daha önce yaptığımız gibi tüm nesneleri oluşturmak yerine sadece manipüle olacak nesneleri oluşturacağız. Bu, pencereyi hareket ettirmemiz gerektiğinde bize çok fazla işleme süresi kazandıracaktır. Ancak hala başka bir sorunumuz daha var, ama önce bu sorununu çözelim ve sistemi işlevsel hale getirelim. Aslında, bu kısım zaten tamamlandı, sadece çalışır hale getirmek amacıyla birkaç ince ayar yapmamız gereklidir.

Sınıflar arasındaki kalıtım dizisinde düzenleme yaparak başlayalım. Bunu yapmak zorundayız çünkü çoklu kalıtıma sahip değiliz, yeni yapı şu şekilde olacaktır:


Ancak bu değişiklik bir endişe kaynağı olmamalıdır: kalıtım dizisindeki değişiklik, kod üzerinde çok fazla değişiklik gerektirmez. Değişiklik yapılan noktalar aşağıda vurgulanmıştır.

bool Add_RAD_IDE(string sz0, int x, int y, int w, int h)
{
	if ((w <= 0) || (h <= 0)) return false;
        if (!StageLocal01(sz0, PERIOD_CURRENT, -1)) return false;
        ChartApplyTemplate(m_Win[m_MaxCounter].handle, "\\Files\\Chart Trade\\IDE.tpl");
        StageLocal02(x, y, w, h);
        return true;
}
void AddTemplate(void)
{
// ..... Código ....
        if (h == 0)
        {
                SetBase(m_Params.Param[TEMPLATE], (bIsSymbol ? m_Params.Param[TEMPLATE] : _Symbol), timeframe, i, w);
                if (!ChartApplyTemplate(m_Info[m_Counter - 1].handle, m_Params.Param[TEMPLATE] + ".tpl")) if (bIsSymbol) ChartApplyTemplate(m_Info[m_Counter - 1].handle, "Default.tpl");
        }
        if (m_Params.Param[TEMPLATE] == def_NameTemplateRAD)
        {
		C_Chart_IDE::Create(Add_RAD_IDE(m_Params.Param[TEMPLATE], 0, -1, w, h));
                m_Info[m_Counter - 1].szVLine = "";
        }else
        {
                if ((w > 0) && (h > 0)) AddIndicator(m_Params.Param[TEMPLATE], 0, -1, w, h, timeframe, i); else
                {
                        m_Info[m_Counter - 1].szVLine = (string)ObjectsTotal(Terminal.Get_ID(), -1, -1) + (string)MathRand();
                        ObjectCreate(m_Info[m_Counter - 1].handle, m_Info[m_Counter - 1].szVLine, OBJ_VLINE, 0, 0, 0);
                        ObjectSetInteger(m_Info[m_Counter - 1].handle, m_Info[m_Counter - 1].szVLine, OBJPROP_COLOR, clrBlack);
                }
        }
}
bool Create(bool bFloat)
{
        m_CountObject = 0;
        if ((m_fp = FileOpen("Chart Trade\\IDE.tpl", FILE_BIN | FILE_READ)) == INVALID_HANDLE) return false;
        FileReadInteger(m_fp, SHORT_VALUE);
                        
        for (m_CountObject = eRESULT; m_CountObject <= eEDIT_STOP; m_CountObject++) m_ArrObject[m_CountObject].szName = "";
	m_SubWindow = ((m_IsFloating = bFloat) ? 0 : GetIdSubWinEA());
        m_szLine = "";
        while (m_szLine != "</chart>")
        {
                if (!FileReadLine()) return false;
                if (m_szLine == "<object>")
                {
                        if (!FileReadLine()) return false;
                        if (m_szLine == "type")
                        {
                                if (m_szValue == "102") if (!LoopCreating(OBJ_LABEL)) return false;
                                if (m_szValue == "103") if (!LoopCreating(OBJ_BUTTON)) return false;
                                if (m_szValue == "106") if (!LoopCreating(OBJ_BITMAP_LABEL)) return false;
                                if (m_szValue == "107") if (!LoopCreating(OBJ_EDIT)) return false;
                                if (m_szValue == "110") if (!LoopCreating(OBJ_RECTANGLE_LABEL)) return false;
                        }
                }
        }
        FileClose(m_fp);
        DispatchMessage(CHARTEVENT_CHART_CHANGE, 0, 0, szMsgIDE[eLABEL_SYMBOL]);
        return true;
}

bool LoopCreating(ENUM_OBJECT type)
{
#define macro_SetInteger(A, B) ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, A, B)
#define macro_SetString(A, B) ObjectSetString(Terminal.Get_ID(), m_ArrObject[c0].szName, A, B)
        int c0;
        bool b0;
        string sz0 = m_szValue;
        while (m_szLine != "</object>") if (!FileReadLine()) return false; else
        {
                if (m_szLine == "name")
                {
                        b0 = false;
                        StringToUpper(m_szValue);
                        for(c0 = eRESULT; (c0 <= eEDIT_STOP) && (!(b0 = (m_szValue == szMsgIDE[c0]))); c0++);
                        if (!b0 && m_IsFloating) return true; else c0 = (b0 ? c0 : m_CountObject);
                        m_ArrObject[c0].szName = StringFormat("%s%04s>%s", def_HeaderMSG, sz0, m_szValue);

//... The rest of the function...

}

Bu yaptıklarım size kafa karıştırıcı görünebilir. Ancak burada karmaşık bir şey yoktur. Kayan pencere kullanmazsak, sistem IDE'mizde yürütülen olayları halihazırda işleme yeteneğine sahiptir. Ancak kayan pencere nedeniyle her şeyi yeniden inşa etmek zorundayız. Bunu sıfırdan yapmak zorunda değiliz - IDE'yi doğru yere eklemek için mevcut kodu değiştireceğiz. Ancak tüm nesneleri değil, yalnızca olaylar alan nesneleri ekleyeceğiz. Ve bu değişiklikler de tam da bunu yapmamıza, tüm nesneleri mi yoksa yalnızca bir kısmını mı oluşturmamız gerektiğini bilmemize olanak sağlar.

Bu değişikliklerden sonra grafik üzerinde IDE bilgisine sahibiz. Ancak IDE nesneleri, kayan pencereyle ilişkili olmadıkları için gerçek bir karmaşa yaratacaktır. Şimdi ise bunun düzeltilmesi gerekiyor.

Yapacağımız ilk şey, kayan pencerenin grafik üzerinde bulunduğu noktaları elde etmektir. Pencereyi temsil eden nesnenin noktalarını almak yeterli olacaktır. Ancak nesne yönelimli programlamaya bağlı kaldığımız için bazı değişiklikler yapmamız gerekecektir; ana sonuç aşağıda görünen kod olacaktır.

//+------------------------------------------------------------------+
struct IDE_Struct
{
        int     X,
                Y,
                Index;
        bool    IsMaximized;
};
//+------------------------------------------------------------------+
class C_ChartFloating
{

// ... Class code ...

//+------------------------------------------------------------------+
                void SetPosition(const int X, const int Y, const int c0)
                        {

// ... Function code ...

                                if (c0 == m_IDEStruct.Index)
                                {
                                        m_IDEStruct.X = m_Win[c0].PosX + 3;
                                        m_IDEStruct.Y = m_Win[c0].PosY + def_SizeBarCaption + 3;
                                        m_IDEStruct.IsMaximized = m_Win[c0].IsMaximized;
                                }
                        }
//+------------------------------------------------------------------+

// ... Class code ...

//+------------------------------------------------------------------+
inline IDE_Struct GetIDE_Struct(void) const { return m_IDEStruct; }
//+------------------------------------------------------------------+
                bool Add_RAD_IDE(string sz0, int x, int y, int w, int h)
                        {
                                if ((w <= 0) || (h <= 0)) return false;
                                if (!StageLocal01(sz0, PERIOD_CURRENT, -1)) return false;
                                ChartApplyTemplate(m_Win[m_MaxCounter].handle, "\\Files\\Chart Trade\\IDE.tpl");
                                m_IDEStruct.Index = m_MaxCounter;
                                StageLocal02(x, y, w, h);
                                return true;
                        }
//+------------------------------------------------------------------+

//... The rest of the class code 
}

Çözüm, ticaret arayüzümüz olan tek pencereye odaklı olduğundan, bundan sonra işleri karmaşıklaştırmanın bir anlamı yoktur. Şimdi, en azından başlangıçta, nesneleri pencerede doğru şekilde konumlandırabiliriz. Bu, aşağıdaki fonksiyon kullanılarak yapılır.

void Resize(int x)
{
        for (int c0 = 0; c0 < m_CountObject; c0++) if (m_IsFloating)
        {
                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_XDISTANCE, GetIDE_Struct().X + m_ArrObject[c0].iPosX);
                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_YDISTANCE, GetIDE_Struct().Y + m_ArrObject[c0].iPosY);
        }else   ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_XDISTANCE, x + m_ArrObject[c0].iPosX);
};

Yukarıdaki fonksiyon, nesnelerin başlangıçta doğru şekilde konumlanmasını sağlar. Sonuç aşağıda görülebilir:


Nesneler, başlangıçta doğru konumlandırılmalarına ek olarak olaylara da tepki verebilmektedirler. Ancak, sistemde hala düzeltilmesi gereken hatalar vardır. Düzelteceğimiz ilk şey şudur:


Gördüğümüz gibi pencereyi küçültüyoruz ancak nesneler ekranda kalıyor. Bunun nedeni nesnelerin aslında pencere alanı içerisinde olmamasıdır (bunu daha önce açıklamıştım). Devam etmeden önce bunun düzeltilmesi gerekiyor. Bu, aşağıda gösterilen kod değişikliğiyle basitçe yapılabilir:

void Resize(int x)
{
        for (int c0 = 0; c0 < m_CountObject; c0++) if (m_IsFloating)
        {
                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_XDISTANCE, GetIDE_Struct().X + m_ArrObject[c0].iPosX + (GetIDE_Struct().IsMaximized ? 0 : Terminal.GetWidth()));
                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_YDISTANCE, GetIDE_Struct().Y + m_ArrObject[c0].iPosY + (GetIDE_Struct().IsMaximized ? 0 : Terminal.GetHeight()));
        }else   ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_XDISTANCE, x + m_ArrObject[c0].iPosX);
};

Sonuç aşağıdaki gibi olur:

Şimdi de hareket problemini çözelim. Bunu çözmek de aynı derecede basit. Aşağıda gösterilen kod değişikliğiyle yapılabilir:

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        static double AccumulatedRoof = 0.0;
        bool            b0;
        double  d0;
        static int px = -1, py = -1;
                                
        C_ChartFloating::DispatchMessage(id, lparam, dparam, sparam);
        if (m_CountObject < eEDIT_STOP) return;
        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
                        if ((GetIDE_Struct().X != px) || (GetIDE_Struct().Y != py))
                        {
                                px = GetIDE_Struct().X;
                                py = GetIDE_Struct().Y;
                                Resize(-1);
                        }
                        break;

//... The rest of the function ...

Ve işte nihai sonuç:

 


Sonuç

Programlamanın ne kadar harika olduğunu görüyoruz: bir problemle başlıyoruz, sonrasında kodda büyük değişiklikler yapmadan tüm problemleri teker teker çözüyoruz ve en sonunda da mümkün olan en az değişiklikle çalışan koda sahip oluyoruz.


Önemli not: Ticaret arayüzünün arka planında garip bilgiler görüyorsanız, bunun nedeni, clrNONE'da renkli nesneleri opak yapan 3228 güncellemesidir. Sorunu gidermek amacıyla ekteki IDE dosyasını kullanın.