
在MQL5中创建交易管理员面板(第三部分):通过视觉样式设计增强图形用户界面(1)
内容:
概述
回顾我们在上一篇文章中设定的目标,我们是否可以自信地说已经做得足够了呢?在我看来,激发了我们超越当前已有功能的动力。想象一下,如果我们为管理员面板实现深色和浅色主题之间的切换,那将会带来多大的用处。此外,我们可以通过添加时尚按钮、提供多种字体选择以及启用主要语言之间的切换来增强用户体验。这将使我们的面板对每个人来说都更加用户友好。
我们的目标是为交易管理员提供一个集成在交易平台内的全面通信解决方案。我们所力求融入的理念,源自20世纪70年代以来图形用户界面(GUI)领域那些具有影响力的研究成果与发展进步。值得一提的贡献者包括Alan Kay、Xerox PARC、Apple (macOS)、Microsoft (Windows)、CSS (Cascading Style Sheets)以及来自Google的Material Design。通过利用这些成果,我们可以创建一个满足用户需求并提升他们整体体验的管理员面板。
迄今为止我们开发的基础管理员面板。
回顾我们迄今为止所取得的成果:
- 创建带有消息界面和Telegram集成的管理员面板。
- 在界面上添加基本的按钮,如最小化、最大化、关闭和快速消息按钮。
到本文结尾时,我们将拥有一个完全定制并具有视觉样式的MQL5交易管理员面板。您将学习如何实现各种样式技术,这些技术可以改善界面的外观和功能,为交易者创建一个专业且用户友好的环境。
以下是本文的主要目标:- 在MQL5中应用基本样式技术
- 定制字体、颜色和布局
- 通过视觉元素增强用户交互
- 在浅色和深色主题模式之间增加可定制性
- 添加动态特性,如动画和过渡效果
应用MQL5图形用户界面样式设计特性
MQL5提供了多种功能和特性,用于定制交易应用程序的图形用户界面。包括定制颜色、字体和布局的选项,以满足用户需求并实现您想要的整体设计美感。
在MQL5中定制GUI涉及使用几个关键函数和技术。我们将讨论允许我们更改图形对象(如按钮、标签和面板)属性的函数。通过这些函数,我们可以定制背景颜色、边框样式、字体大小和其他视觉方面,以创建一个协调一致的外观和风格。
- 自定义颜色和字体
- 主界面管理逻辑
- 调整新按钮布局
自定义颜色和字体
字体数组和索引:
我们首先定义一个availableFonts数组和一个currentFontIndex来控制管理员面板的字体选择。availableFonts数组包括“Arial”、“Courier New”、“Verdana”和“Times New Roman”等字体名称,为用户提供了定制面板外观的多种选择。currentFontIndex通过索引此数组来跟踪选定的字体。这种设置使我们能够轻松地在字体之间切换,并在用户更改字体时将它们应用于UI组件,确保用户体验既具有动态性又协调一致。
// Array of available fonts string availableFonts[] = {"Arial", "Courier New", "Verdana", "Times New Roman"}; // Index of the current font in use int currentFontIndex = 0;
创建更改字体按钮:
让我们创建一个标有“字体”的按钮,并将其策略性地放置在管理员面板内。这个按钮不仅仅是一个普通按钮,也更改字体的关键功能。我们确保它能够很好地融入面板的布局,并处理其创建过程中可能出现的任何问题。通过添加这个按钮,我们为用户提供了一种直观的方式来切换不同的字体,增强了面板的可用性和美观灵活性。如果在创建按钮时出现问题,我们打印错误消息以便跟踪问题。
// Create a button for changing the font CButton changeFontButton; changeFontButton.Create(panel, "ChangeFontButton", 0, 10, 10, 100, 30); changeFontButton.Text("Font<>"); // Verify button creation and handle errors if(!changeFontButton.IsCreated()) { Print("Error creating Font<> button."); }
点击处理字体更改按钮:
当我们实现OnChangeFontButtonClick函数时,我们的目标是平稳地控制字体更改过程。该函数更新currentFontIndex以选择availableFonts数组中的下一个字体,如果需要的话,会循环回到开头。在更新索引后,我们将新字体应用于所有相关的UI组件,如输入框、清除按钮和发送按钮,确保面板的整体外观保持一致。为了完成更改,我们使用ChartRedraw来刷新显示,并打印确认消息,让用户知道字体已更改成功。
// Function to handle the font change button click void OnChangeFontButtonClick() { // Update the font index, wrapping around if necessary currentFontIndex = (currentFontIndex + 1) % ArraySize(availableFonts); string newFont = availableFonts[currentFontIndex]; // Apply the new font to UI components inputBox.Font(newFont); clearButton.Font(newFont); sendButton.Font(newFont); // Refresh the display to apply the changes ChartRedraw(); // Print confirmation of the font change Print("Font changed to ", newFont); }
OnChartEvent处理按钮点击:
在OnChartEvent函数中,我们处理用户与各种图表对象的交互,包括字体更改按钮。该函数监听按钮点击事件,并通过检查sparam字符串来确定点击了哪个按钮。当点击“ChangeFontButton”时,我们调用OnChangeFontButtonClick函数来管理字体更改。这种事件驱动的方法使我们的用户界面保持响应性和交互性,确保用户操作触发正确的响应并保持美观的界面。
// Function to handle chart events void OnChartEvent(const int id, const int sub_id, const int type, const int x, const int y, const int state) { // Handle button clicks if(type == CHARTEVENT_OBJECT_CLICK) { string buttonName = ObjectGetString(0, "ChangeFontButton", OBJPROP_TEXT); if(buttonName == "Font<>") { OnChangeFontButtonClick(); } } }
执行字体切换
主界面管理逻辑
主界面切换逻辑:
我们首先设置主题管理系统,包含两种不同的主题:浅色和深色。为了管理切换,我们使用一个布尔变量isDarkMode来跟踪当前激活的主题。切换逻辑很简单:当用户点击主题按钮时,isDarkMode的值会翻转,从而改变管理员面板的整体外观和风格。通过分别定义每种主界面的颜色,我们简化了流程,使其在需要时易于维护和应用新样式。
bool isDarkMode = false; // Tracks the current theme mode (light or dark) color lightBackgroundColor = clrWhite; // Background color for light mode color darkBackgroundColor = clrBlack; // Background color for dark mode color lightTextColor = clrBlack; // Text color for light mode color darkTextColor = clrWhite; // Text color for dark mode
创建主界面切换按钮:接下来,我们创建一个标有"主界面" 的按钮。此按钮位于管理员面板内,为用户提供了一种轻松切换浅色和深色模式的方法。如果在创建过程中出现问题,我们会通过打印消息来处理错误。这有助于简化故障排除,并确保界面保持直观性和响应性。//Creating the theme switch button if(!CreateButton("ToggleThemeButton", "Theme<>", 50, 220, 100, 30)) { Print("Error: Failed to create theme toggle button"); // Error handling if button creation fails }
点击处理主界面切换按钮:接下来,我们通过实现OnToggleModeButtonClick函数来处理实际的主界面更改。该函数会快速翻转isDarkMode变量,从而在浅色和深色主题之间切换。一旦我们知道当前激活的是哪种主界面,我们就会将相应的背景和文本颜色应用到所有UI元素上,例如面板、按钮和文本。由于进行了快速刷新,主界面更改会实时发生,使界面感觉流畅且响应迅速。我们还会打印一条确认消息,以便告知用户模式已更改。//Theme switching handler void OnToggleModeButtonClick() { isDarkMode = !isDarkMode; // Toggle the theme mode if(isDarkMode) { ApplyTheme(darkBackgroundColor, darkTextColor); // Apply dark mode colors } else { ApplyTheme(lightBackgroundColor, lightTextColor); // Apply light mode colors } Print("Theme has been switched"); // Inform the user that the theme has changed }
OnChartEvent点击处理主界面切换:在OnChartEvent函数中,我们检测用户何时点击“切换主界面”按钮,并触发OnToggleModeButtonClick函数。这种事件驱动的方法确保面板能够即时响应用户操作。通过监听按钮点击事件,我们确保管理员面板保持交互性和美观度,让用户能够根据需要轻松切换浅色和深色主题。
//The OneChartEvent for the theme void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if(id == CHARTEVENT_OBJECT_CLICK) // Check if the event is a button click { if(sparam == "ToggleThemeButton") // Check if the clicked button is the theme toggle button { OnToggleModeButtonClick(); // Call the function to handle the theme change } } }
在不重建对象的情况下应用主界面:我们的一项关键设计决策是在不重建面板任何对象的情况下更新主界面。我们不是拆卸并构建新的UI组件,而是简单地将新的配色方案应用到现有元素上。这样可以使系统保持高效,减少延迟并维持流畅的用户体验。还能确保我们在动态应用新颜色时,面板保持响应性。
//Applying theme void ApplyTheme(color backgroundColor, color textColor) { // Update background and text colors of existing objects ObjectSetInteger(0, "AdminPanelBackground", OBJPROP_COLOR, backgroundColor); // Change background color ObjectSetInteger(0, "ClearButton", OBJPROP_COLOR, textColor); // Change text color of clear button ObjectSetInteger(0, "SendButton", OBJPROP_COLOR, textColor); // Change text color of send button ObjectSetInteger(0, "InputBox", OBJPROP_COLOR, textColor); // Change text color of input box ChartRedraw(); // Redraw the chart to reflect the changes }
调整新按钮布局
字体更改按钮:
我们将字体更改按钮放置在管理员面板中,其左上角位于(95, 95),右下角位于(230, 115)。这使得它位于发送和清除按钮的左侧。它的尺寸足够宽,可以容纳“字体”标签并方便用户进行交互。该按钮允许用户在面板内所有文本元素的不同字体选项之间进行切换。if (!changeFontButton.Create(chart_id, "ChangeFontButton", 0, 95, 95, 230, 115))
主界面切换按钮:
至于主界面切换按钮,我们将其左上角放置在坐标(5, 95),右下角放置在(90, 115)。这使得按钮位于面板的最左侧,略微高于字体更改按钮,提供了清晰的分隔。其小巧的尺寸以及与其他按钮紧密相邻的设计,使得用户可以轻松地在深色和浅色主界面之间切换,而不会使界面显得杂乱无章。if (!toggleThemeButton.Create(chart_id, "ToggleThemeButton", 0, 5, 95, 90, 115))
以下是我们的完整程序,所有新功能都已完美集成。
//+------------------------------------------------------------------+ //| Admin Panel.mq5 | //| Copyright 2024, Clemence Benjamin | //| https://www.mql5.com/en/users/billionaire2024/seller | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, Clemence Benjamin" #property link "https://www.mql5.com/en/users/billionaire2024/seller" #property description "A responsive Admin Panel. Send messages to your telegram clients without leaving MT5" #property version "1.11" #include <Trade\Trade.mqh> #include <Controls\Dialog.mqh> #include <Controls\Button.mqh> #include <Controls\Edit.mqh> #include <Controls\Label.mqh> // Input parameters input string QuickMessage1 = "Updates"; input string QuickMessage2 = "Close all"; input string QuickMessage3 = "In deep profits"; input string QuickMessage4 = "Hold position"; input string QuickMessage5 = "Swing Entry"; input string QuickMessage6 = "Scalp Entry"; input string QuickMessage7 = "Book profit"; input string QuickMessage8 = "Invalid Signal"; input string InputChatId = "Enter Chat ID from Telegram bot API"; input string InputBotToken = "Enter BOT TOKEN from your Telegram bot"; // Global variables CDialog adminPanel; CButton sendButton, clearButton, changeFontButton, toggleThemeButton; CButton quickMessageButtons[8], minimizeButton, maximizeButton, closeButton; CEdit inputBox; CLabel charCounter; #define BG_RECT_NAME "BackgroundRect" bool minimized = false; bool darkTheme = false; int MAX_MESSAGE_LENGTH = 4096; string availableFonts[] = { "Arial", "Courier New", "Verdana", "Times New Roman" }; int currentFontIndex = 0; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Initialize the Dialog if (!adminPanel.Create(ChartID(), "Admin Panel", 0, 30, 30, 500, 500)) { Print("Failed to create dialog"); return INIT_FAILED; } // Create controls if (!CreateControls()) { Print("Control creation failed"); return INIT_FAILED; } adminPanel.Show(); // Initialize with the default theme CreateOrUpdateBackground(ChartID(), darkTheme ? clrBlack : clrWhite); Print("Initialization complete"); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Create necessary UI controls | //+------------------------------------------------------------------+ bool CreateControls() { long chart_id = ChartID(); // Create the input box if (!inputBox.Create(chart_id, "InputBox", 0, 5, 25, 460, 95)) { Print("Failed to create input box"); return false; } adminPanel.Add(inputBox); // Character counter if (!charCounter.Create(chart_id, "CharCounter", 0, 380, 5, 460, 25)) { Print("Failed to create character counter"); return false; } charCounter.Text("0/" + IntegerToString(MAX_MESSAGE_LENGTH)); adminPanel.Add(charCounter); // Clear button if (!clearButton.Create(chart_id, "ClearButton", 0, 235, 95, 345, 125)) { Print("Failed to create clear button"); return false; } clearButton.Text("Clear"); adminPanel.Add(clearButton); // Send button if (!sendButton.Create(chart_id, "SendButton", 0, 350, 95, 460, 125)) { Print("Failed to create send button"); return false; } sendButton.Text("Send"); adminPanel.Add(sendButton); // Change font button if (!changeFontButton.Create(chart_id, "ChangeFontButton", 0, 95, 95, 230, 115)) { Print("Failed to create change font button"); return false; } changeFontButton.Text("Font<>"); adminPanel.Add(changeFontButton); // Toggle theme button if (!toggleThemeButton.Create(chart_id, "ToggleThemeButton", 0, 5, 95, 90, 115)) { Print("Failed to create toggle theme button"); return false; } toggleThemeButton.Text("Theme<>"); adminPanel.Add(toggleThemeButton); // Minimize button if (!minimizeButton.Create(chart_id, "MinimizeButton", 0, 375, -22, 405, 0)) { Print("Failed to create minimize button"); return false; } minimizeButton.Text("_"); adminPanel.Add(minimizeButton); // Maximize button if (!maximizeButton.Create(chart_id, "MaximizeButton", 0, 405, -22, 435, 0)) { Print("Failed to create maximize button"); return false; } maximizeButton.Text("[ ]"); adminPanel.Add(maximizeButton); // Close button if (!closeButton.Create(chart_id, "CloseButton", 0, 435, -22, 465, 0)) { Print("Failed to create close button"); return false; } closeButton.Text("X"); adminPanel.Add(closeButton); // Quick messages return CreateQuickMessageButtons(); } //+------------------------------------------------------------------+ //| Create quick message buttons | //+------------------------------------------------------------------+ bool CreateQuickMessageButtons() { string quickMessages[8] = { QuickMessage1, QuickMessage2, QuickMessage3, QuickMessage4, QuickMessage5, QuickMessage6, QuickMessage7, QuickMessage8 }; int startX = 5, startY = 160, width = 222, height = 65, spacing = 5; for (int i = 0; i < 8; i++) { if (!quickMessageButtons[i].Create(ChartID(), "QuickMessageButton" + IntegerToString(i + 1), 0, startX + (i % 2) * (width + spacing), startY + (i / 2) * (height + spacing), startX + (i % 2) * (width + spacing) + width, startY + (i / 2) * (height + spacing) + height)) { Print("Failed to create quick message button ", i + 1); return false; } quickMessageButtons[i].Text(quickMessages[i]); adminPanel.Add(quickMessageButtons[i]); } return true; } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { adminPanel.Destroy(); ObjectDelete(ChartID(), BG_RECT_NAME); Print("Deinitialization complete"); } //+------------------------------------------------------------------+ //| Handle chart events | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { switch (id) { case CHARTEVENT_OBJECT_CLICK: if (sparam == "SendButton") OnSendButtonClick(); else if (sparam == "ClearButton") OnClearButtonClick(); else if (sparam == "ChangeFontButton") OnChangeFontButtonClick(); else if (sparam == "ToggleThemeButton") OnToggleThemeButtonClick(); else if (sparam == "MinimizeButton") OnMinimizeButtonClick(); else if (sparam == "MaximizeButton") OnMaximizeButtonClick(); else if (sparam == "CloseButton") OnCloseButtonClick(); else if (StringFind(sparam, "QuickMessageButton") != -1) { int index = StringToInteger(StringSubstr(sparam, 18)); OnQuickMessageButtonClick(index - 1); } break; case CHARTEVENT_OBJECT_ENDEDIT: if (sparam == "InputBox") OnInputChange(); break; } } //+------------------------------------------------------------------+ //| Handle custom message send button click | //+------------------------------------------------------------------+ void OnSendButtonClick() { string message = inputBox.Text(); if (message != "") { if (SendMessageToTelegram(message)) Print("Custom message sent: ", message); else Print("Failed to send custom message."); } else { Print("No message entered."); } } //+------------------------------------------------------------------+ //| Handle clear button click | //+------------------------------------------------------------------+ void OnClearButtonClick() { inputBox.Text(""); // Clear the text in the input box OnInputChange(); // Update the character counter Print("Input box cleared."); } //+------------------------------------------------------------------+ //| Handle quick message button click | //+------------------------------------------------------------------+ void OnQuickMessageButtonClick(int index) { string quickMessages[8] = { QuickMessage1, QuickMessage2, QuickMessage3, QuickMessage4, QuickMessage5, QuickMessage6, QuickMessage7, QuickMessage8 }; string message = quickMessages[index]; if (SendMessageToTelegram(message)) Print("Quick message sent: ", message); else Print("Failed to send quick message."); } //+------------------------------------------------------------------+ //| Update character counter | //+------------------------------------------------------------------+ void OnInputChange() { int currentLength = StringLen(inputBox.Text()); charCounter.Text(IntegerToString(currentLength) + "/" + IntegerToString(MAX_MESSAGE_LENGTH)); ChartRedraw(); } //+------------------------------------------------------------------+ //| Handle toggle theme button click | //+------------------------------------------------------------------+ void OnToggleThemeButtonClick() { darkTheme = !darkTheme; color bgColor = darkTheme ? clrBlack : clrWhite; color textColor = darkTheme ? clrWhite : clrBlack; // Set text color appropriate to the theme inputBox.Color(textColor); clearButton.Color(textColor); sendButton.Color(textColor); toggleThemeButton.Color(textColor); changeFontButton.Color(textColor); for(int i = 0; i < ArraySize(quickMessageButtons); i++) { quickMessageButtons[i].Color(textColor); } charCounter.Color(textColor); CreateOrUpdateBackground(ChartID(), bgColor); ChartRedraw(); } //+------------------------------------------------------------------+ //| Create and update background rectangle | //+------------------------------------------------------------------+ void CreateOrUpdateBackground(long chart_id, color bgColor) { if (!ObjectFind(chart_id, BG_RECT_NAME)) { if (!ObjectCreate(chart_id, BG_RECT_NAME, OBJ_RECTANGLE, 0, 0, 0)) Print("Failed to create background rectangle"); } ObjectSetInteger(chart_id, BG_RECT_NAME, OBJPROP_COLOR, bgColor); ObjectSetInteger(chart_id, BG_RECT_NAME, OBJPROP_BACK, true); ObjectSetInteger(chart_id, BG_RECT_NAME, OBJPROP_SELECTABLE, false); ObjectSetInteger(chart_id, BG_RECT_NAME, OBJPROP_SELECTED, false); ObjectSetInteger(chart_id, BG_RECT_NAME, OBJPROP_HIDDEN, false); ObjectSetInteger(chart_id, BG_RECT_NAME, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(chart_id, BG_RECT_NAME, OBJPROP_XOFFSET, 25); ObjectSetInteger(chart_id, BG_RECT_NAME, OBJPROP_YOFFSET, 25); } //+------------------------------------------------------------------+ //| Handle change font button click | //+------------------------------------------------------------------+ void OnChangeFontButtonClick() { currentFontIndex = (currentFontIndex + 1) % ArraySize(availableFonts); inputBox.Font(availableFonts[currentFontIndex]); clearButton.Font(availableFonts[currentFontIndex]); sendButton.Font(availableFonts[currentFontIndex]); toggleThemeButton.Font(availableFonts[currentFontIndex]); changeFontButton.Font(availableFonts[currentFontIndex]); for(int i = 0; i < ArraySize(quickMessageButtons); i++) { quickMessageButtons[i].Font(availableFonts[currentFontIndex]); } Print("Font changed to: ", availableFonts[currentFontIndex]); ChartRedraw(); } //+------------------------------------------------------------------+ //| Handle minimize button click | //+------------------------------------------------------------------+ void OnMinimizeButtonClick() { minimized = true; adminPanel.Hide(); minimizeButton.Hide(); maximizeButton.Show(); closeButton.Show(); Print("Panel minimized."); } //+------------------------------------------------------------------+ //| Handle maximize button click | //+------------------------------------------------------------------+ void OnMaximizeButtonClick() { if (minimized) { adminPanel.Show(); minimizeButton.Show(); maximizeButton.Hide(); closeButton.Hide(); Print("Panel maximized."); } } //+------------------------------------------------------------------+ //| Handle close button click | //+------------------------------------------------------------------+ void OnCloseButtonClick() { ExpertRemove(); // Completely remove the EA Print("Admin Panel closed."); } //+------------------------------------------------------------------+ //| Send the message to Telegram | //+------------------------------------------------------------------+ bool SendMessageToTelegram(string message) { string url = "https://api.telegram.org/bot" + InputBotToken + "/sendMessage"; string jsonMessage = "{\"chat_id\":\"" + InputChatId + "\", \"text\":\"" + message + "\"}"; char post_data[]; ArrayResize(post_data, StringToCharArray(jsonMessage, post_data, 0, WHOLE_ARRAY) - 1); int timeout = 5000; char result[]; string responseHeaders; int res = WebRequest("POST", url, "Content-Type: application/json\r\n", timeout, post_data, result, responseHeaders); if (res == 200) // HTTP 200 OK { Print("Message sent successfully: ", message); return true; } else { Print("Failed to send message. HTTP code: ", res, " Error code: ", GetLastError()); Print("Response: ", CharArrayToString(result)); return false; } }
在黄金兑美元(XAUUSD)上测试新功能
在掌握这些基础样式技术之后,我们现在可以探索更多高级定制选项,这些选项可以为图形用户界面带来更大的交互性和视觉吸引力。从上面的图像中,我们可以看到我们的主界面仅对前景文本有效,但我们希望它也能影响面板背景。在下一个部分中,我们将探讨解决这一问题的方法。
图形用户界面的高级增强
扩展主界面管理对话框类:
为了扩展Dialog以进行主界面管理,我们可以定制现有的对话框类,以支持动态主题更改,类似于我们在管理员面板中管理主界面的方式。这将涉及修改或子类化CDialog类,以包含背景和文本颜色的属性,以及应用不同主题(浅色或深色)的方法。通过覆盖构造函数或添加如ApplyTheme等方法,我们可以确保使用此类创建的对话框能够响应主界面更改,而无需重新创建对话框对象。
自定义Dialog类的颜色
为什么这点很重要?
为主题管理扩展对话框类,可以在所有UI元素(而不仅仅是管理员面板)中提供更无缝且协调一致的用户体验。这样确保应用程序的所有部分——包括对话框——都遵循所选主界面,从而增强可用性和视觉一致性。在交易应用程序中,此功能尤其重要,因为用户可能会花费较长时间与界面交互,而可定制的主界面可以减少视觉疲劳并提高用户的满意度。
管理员面板:修改对话框类后的背景主界面
其他选项:
虽然扩展对话框类是一种直接且灵活的方法,但另一种选择是在更高层次上应用主界面管理。例如,我们可以创建一个全局主界面管理系统,自动更新所有UI元素(包括对话框)的属性,而无需对各个组件进行更改。此外,如果出现特定的样式需求,利用外部库或设计一个自定义的对话框框架,或许能对UI元素实现更精细地控制。
CEdit类
根据谷歌搜索,Telegram消息的最大长度为4096个字符,并且必须是UTF-8编码。在尝试将该值应用到本项目中时,我们发现最多只能输入63个字符,问题可能出在我们将要在下一篇文章中讨论的CEdit类的限制上。
结论
总而言之,我们在管理员面板程序中实施的字体和主界面管理,表现出令人满意的结果。尽管我们在对话框类的静态背景方面遇到了一些限制,但文本前景成功地适配了主界面的更改,从而提升了用户体验。动态字体管理也运行良好,允许用户轻松切换不同的字体。接下来,我们的下一步将是扩展对话框类以完全支持主界面更改,包括动态背景更新。这一改进旨在克服当前的限制,并提供一个更加协调且视觉上更具吸引力的界面。请继续关注,我们将在后续文章中应对这些挑战!
在您自己的交易面板上尝试这些样式技术,并探索MQL5中更多的定制选项。我很想听听您的经验和见解,请随时在下面的评论中分享,因为我们将深入探讨更高级的GUI设计挑战。本项目的源文件已附上——您可以随时查看。
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/15419
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.



代码行:
问题:在图表上移动窗口无法实现?