热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Graphics.FromImage(bitmap)的问题,为什么每次都要重新newbitmap

使用Bitmap对控件进行绘图,并且令控件的Background为Bitmap,代码如下Bitmapbitmap;申明为类成员Graphicsgraph;privatevoi
使用Bitmap对控件进行绘图,并且令控件的Background为Bitmap,代码如下


Bitmap bitmap;//申明为类成员
Graphics graph;

private void createImage()
{//调用的时候先调用createImage方法
   bitmap=new Bitmap(this.Width,this.Height);
   graph=new Graphics.FromImage(bitmap);
}
private void updateImage()

   bitmap=new Bitmap(this.Width,this.Height);//问题在于为什么一定要在此处对bitmap重新new,
                                             //如果不重新new就显示不出线条
   graph=new Graphics.FromImage(bitmap);
   graph.DrawLine(pen,0,2,20,50);
   this.BackgroundImage=bitmap;
}

//如果这样写就不会有图形
private void updateImage()
{
   graph.DrawLine(pen,0,2,20,50);
   this.BackgroundImage=bitmap;
}


调用的时候是先调用的createImage(),先创建了bitmap和graph,然后再调用updateImage()方法,但是在updateImage里一定要对bitmap重新赋值才能画出图形,请教这是为什么?

25 个解决方案

#1


 
//如果这样写就不会有图形
private void updateImage()
{
   graph.DrawLine(pen,0,2,20,50);
   this.BackgroundImage=bitmap;
}

这样有可能会报错:未将对象引用到对象实例。

我想楼主还没有找着对象吧。

#2


你断点调试下:
1)看看第一次调用createImage时,this.Width,this.Height具体的值是多少,可能是0或者很小的值;
其实是画了,只是根本看不见;
2)你调用updateImage时,this.Width,this.Height已经变成非0或者肉眼能看到的值了;只不过需要重新给bitmap赋值!

#3


你不new 哪里来的对象呢?虽然有这个方法createImage,你确定他不会被GC回收?

#4


引用 1 楼 yuwenge 的回复:
 
//如果这样写就不会有图形
private void updateImage()
{
   graph.DrawLine(pen,0,2,20,50);
   this.BackgroundImage=bitmap;
}

这样有可能会报错:未将对象引用到对象实例。

我想楼主还没有找着对象吧。


你都不仔细看我的帖子的。。。。
我先调用的CreateImage()这个方法,这个方法里已经对bitmap创建实例了,为什么创建了还要再创建?

#5


引用 2 楼 sudazf 的回复:
你断点调试下:
1)看看第一次调用createImage时,this.Width,this.Height具体的值是多少,可能是0或者很小的值;
其实是画了,只是根本看不见;
2)你调用updateImage时,this.Width,this.Height已经变成非0或者肉眼能看到的值了;只不过需要重新给bitmap赋值!


我断点调了,在updateImage里即使没有重新New Bitmap,graph的Width是150,Height是300,正好是我那个控件的大小,奇怪的是为啥就是画不出图来。

#6


引用 3 楼 yyantifa 的回复:
你不new 哪里来的对象呢?虽然有这个方法createImage,你确定他不会被GC回收?


我也想是不是这个原因,但是Bitmap我是申明为类成员的,并不是局部变量,而且奇怪的是,如果在UpdateImage里没有对bitmap重新new,断点调试时,可以看到graph是有大小,好苦恼这是为什么

#7


如果你createImage()执行后立刻调用updateImage(),应该效果和重新new一样的,因为代码之间没有任何其它干预,你说的是什么特殊情况?

#8


引用 4 楼 leafmao 的回复:
Quote: 引用 1 楼 yuwenge 的回复:

 
//如果这样写就不会有图形
private void updateImage()
{
   graph.DrawLine(pen,0,2,20,50);
   this.BackgroundImage=bitmap;
}

这样有可能会报错:未将对象引用到对象实例。

我想楼主还没有找着对象吧。


你都不仔细看我的帖子的。。。。
我先调用的CreateImage()这个方法,这个方法里已经对bitmap创建实例了,为什么创建了还要再创建?

贴完整代码吧,这么两句话说不清你到底怎么写的。

#9


引用 7 楼 qldsrx 的回复:
如果你createImage()执行后立刻调用updateImage(),应该效果和重新new一样的,因为代码之间没有任何其它干预,你说的是什么特殊情况?


先调用createImage(),并且这个过程中是不会释放 bitmap的,我把这个控件封装好,然后在外部调用这些方法,详细代码贴在下楼

#10


引用 8 楼 yuwenge 的回复:
贴完整代码吧,这么两句话说不清你到底怎么写的。


详细代码大致如下, 这是自定义的控件

private Bitmap bitmap;
private Graphics graph;
private Pen penWave = new Pen(Color.Lime);
private Pen penGrid = new Pen(Color.Gray);

 protected override void OnLoad(EventArgs e)
{
            //打开双缓冲,防止闪烁
            DoubleBuffered = true;
            canvas_height = base.ClientSize.Height;
            canvas_width = base.ClientSize.Width;
            bitmap = new Bitmap(this.Width, this.Height);
            graph = Graphics.FromImage(bitmap);
            DrawGrids(ref graph);
            this.BackgroundImage = bitmap;
 }

protected override void OnResize(EventArgs e)
 {
            canvas_height = base.ClientSize.Height;
            canvas_width = base.ClientSize.Width;
            bitmap = new Bitmap(this.Width, this.Height);
            graph = Graphics.FromImage(bitmap);
            DrawGrids(ref graph);
            DrawDot(ref graph);
            this.BackgroundImage = bitmap;
       
}

 private void DrawGrids(ref Graphics g)
{//画网格
  //pos,canvas_height都是变量,不重复贴了,不影响整体
   g.DrawLine(penGrid, pos, 0, pos, canvas_height);//基本上都是这种代码,不重复贴,没做别的处理
}

private void DrawWave(ref Graphics g)
{
  graph.DrawLine(penWave,0,2,20,50);//基本上都是这种,两点之间连线的,不重复贴代码了
}

//调用的时候,会先调用这个CreateImage()方法,再调用UpdateImage
//CreateImage只调用一次,UpdateImage会调用多次
public void CreateImage()

  bitmap=new Bitmap(this.Width,this.Height);
   graph=new Graphics.FromImage(bitmap);
}


 public void UpdateImage()
 {//经过反复试验,在这个方法里必须要对bitmap重新new,否则看不到两点间的连线
       
            bitmap = new Bitmap(this.Width, this.Height);
            graph = Graphics.FromImage(bitmap);
            DrawDot(ref graph);
            this.BackgroundImage = bitmap;
}



本来是想在bitmap上先画好格子,然后再调用UpdateImage只画点,不用重复画格子,但是现在的问题是每次都要对bitmap重新new,于是只能每次都要重新画格子,这样影响效率,所以才问这么个问题。为什么之前对bitmap创建过实例了,然后进行画图看不到图形?

#11


上面写错一行代码

在UpdateImage()里的DrawDot(ref graph)要改成DrawWave(ref graph)

#12


你上面给的代码里面CreateImage和UpdateImage压根没用到,你应该把不起作用的代码贴出来,把能作用的代码贴出来分析什么啊?

#13


引用 12 楼 qldsrx 的回复:
你上面给的代码里面CreateImage和UpdateImage压根没用到,你应该把不起作用的代码贴出来,把能作用的代码贴出来分析什么啊?


囧死了,我文字部分做出说明了,那我就再把怎么使用的贴出来好了。。。。。
这个类的实例名字叫gridCanvas,在Form1里使用


 private void Form1_Load(object sender, EventArgs e)
{
     gridCanvas.CreateImage();//调用的是类里的方法
}

//以下方法被执行
private void receive()
{
   gridCanvas.UpdateImage();
}

#14


引用 12 楼 qldsrx 的回复:
你上面给的代码里面CreateImage和UpdateImage压根没用到,你应该把不起作用的代码贴出来,把能作用的代码贴出来分析什么啊?


我在代码里有问题的地方都提了问题。。。。做了文字说明
问题在于如果UpdateImage里如果不对bitmap进行重新new,就显示不出后来画的图形

#15




引用 12 楼 qldsrx 的回复:
你上面给的代码里面CreateImage和UpdateImage压根没用到,你应该把不起作用的代码贴出来,把能作用的代码贴出来分析什么啊?


你看下我贴出来的代码,至于receive()方法执行,是在一个按钮的事件里,我可以肯定它被执行了

#16



private Bitmap bitmap;
private Graphics graph;
private Pen penWave = new Pen(Color.Lime);
private Pen penGrid = new Pen(Color.Gray);
 
 protected override void OnLoad(EventArgs e)
{
            //打开双缓冲,防止闪烁
            DoubleBuffered = true;
            canvas_height = base.ClientSize.Height;
            canvas_width = base.ClientSize.Width;
           CreateImage();
            DrawGrids(ref graph);
            this.BackgroundImage = bitmap;
 }
 
protected override void OnResize(EventArgs e)
 {
            canvas_height = base.ClientSize.Height;
            canvas_width = base.ClientSize.Width;
           
            this.Refresh();
        
}
 
 private void DrawGrids()
{//画网格
  //pos,canvas_height都是变量,不重复贴了,不影响整体
   graph.DrawLine(penGrid, pos, 0, pos, canvas_height);//基本上都是这种代码,不重复贴,没做别的处理
}
 
private void DrawWave()
{
  graph.DrawLine(penWave,0,2,20,50);//基本上都是这种,两点之间连线的,不重复贴代码了
}
 
//调用的时候,会先调用这个CreateImage()方法,再调用UpdateImage
//CreateImage只调用一次,UpdateImage会调用多次
public void CreateImage()

  bitmap=new Bitmap(this.Width,this.Height);
   graph=new Graphics.FromImage(bitmap);
}
 
 
 public void UpdateImage()
 {//经过反复试验,在这个方法里必须要对bitmap重新new,否则看不到两点间的连线
        
       
            DrawDot();
            this.Refresh();
}


改成酱紫应该没问题了。

#17


设定背景颜色的时候,重新new一下就好了。
原因就出在this.BackgroundImage和graph使用了同一个bitmap对象。
        public void UpdateImage()
        {//经过反复试验,在这个方法里必须要对bitmap重新new,否则看不到两点间的连线
            DrawWave(ref graph);
             this.BackgroundImage = new Bitmap(bitmap);
        }

#18


引用 17 楼 u011303459 的回复:
设定背景颜色的时候,重新new一下就好了。
原因就出在this.BackgroundImage和graph使用了同一个bitmap对象。
        public void UpdateImage()
        {//经过反复试验,在这个方法里必须要对bitmap重新new,否则看不到两点间的连线
            DrawWave(ref graph);
             this.BackgroundImage = new Bitmap(bitmap);
        }

谢谢你的解答,你的方法可行。
是因为当Bitmap作为背景图像的时候,再对这个Bitmap进行操作,等到更新之前无法显示吗?yuwenge提供的办法也行,要Reflesh才能显示图形
请教下这是为什么呢,为啥this.BackgroundImage和graph使用了同一个bitmap对象,然后再重新设置BackgroundImage的时候需要重新new呢

#19


引用 16 楼 yuwenge 的回复:

private Bitmap bitmap;
private Graphics graph;
private Pen penWave = new Pen(Color.Lime);
private Pen penGrid = new Pen(Color.Gray);
 
 protected override void OnLoad(EventArgs e)
{
            //打开双缓冲,防止闪烁
            DoubleBuffered = true;
            canvas_height = base.ClientSize.Height;
            canvas_width = base.ClientSize.Width;
           CreateImage();
            DrawGrids(ref graph);
            this.BackgroundImage = bitmap;
 }
 
protected override void OnResize(EventArgs e)
 {
            canvas_height = base.ClientSize.Height;
            canvas_width = base.ClientSize.Width;
           
            this.Refresh();
        
}
 
 private void DrawGrids()
{//画网格
  //pos,canvas_height都是变量,不重复贴了,不影响整体
   graph.DrawLine(penGrid, pos, 0, pos, canvas_height);//基本上都是这种代码,不重复贴,没做别的处理
}
 
private void DrawWave()
{
  graph.DrawLine(penWave,0,2,20,50);//基本上都是这种,两点之间连线的,不重复贴代码了
}
 
//调用的时候,会先调用这个CreateImage()方法,再调用UpdateImage
//CreateImage只调用一次,UpdateImage会调用多次
public void CreateImage()

  bitmap=new Bitmap(this.Width,this.Height);
   graph=new Graphics.FromImage(bitmap);
}
 
 
 public void UpdateImage()
 {//经过反复试验,在这个方法里必须要对bitmap重新new,否则看不到两点间的连线
        
       
            DrawDot();
            this.Refresh();
}


改成酱紫应该没问题了。


引用 16 楼 yuwenge 的回复:

private Bitmap bitmap;
private Graphics graph;
private Pen penWave = new Pen(Color.Lime);
private Pen penGrid = new Pen(Color.Gray);
 
 protected override void OnLoad(EventArgs e)
{
            //打开双缓冲,防止闪烁
            DoubleBuffered = true;
            canvas_height = base.ClientSize.Height;
            canvas_width = base.ClientSize.Width;
           CreateImage();
            DrawGrids(ref graph);
            this.BackgroundImage = bitmap;
 }
 
protected override void OnResize(EventArgs e)
 {
            canvas_height = base.ClientSize.Height;
            canvas_width = base.ClientSize.Width;
           
            this.Refresh();
        
}
 
 private void DrawGrids()
{//画网格
  //pos,canvas_height都是变量,不重复贴了,不影响整体
   graph.DrawLine(penGrid, pos, 0, pos, canvas_height);//基本上都是这种代码,不重复贴,没做别的处理
}
 
private void DrawWave()
{
  graph.DrawLine(penWave,0,2,20,50);//基本上都是这种,两点之间连线的,不重复贴代码了
}
 
//调用的时候,会先调用这个CreateImage()方法,再调用UpdateImage
//CreateImage只调用一次,UpdateImage会调用多次
public void CreateImage()

  bitmap=new Bitmap(this.Width,this.Height);
   graph=new Graphics.FromImage(bitmap);
}
 
 
 public void UpdateImage()
 {//经过反复试验,在这个方法里必须要对bitmap重新new,否则看不到两点间的连线
        
       
            DrawDot();
            this.Refresh();
}


改成酱紫应该没问题了。


谢谢你的解答,调用Refresh是可以显示出图像的,请教下这是为什么呢,对同一个bitmap进行操作,这个bitmap已经作为背景图像了,再次操作的时候为啥设置BackgroundImage为bitmap不管用,要调用Reflesh才行呢?

#20




引用 19 楼 leafmao 的回复:
谢谢你的解答,调用Refresh是可以显示出图像的,请教下这是为什么呢,对同一个bitmap进行操作,这个bitmap已经作为背景图像了,再次操作的时候为啥设置BackgroundImage为bitmap不管用,要调用Reflesh才行呢?

因为你的
  this.BackgroundImage = bitmap;
bitmap是一个引用类型。当你再次设置 this.BackgroundImage = bitmap;的时候,其实什么都没做,因为 this.BackgroundImage所指向的内存地址已经是bitmap了。这个时候不会自动触发窗体print事件。当你在
this.BackgroundImage = bitmap;之前加了bitmap=new Bitmap()之后,bitmap的内存地址发生了重新分配,这样
BackgroundImage发现接收的内存地址发生了变化,于是触发了窗体print事件。

最终的原因还是因为:你没有找着对象。


#21


引用 20 楼 yuwenge 的回复:
Quote: 引用 19 楼 leafmao 的回复:



谢谢你的解答,调用Refresh是可以显示出图像的,请教下这是为什么呢,对同一个bitmap进行操作,这个bitmap已经作为背景图像了,再次操作的时候为啥设置BackgroundImage为bitmap不管用,要调用Reflesh才行呢?

因为你的
  this.BackgroundImage = bitmap;
bitmap是一个引用类型。当你再次设置 this.BackgroundImage = bitmap;的时候,其实什么都没做,因为 this.BackgroundImage所指向的内存地址已经是bitmap了。这个时候不会自动触发窗体print事件。当你在
this.BackgroundImage = bitmap;之前加了bitmap=new Bitmap()之后,bitmap的内存地址发生了重新分配,这样
BackgroundImage发现接收的内存地址发生了变化,于是触发了窗体print事件。

最终的原因还是因为:你没有找着对象。




好吧,意思就是说BackgroundImage指向了bitmap的引用地址,但是再次设置BackgroundImage的时候,编译器检测到指向的内存地址不变,即使bitmap里的东西发生了变化,也不会更新UI,大致是这个意思吧?感觉有点像是编译优化,在C里可以用volatile进行修饰防止编译器优化,对Bitmap用volatile修饰,发现不管用。。。。还是得刷新才行

#22


引用 21 楼 leafmao 的回复:
Quote: 引用 20 楼 yuwenge 的回复:



Quote: 引用 19 楼 leafmao 的回复:



谢谢你的解答,调用Refresh是可以显示出图像的,请教下这是为什么呢,对同一个bitmap进行操作,这个bitmap已经作为背景图像了,再次操作的时候为啥设置BackgroundImage为bitmap不管用,要调用Reflesh才行呢?

因为你的
  this.BackgroundImage = bitmap;
bitmap是一个引用类型。当你再次设置 this.BackgroundImage = bitmap;的时候,其实什么都没做,因为 this.BackgroundImage所指向的内存地址已经是bitmap了。这个时候不会自动触发窗体print事件。当你在
this.BackgroundImage = bitmap;之前加了bitmap=new Bitmap()之后,bitmap的内存地址发生了重新分配,这样
BackgroundImage发现接收的内存地址发生了变化,于是触发了窗体print事件。

最终的原因还是因为:你没有找着对象。




好吧,意思就是说BackgroundImage指向了bitmap的引用地址,但是再次设置BackgroundImage的时候,编译器检测到指向的内存地址不变,即使bitmap里的东西发生了变化,也不会更新UI,大致是这个意思吧?感觉有点像是编译优化,在C里可以用volatile进行修饰防止编译器优化,对Bitmap用volatile修饰,发现不管用。。。。还是得刷新才行

就是这个意思,因为.net中认为bitmap是一个非托管对象,所以不管是不是优化,都不会对bitmap进行检查。

另外你的代码里面 ref Graphics也是没有意义的,因为Graphics本身就是一个引用对象,不需要ref也是ref了。

#23


private void updateImage()
{
   graph.DrawLine(pen,0,2,20,50);
   this.BackgroundImage=bitmap;
}

改为:
private void updateImage()
{
   graph.DrawLine(pen,0,2,20,50);
   this.BackgroundImage=bitmap;
   OnBackgroundImageChanged(new EventArgs());
}

控件的BackgroundImage属性在设置值的时候,有一段判断语句:
if(this.BackgroundImage != value)
因此对于第二次的this.BackgroundImage=bitmap;操作是没有任何动作的。不过你可以人为地引发那个事件。

#24


引用 22 楼 yuwenge 的回复:
就是这个意思,因为.net中认为bitmap是一个非托管对象,所以不管是不是优化,都不会对bitmap进行检查。

另外你的代码里面 ref Graphics也是没有意义的,因为Graphics本身就是一个引用对象,不需要ref也是ref了。


感谢,结贴了

#25


引用 23 楼 qldsrx 的回复:
private void updateImage()
{
   graph.DrawLine(pen,0,2,20,50);
   this.BackgroundImage=bitmap;
}

改为:
private void updateImage()
{
   graph.DrawLine(pen,0,2,20,50);
   this.BackgroundImage=bitmap;
   OnBackgroundImageChanged(new EventArgs());
}

控件的BackgroundImage属性在设置值的时候,有一段判断语句:
if(this.BackgroundImage != value)
因此对于第二次的this.BackgroundImage=bitmap;操作是没有任何动作的。不过你可以人为地引发那个事件。


原来是这样,谢谢!

推荐阅读
  • 还是那个只能打开并显示任意的系统磁盘上的24位真彩bmp文件的问题,常规方法咱看不懂,什么文件头,什么信息头,什么句柄,什么参数表,一看头就晕.虽说CBitmap类只能显示VCMFC所生成的应用程序 ... [详细]
  • http:wiki.lazarus.freepascal.orgBGRABitmap_tutorial_1bgrabitmap是一套用来修改和创建图像的单元,可以使用alpha通道,直接 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • 本文介绍了OpenStack的逻辑概念以及其构成简介,包括了软件开源项目、基础设施资源管理平台、三大核心组件等内容。同时还介绍了Horizon(UI模块)等相关信息。 ... [详细]
  • 本文讨论了在VMWARE5.1的虚拟服务器Windows Server 2008R2上安装oracle 10g客户端时出现的问题,并提供了解决方法。错误日志显示了异常访问违例,通过分析日志中的问题帧,找到了解决问题的线索。文章详细介绍了解决方法,帮助读者顺利安装oracle 10g客户端。 ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • Android中Bitmap与Drawable的区别有哪些?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更 ... [详细]
  • 从具有确切位置的两个imageView创建位图 - Create bitmap from two imageView with exact location
    MyTarget:Getonephotofromcameraorgallery,thensetittoimageViewA从相机或图库中获取一张照片,然后将其设置 ... [详细]
  • 请高手解答:GDI+BitMapnew成功,GetLastStatus返回无效。delete指针崩溃,求解答代码如下:Bitmap*pBitmap::newBitma ... [详细]
  • 最近要做一个为视频设置封面的功能,这里展示一下简单的demo。demo效果这里直接将选取的视频某一时间的bitmap显示在视频下方。上面是视频,下面是所获取那一帧的截图。具体代码 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • Oracle seg,V$TEMPSEG_USAGE与Oracle排序的关系及使用方法
    本文介绍了Oracle seg,V$TEMPSEG_USAGE与Oracle排序之间的关系,V$TEMPSEG_USAGE是V_$SORT_USAGE的同义词,通过查询dba_objects和dba_synonyms视图可以了解到它们的详细信息。同时,还探讨了V$TEMPSEG_USAGE的使用方法。 ... [详细]
  • 在C#中,使用关键字abstract来定义抽象类和抽象方法。抽象类是一种不能被实例化的类,它只提供部分实现,但可以被其他类继承并创建实例。抽象类可以用于类、方法、属性、索引器和事件。在一个类声明中使用abstract表示该类倾向于作为其他类的基类成员被标识为抽象,或者被包含在一个抽象类中,必须由其派生类实现。本文介绍了C#中抽象类和抽象方法的基础知识,并提供了一个示例代码。 ... [详细]
author-avatar
细野本尊
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有