C++ / CLI 入门及 GDI 库实现 FCFS





5.00/5 (3投票s)
C++ / CLI 入门,使用 C++ CLI 和 GDI 库
引言
今天,我们将介绍如何在 GUI 项目中使用 C++ CLI(通用语言接口),并讨论 FCFS(先来先服务)算法,以及如何使用 GDI 库绘制它。我们将一步一步地创建你的第一个 CLI 项目直到发布。
“使用代码”部分分为:
- 设置你的 CLI 项目
- 简单测试
- 开始工作
- 处理表格中的内容
- 如何解析表格中的文本框
- 使用 GDI 库绘图
- 发布我们的成果
背景
FCFS(先来先服务)是操作系统用于排序哪个进程使用 CPU 的算法……简单来说,就是哪个进程先来就先服务,然后是下一个,再下一个。
我们将使用 GDI 库来实现它。
Using the Code
1. 设置
开始创建一个新的空 CLR 项目

添加新窗体


将这段代码添加到你的 form.cpp 中
using namespace System;
using namespace System::Windows::Forms;
[STAThread]//leave this as is
void main(array<String^>^ args) {
 Application::EnableVisualStyles();
 Application::SetCompatibleTextRenderingDefault(false);
 <<projectName>>::<<formName>> form; // replace<<...>> //don't write << and >> 
                                     // just replace what is inside
 Application::Run(%form);
}

打开属性

在“配置属性”>“链接器”>“高级”下,将入口点更改为“main”(不含引号)。

在“配置属性”>“链接器”>“系统”下,将“子系统”更改为“Windows (/SUBSYSTEM/WINDOWS)”。

2. 简单测试
在你的 `designer` 中,我们先从将一个按钮拖放到窗体上开始

双击此按钮,将自动为你添加一个后台代码函数,并添加这段简单的代码来测试你的程序是否正常工作。
private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e)
 {
  MessageBox::Show("hi");
 }

现在运行: 
如果出现这个,那么你所有的配置都正确了!!
3. 那么让我们开始工作吧
首先,让我们从属性中更改我们刚刚添加的按钮的名称

让我们向窗体添加一个表格

更改其一些属性,只需将列数更改为三

现在,只需添加 3 个标签,并像我们处理按钮属性一样更改它们的名称>>外观>>文本。

现在让我们回到后台代码并修改我们旧的 `button_click` 函数,但在此之前定义一个 `int`
  int u = 1;//to hold the number of rows
private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e)
 {
  TextBox^ t1 = gcnew TextBox();    //define a text box
  TextBox^ t2 = gcnew TextBox();    //define a text box
				                    //both will then go under the first column
  
  Button^ b1 = gcnew Button();      //define a new button that will be 
                                    //a selectable button to change colors
  b1->Text = "color" + u.ToString();//change the text that appears to the user
  b1->Name = "color" + u.ToString();//change the text that you be able to access 
                                    //the button within the back code
  b1->Click += gcnew System::EventHandler(this, &cli_fcfs::MyForm::OnClick);
  //add a click event on this button
  tableLayoutPanel1->Controls->Add(t1, 0, u);//here, you would add the first text box 
                                             //to the new row in the first col
  tableLayoutPanel1->Controls->Add(t2, 1, u);//here, you would add the second text box 
                                             //to the new row in the second col
  tableLayoutPanel1->Controls->Add(b1, 2, u);//add the color button to the third column
  u++;//increment a counter to hold the value of number of rows
 }
它会是这样的

当你创建 `onclick` 事件时,这将生成
 void OnClick(System::Object ^sender, System::EventArgs ^e); // a constructor
Button^ temp = gcnew Button();//specify a new button 
};
}
void cli_fcfs::MyForm::OnClick(System::Object ^sender, System::EventArgs ^e)//function itself
{
 temp = (Button^)(Object^)sender;  //to define the temp button with the clicked button
	//here we just type casted the sender as a general object
	//then define it as a button
 button2->Visible = true;    //after some seconds, 
                             //I will go through creating the color options so this code 
			                 // will be illustrated even more
 pictureBox1->Visible = true;
	   	//but simply you would have a picture box for selecting colors 
        //and a button to view which color is selected
		//normally this would be invisible
		//when you click the generated button both the picture box 
        //and the button would be visible 
}
这将看起来像

所以当你运行你的代码时,当你点击 **添加** 按钮时,它应该看起来像这样

4. 处理表格中的内容
开始添加一个处理按钮

并双击它在后台代码中创建一个函数。
现在,让我们添加我告诉你的 picture box

并在“属性>>图像”中,放入此图像

并为此 picture box 添加一些事件。
添加一个鼠标移动事件,这样当用户移动鼠标时,选定的颜色就会被捕获到临时存储中

直到他点击鼠标……然后 picture box 会消失……颜色会被捕获。
所以,让我们添加另一个事件。

我们还添加一个按钮来向用户显示如果他点击鼠标他将选择什么颜色。

并将其“属性>>扁平样式”更改为“flat”,将其“属性>>文本”设置为空
现在让我们在用户首次打开窗体时让按钮和 picture box 和按钮不可见,因为我们需要它们在用户点击它们之前是不可见的。

现在让我们修改 `pictureBox1_MouseMoveon`
Color pixel; //define a color outside the function to be easily accessed
private: System::Void pictureBox1_MouseMove
         (System::Object^  sender, System::Windows::Forms::MouseEventArgs^  e)
{
 Bitmap ^ bmpSource = reinterpret_cast<Bitmap ^>
         (pictureBox1->Image); //put the image of th picture box into a bitmap
 pixel = bmpSource->GetPixel(e->X, e->Y);//get the color of mouse position 
 button2->BackColor = pixel;//set this color to the button of temp storage
}
现在,让我们修改 `pictureBox1_MouseClick`
private: System::Void pictureBox1_MouseClick
(System::Object^  sender, System::Windows::Forms::MouseEventArgs^  e)
{
 pictureBox1->Visible = false; 
 button2->Visible = false;
//make the picture and the button invisible
 temp->BackColor = pixel;// save the temp color to the button 
                         // that raised the event from the first place
}
5. 如何解析表格中的文本框
我们将讨论你窗体中两种类型的解析元素,第一种是如何解析表格列中的控件。第二种将在稍后讨论如何解析你知道其名称的控件。
但首先,让我们定义两个列表,我们将把我们的元素放在里面。我们在任何函数之外定义它们。
 ArrayList names;
 ArrayList cpu_burst_names;
然后,让我们在 `process_Click` 中开始第一个方法
private: System::Void process_Click(System::Object^  sender, System::EventArgs^  e)
{
 for (int i = 1; i < u; i++)
 {
  TextBox^ t1 = gcnew TextBox();//define a temp text box
  t1 = (TextBox^)tableLayoutPanel1->GetControlFromPosition(0, i);//parse the table 
                                                                 //from a specified position
                                                                 //and type cast it and 
                                                                 //put it in the temp text box
  names.Add(t1->Text);                 //now add the text inside the temp text box to the list
 }
// same for the next column
 for (int i = 1; i < u; i++)
 {
  TextBox^ t1 = gcnew TextBox();;
  t1 = (TextBox^)tableLayoutPanel1->GetControlFromPosition(1, i);
  cpu_burst_names.Add(t1->Text);
 }
 draw();    //then draw
}
6. 让我们使用 GDI 库绘图
只需创建一个名为 `draw()` 的函数
   void draw()
   {
    auto g = this->CreateGraphics(); //this will define your starting graphics
    int count = 0;//just a counter for rows
    int x = 50;//for coordinates
    int sum_cpu = 0;//to view the total time for FCFS
    for (int i = 0; i < names.Count; i++)//just a loop to iterate on all names 
					//with the same loop, we will iterate through cpu burt times
    {
     String^ color_name = "color" + (i + 1); //make a string to hold the name of the button 
                                             //to find it
     Control^ ctn1 = FindControl(color_name);//a function n that I will talk about later 
                                             //which the second way to parse your form for a 
					                         //specified control just if you know its name
     Pen^  my_pen = gcnew Pen((Color)ctn1->BackColor);//make a pen with the 
                                                      //color of the button
     String^ a = (cpu_burst_names[i]->ToString());    //string of the time
     int^  width;
     width = Convert::ToInt32(a);//convert time into int
     g->DrawRectangle(my_pen, x, tableLayoutPanel1->Size.Height, *width * 10, 50);
			//draw the outline of the rectangle with the width if the time
			//and its y to be at the end of your tabel
			//it would be even better to make outline a white color 
            //not like what it states here
			//and only when you fill the rectangle you fill with 
            //the specified color of the button
     Rectangle r(x, tableLayoutPanel1->Size.Height, *width * 10, 50);
//define a rectangle just the same as when you drew it
//but we define it to be able to put the name of the process 
//in the middle of this rectangle 
     //just some properties to be able to draw the strings
     StringFormat^ sf = gcnew StringFormat();
     SolidBrush^ br = gcnew SolidBrush(Color::Black);
     System::Drawing::Font^ f = gcnew System::Drawing::Font("Arial", 10);
	 //define a solid brush to be able to draw the rectangle with the needed color 
     SolidBrush^ baaaa = gcnew SolidBrush((Color)ctn1->BackColor);
     g->FillRectangle(baaaa, r);//now fill the rectangle
     g->DrawString(names[i]->ToString(), f, br, midp(r).X, midp(r).Y, sf);
	 // add the name of the process in the middle of the rectangle 
     // so we define the function of the middle 
	 // that would be later discussed
	 //now we need to view the sum of the cpu burst time
     sum_cpu = sum_cpu + Convert::ToInt32(cpu_burst_names[i]->ToString());
     int^ temp = sum_cpu;
     System::String^ str = temp->ToString();//define the number to a string
     g->DrawString(str, f, br, r.Right - 10, r.Bottom, sf);
	 //draw the string at the end of each process
     x = x + *width * 10;//update the coordinates
    }
}
现在让我们定义一个函数来解析窗体以查找特定控件并返回该控件
   Control^ FindControl(String^ strControlName)
   {
    if (strControlName->Length == 0 || 
        this->Controls->Find(strControlName, true)->Length == 0)
     return nullptr;
    return this->Controls->Find(strControlName, true)[0];
   }
现在,我们将编写中间的函数
Point midp(Rectangle r)
   {
    Rectangle a = (Rectangle)r; //take the rectangle
    int x = a.Location.X;
    int y = a.Location.Y;
    int h = a.Height;
    int w = a.Width;
    Point pp;
    pp.X = x + w / 2 - 5; //calculate mid point x//me 5
    pp.Y = y + h / 2 - 5; //calculate mid point y//me 5
    return pp;
   }
它只是计算矩形的中心点,以便我们将进程名称显示在中心。
所以最后,它应该看起来像这样

7. 让我们发布我们的作品
理想情况下,你应该将你的项目设置为 release 而不是 debug……但出于某种奇怪的原因,这对我不起作用。
所以只需在调试你的项目后……转到其目录>>debug>>yourName.exe 并复制此文件。
它可能需要一些依赖项……所以在 Visual Studio 的 bin 文件中查找它们
了解你们对这些教程的看法对我来说将非常重要……它们是否达到了你们的期望?……我真的很想听听你们的反馈
关注点
我们想邀请你们观看我们使用 C# 中的 GDI 教程
- [教程1] GDI+ SVG 艺术作品
- [tut2]---使用链表 GDI+ SVG 交互式添加多个形状
- [tut3]---[教程 3] 使用 C# 拖放删除对象的图形程序
历史
- 2016 年 3 月 9 日:初始版本


