文档章节

.Net开发笔记(十九) 创建一个可以可视化设计的对象

IT周见智
 IT周见智
发布于 2015/06/05 17:17
字数 4211
阅读 4
收藏 0
点赞 0
评论 0

阅读本篇博客之前需要了解VS窗体设计器的工作原理,详细可参见本系列博客(十)(十一)(十二)。必须需要知道的一条结论就是:处于窗体设计器(Form Designer)中的任何组件(包含控件,下同),都是实际存在的一个实例。也就是说,拖进去的button1,其实就是实例化一个Button控件。

通常编码中,我们在使用一个类型对象时,通过以下方式:

1 Car c = new Car(); //实例化对象
2 c.Type = “标致308”; //设置属性
3 c.Color = Color.Black; //设置属性
4 c. InspectionInformation = new Inspection(“2013-11-12”,”张三”,”RS678T”);//设置属性
5 c.SomethingHappened+=new SomethingHappenedEventHandler(c_SomethingHappened);//注册事件
6 //以上为对象的初始化  以下开始使用c对象
7 //
View Code

如上所示,我们在使用Car类时,是通过new的方式来创建一个实例,然后给它初始化一些信息,这些所有操作都是通过我们手动来编写代码实现的。

我们注意到,在设计UI界面的时候,窗体中的所有控件、组件都是可以通过“属性窗体”来编辑的,也就是说,界面上这些元素的初始化不需要我们手动编写代码,完全可以通过点点鼠标,按按键盘就可以做到。我们可以总结出来设计器可以帮我们做以下工作:

  • 实例化对象

    没错,不用你手动new对象了,设计器帮你来完成。

  • 编辑属性

    选定一个组件,在属性窗体中编辑它的属性,跟你通过编写“实例.属性=属性值”是一样的效果。

  • 注册事件

    选定一个组件,在属性窗体的事件选项卡,双击事件空白处,自动注册事件。

窗体设计器不需要你手动编写一行代码,对象的实例化、属性编辑、事件注册全部搞定,也就是说,窗体设计器能够可视化设计一些对象。至于哪些类型的对象可以通过窗体设计器来进行可视化设计,请参见本系列(十、十一、十二),我在这里直接给出结果:窗体设计器能够可视化设计实现了IComponent接口类型的对象也就是说,如果你定义了一个类型A,恰好它实现了IComponent接口(直接或者间接),那么你就完全可以通过窗体设计器来可视化设计A类型的对象。

由此可以看出,创建一个可以可视化设计的对象并不难,只要我们的类型实现了IComponent接口就行(官方称这种类型为组件)。我们再来看一下,窗体设计器初始化出来的对象,跟我们自己手动编写代码初始化的对象有哪些相同点和不同点:

不同点:

  • 前者更直观简单,隐藏的东西太多,后者复杂,但是清楚内部过程。
  • 前者对象的初始化,在程序一启动就开始,不能人工控制其时机,具体是在Form1的构造方法中的InitializeComponent()中进行,后者就更灵活,需要的时候编写代码就可以。
  • 前者初始化出来的对象几乎都跟UI界面有关(这个很容易就能想到,窗体设计器肯定设计跟窗体界面有关的东西),而后者没有这个原则,不管是什么对象,都是可以的。

相同点:

  • 都是初始化一个对象。
  • 都有代码产生,前者产生的代码在InitializeComponent()中,后者为人工编写。

我们应该清楚,程序最终都是要经过将源代码编译成可执行文件之后才能运行的,所以源代码是一切根本,没有源代码,其他的都是白扯。

综上所有之述,我们可以手动编写代码来初始化任何对象,我们可以通过窗体设计器来初始化实现了IComponent接口的类型对象。

好了知道怎样才能创建一个可以可视化设计的对象之后,我们来创建一个试一下,定义一个类型MyComponent,使其继承自Component:

 1 /// <summary>
 2     /// 可被 可视化设计的类,该类默认只包含属性
 3     /// </summary>
 4     public partial class MyComponent : Component
 5     {
 6         public MyComponent()
 7         {
 8             InitializeComponent();
 9         }
10 
11         public MyComponent(IContainer container)
12         {
13             container.Add(this);
14             InitializeComponent();
15         }
16         /// <summary>
17         /// 字符串属性 使用默认属性编辑器
18         /// </summary>
19         public string StringProperty
20         {
21             set;
22             get;
23         }
24         /// <summary>
25         /// 颜色属性  使用默认属性编辑器
26         /// </summary>
27         public Color ColorProperty
28         {
29             set;
30             get;
31         }
32         /// <summary>
33         /// 自定义类型属性 使用下拉列表编辑器
34         /// </summary>
35         [Editor(typeof(MyTypeEditor1),typeof(UITypeEditor)),DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
36         public MyType MyTypeProperty1
37         {
38             set;
39             get;
40         }
41         /// <summary>
42         /// 自定义类型属性 使用弹出对话框编辑器
43         /// </summary>
44         [Editor(typeof(MyTypeEditor2),typeof(UITypeEditor)),DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
45         public MyType MyTypeProperty2
46         {
47             set;
48             get;
49         }
50         /// <summary>
51         /// 控件属性 可以在设计器中选择已经存在的控件
52         /// </summary>
53         [Editor(typeof(ControlEditor),typeof(UITypeEditor))]
54         public Control ControlProperty
55         {
56             set;
57             get;
58         }
59         /// <summary>
60         /// ImageList类型属性 
61         /// </summary>
62         [Editor(typeof(ImageListEditor),typeof(UITypeEditor))]
63         public ImageList ImageListProperty
64         {
65             get;
66             set;
67         }
68     }
View Code

如上代码所示,该类型只包含了几个公共属性,没有其他内容。此类型对象就可以通过窗体设计器来设计了,也就是说,从工具箱中向设计器中拖放MyComponent类型之后,窗体设计器自动会实例化一个MyComponent对象,并且你可以通过属性窗体来编辑该对象的属性:

1)StringProperty

     String类型属性,直接可以在属性窗体中输入。

2)ColorProperty

     Color类型属性,属于.NET自带类型,所以有默认的属性编辑器,如下图:

图1

3)MyTypeProperty1

    自定义类型属性,需要我们自己定义一个属性编辑器Editor(typeof(MyTypeEditor1),typeof(UITypeEditor))。

4)MyTypeProperty2

    自定义类型属性,需要我们自己定义一个属性编辑器Editor(typeof(MyTypeEditor2),typeof(UITypeEditor))。

5)ControlProperty

    Control类型属性,我们可以将设计器中已经存在的Control赋值给该属性,指定了属性编辑器Editor(typeof(ControlEditor),typeof(UITypeEditor))。

6)ImageListProperty

    ImageList属性,这个就是我们常见的一些控件(比如TabControl)含有ImageList属性,点击右方的小三角形,就可以列出窗体设计器中已经存在的ImageList,供你选择。指定了属性编辑器Editor(typeof(ImageListEditor),typeof(UITypeEditor))。

也就是说,当我们在窗体设计器中设计一个对象的时候,如果该对象包含一些特殊(非.NET默认自带类型)类型属性时,我们需要为该属性提供一个“属性编辑器”。

以下就分别为每个属性对应的属性编辑器了(假设诸位看官都知道了UITypeEditor的作用,不知道可以查一下):

1)MyTypeProperty1属性

 1 class MyTypeEditor1 : UITypeEditor
 2     {
 3         public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
 4         {
 5             return UITypeEditorEditStyle.DropDown; //下拉列表 在下拉列表中输入MyType属性值
 6         }
 7         public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
 8         {
 9             usrMyTypeEditor usrme = new usrMyTypeEditor(); //下拉列表
10             usrme.EditedValue = value as MyType; //初始化下拉列表框 value为旧值
11 
12             IWindowsFormsEditorService ie = provider.GetService(typeof(IWindowsFormsEditorService)) as IWindowsFormsEditorService;
13             if (ie != null)
14             {
15                 ie.DropDownControl(usrme);  //显示下拉列表
16                 return usrme.EditedValue; //返回编辑后的值
17             }
18             else
19             {
20                 return value;
21             }
22         }
23 }
View Code

鼠标点击MyTypeProperty1属性右侧的小三角形,出现一个下拉列表框。如下图:

图2

2)MyTypeProperty2属性

 1 class MyTypeEditor2 : UITypeEditor
 2     {
 3         public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
 4         {
 5             return UITypeEditorEditStyle.Modal; //模式对话框 在弹出对话框中输入MyType属性值
 6         }
 7         public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
 8         {
 9             using (frmMyTypeEditorForm frmmef = new frmMyTypeEditorForm())
10             {
11                 frmmef.EditedValue = value as MyType; //初始化对话框
12                 IWindowsFormsEditorService ie = provider.GetService(typeof(IWindowsFormsEditorService)) as IWindowsFormsEditorService;
13                 if (ie != null)
14                 {
15                     if (ie.ShowDialog(frmmef) == DialogResult.OK) //显示模式对话框
16                     {
17                         return frmmef.EditedValue; //返回编辑后的值
18                     }
19                     else
20                     {
21                         return value; //返回旧值
22                     }
23                     
24                 }
25                 else
26                 {
27                     return value;
28                 }
29             }
30         }
31 }
View Code

鼠标点击MyTypeProperty2右侧的小三角形,弹出一个对话框。

图3

3)ControlProperty属性

 1 class ControlEditor : UITypeEditor
 2     {
 3         IWindowsFormsEditorService ie = null;
 4 
 5         public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
 6         {
 7             return UITypeEditorEditStyle.DropDown; //下拉列表 在下拉列表中选择一个(设计器中已经存在)控件
 8         }
 9         public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
10         {
11             ListBox li = new ListBox(); //下拉列表
12             li.Click += new EventHandler(li_Click);
13             List<Control> liCo=new List<Control>(); //下拉列表每一项对应的控件值
14             foreach(Component c in context.Container.Components) //查找窗体设计器中的每一个组件
15             {
16                 if (c is Control && !(c is Form)) //若是控件 不是窗体
17                 {
18                     li.Items.Add((c as Control).Name); //将控件名称写入listbox
19                     if (value as Control == c as Control)
20                     {
21                         li.SelectedIndex = li.Items.Count - 1; //选中原来值
22                     }
23                     liCo.Add(c as Control); //对应控件值写入list
24                 }
25             }
26             ie = provider.GetService(typeof(IWindowsFormsEditorService)) as IWindowsFormsEditorService;
27             if (ie != null)
28             {
29                 ie.DropDownControl(li);
30                 if (li.SelectedIndex > -1)
31                     return liCo[li.SelectedIndex];
32                 else
33                     return value;
34             }
35             else
36             {
37                 return value;
38             }
39         }
40 
41         void li_Click(object sender, EventArgs e)
42         {
43             if (ie != null)
44             {
45                 ie.CloseDropDown();
46             }
47         }
48 }
View Code

鼠标点击ControlProperty右侧的小三角形,出现下拉列表框,列表中显示的都是窗体设计器中已经存在的Control。

图4

4)ImageListProperty属性

 1 class ImageListEditor : UITypeEditor
 2     {
 3         IWindowsFormsEditorService ie = null;
 4 
 5         public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
 6         {
 7             return UITypeEditorEditStyle.DropDown; //下拉列表 在下拉列表中选择一个(设计器中已经存在)ImageList组件
 8         }
 9         public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
10         {
11             ListBox li = new ListBox(); //下拉列表
12             li.Items.Add("");
13             li.Click += new EventHandler(li_Click);
14             List<ImageList> liCo = new List<ImageList>(); //下拉列表每一项对应的ImageList值
15             foreach (Component c in context.Container.Components) //查找窗体设计器中的每一个组件
16             {
17                 if (c is ImageList) //若是ImageList
18                 {
19                     li.Items.Add((c as ImageList).ToString().Split(' ')[0]); //将ImageList名称写入listbox
20                     if (value as ImageList == c as ImageList)
21                     {
22                         li.SelectedIndex = li.Items.Count - 1; //选中原来值
23                     }
24                     liCo.Add(c as ImageList); //对应ImageList写入list
25                 }
26             }
27             ie = provider.GetService(typeof(IWindowsFormsEditorService)) as IWindowsFormsEditorService;
28             if (ie != null)
29             {
30                 ie.DropDownControl(li);
31                 if (li.SelectedIndex == 0)  //
32                     return null;
33                 else if (li.SelectedIndex > 0)
34                     return liCo[li.SelectedIndex-1];
35                 else
36                     return value; 
37             }
38             else
39             {
40                 return value;
41             }
42         }
43 
44         void li_Click(object sender, EventArgs e)
45         {
46             if (ie != null)
47             {
48                 ie.CloseDropDown();
49             }
50         }
51 }
View Code

鼠标点击ImageListProperty右侧的小三角形,出现下拉列表,列表中显示窗体设计器中已经存在的ImageList。

图5

本篇博客介绍了怎么创建一个可以可视化设计的对象(准确来讲,应该是怎样创建一个可以可视化设计其对象的类型),首先让类型(间接或者直接)实现IComponent接口(不要问我为什么,请参见前面的博客),然后为类型的某些特殊属性创建对应的属性编辑器,就这么简单。类型创建者工作量大一点,但是类型使用者更方便一些,Demo中MyComponent类型就是最后的核心成果,供使用者使用,使用者不需要知道其他的类似属性编辑器之类的东西,在他们看来,这些相当于没有。

注意使用范围,并不是任何时候都可以使用,最好参见前面提到的“相同点”和“不同点”那里。另外,本篇博客中设计到的知识点很多,像provider.GetService(typeof(IWindowsFormsEditorService))、foreach(Component c in context.Container.Components)这些需要和设计器打交道的地方我几乎没有详细说到过,原因是这些太复杂了,要是细说的话,得说一大篓子,而且不是本篇文章的重点。

源码下载地址:http://files.cnblogs.com/xiaozhi_5638/PropertyEditorInDesigner.rar

注意不要试图运行源代码,没有任何效果,只能在设计器中看到效果。希望有帮助。

Update (2013-12-09)

上面结束提到了“支持可视化设计对象”的使用场合,建议如果该类型跟UI界面有关联(需要与界面其他元素交互),并且对象的实例化不需要人工控制其时机,那么可以让该类型支持可视化设计(也就是实现IComponent接口)。注意这里的建议,也就是说不是强制性的,你完全可以定义一个People类,让其实现IComponent接口,这合法!结果就是,从工具栏中拖放一个People到窗体设计器中,它会自动帮你实例化了一个People对象实例,并且你也能通过属性窗体编辑它的属性值(这些后台都能自动生成对应代码)。另外,经发现,具备某一些功能的类型,虽然跟界面无交互,但是它还是支持可视化设计,比如BackgroundWorker组件,拖一个BackgroundWorker到窗体设计器中去,设计器会自动帮你实例化一个Backgroundworker对象(生成对应代码),然后你可以通过属性窗体编辑它的属性值(生成对应代码),这跟你自己手动new BackgroundWorker没有区别,你还是可以在其他地方一样使用该backgroundworke对象,至少你目前看起来没区别。

为了更好的说明通过窗体设计器设计出来的对象,跟我们手动编写代码搞出来的对象有什么相同和不同点,我们先来分析一下Form1.Designer.cs中的代码:

我们注意到,我们在设计器中的每一步操作,对应生成的代码都在InitializeComponent()方法中,它像是word录制宏的功能,你在word中的每一个操作,都可以生成对应的VBA代码,也就是说,窗体设计器从实例化对象,到编辑属性,再到注册事件等等等,都有代码帮我们记录下这些操作。我们再看一下实例化对象的代码:

1 this.myComponent1 = new PropertyEditorInDesigner.MyComponent(this.components);
View Code

没错,任何一个实例化出来的对象,都给它传递了this.components容器,我们再看MyComponent的构造方法是这样的:

1  public MyComponent(IContainer container)
2         {
3             container.Add(this);
4             InitializeComponent();
5         }
View Code

也就是说,对象实例化的时候,都将该对象放进了一个components的容器,这个容器专门用来存放由窗体设计器实例化出来的对象(控件除外)。这就像一个大的容器,专门来存放这些小个体。接下来我们再来看一下Form1.Designer.cs中的dispose方法:

1   protected override void Dispose(bool disposing)
2         {
3             if (disposing && (components != null))
4             {
5                 components.Dispose();
6             }
7             base.Dispose(disposing);
8         }
View Code

我们可以发现,在Form1对象Dispose的时候,它将components中的所有个体都Dispose掉了。到此,我们可以总结出来一条:由窗体设计器设计出来的对象由父窗体(父控件)的InitializeComponents方法统一初始化,由父窗体(父控件)的Dispose()方法统一释放资源。我们来看一张Form1运行结构图

图(更新)1

由此可以看出,由窗体设计器设计出来的对象,它们的生命周期以及存放结构都比较有规律,这个就是窗体设计器设计出来对象的好处。

我们在往窗体设计器中拖放组件时,就是往Form1类型中添加新的成员对象,跟我们定义一个类型,向里面添加成员变量一个意思,只是前者更直观,添加的每一个成员对象,在UI设计器中都能看见与它对应的一个对象实例,只要我们改变了这个对象实例的属性,就能马上看见设计器中的对象实例效果,紧接着后台生成代码;而后者就没有这么直观了,只能手写代码,而且还看不见效果。

图(更新)2

如上图,窗体设计器中某一个对象实例属性更新之后,马上就能在设计器中看见效果(因为它是实实在在存在于堆中的对象),接着InitializeComponents中的代码就会更新,注意,窗体设计器中的myComponent1对象实例跟.cs代码中的myComponent1变量不是一个东西,他们只有一种映射关系。我们最终要的是.cs中的代码文件,而不是我们看见的窗体设计器中的图像,后者只是起到一个可视化的效果,最终一文不值。这个就像photoshop作图一样,最终保存到硬盘的图片文件才是最重要的,作图过程中作图区域显示的东西没有价值。窗体设计器隐藏得越多,我们知道得越少。

© 著作权归作者所有

共有 人打赏支持
IT周见智

IT周见智

粉丝 10
博文 61
码字总数 185891
作品 0
西青
19个免费的UI界面设计工具及资源

开源中国社区刚发布了一篇《21个免费的UI界面设计工具、资源及网站》,介绍了免费的Web UI、移动UI、线框工具等。作为该文章的续篇,本文再介绍19个免费UI设计工具及资源,目的同样是帮助你通...

老枪 ⋅ 2011/03/24 ⋅ 9

java 常用23中设计模式

Java总共有23中常见的设计模式。它们主要可以分为3类,创建型,结构型以及行为型。 创建型的设计模式有: 一、Singleton,单例模式:保证一个类只有一个实例,并提供一个唯一的全局访问点。 ...

菜头_ ⋅ 2016/01/19 ⋅ 1

MyBatis多对多保存示例——MyBatis学习笔记之十七

前几天有网友问到MyBatis多对多的问题,不过愧对网友厚爱的是,最近一直忙,直到现在才有时间处理此事。今天就先写一个多对多保存的示例,算是对这位网友的初步回应,以后会有更多相关的博文...

NashMaster2011 ⋅ 2013/08/11 ⋅ 0

一文带你玩转机器学习和深度学习

  俗话说的好:工欲善其事,必先利其器!一款好的工具可以让你事半功倍,尤其是在大数据时代,更需要强有力的工具通过使数据有意义的方式实现数据可视化,还有数据的可交互性;我们还需要跨学...

深度学习 ⋅ 01/06 ⋅ 0

手把手带你玩转机器学习和深度学习

  俗话说的好:工欲善其事,必先利其器!一款好的工具可以让你事半功倍,尤其是在大数据时代,更需要强有力的工具通过使数据有意义的方式实现数据可视化,还有数据的可交互性;我们还需要跨学...

中国机器人 ⋅ 01/04 ⋅ 0

MySQL Workbench 6 正式版 (6.0.6) 发布

MySQL Workbench 是专为数据库架构师、开发人员和 DBA 打造的一个统一的可视化工具。MySQL Workbench 提供了数据建模工具、SQL 开发工具和全面的管理工具(包括服务器配置、用户管理、备份等...

oschina ⋅ 2013/08/28 ⋅ 13

php 常用的设计模式

红色标记的比较常用。 设计模式主要分三个类型:创建型、结构型和行为型。 其中创建型有: 一、Singleton,单例模式:保证一个类只有一个实例,并提供一个访问它的全局访问点 二、Abstract Fa...

大灰狼wow ⋅ 2014/05/17 ⋅ 1

从0行代码开发房卡棋牌(麻将等)系列教程

本课程从0开始讲解如何使用cocoscreator和nodejs打造商业级别的×××。课程由简入深的进行,课程学习完成后,基本具备了开发任意×××的能力。除此之外,最具核心竞争力的是,本系统支持人...

李鸡蛋 ⋅ 2017/12/28 ⋅ 0

【白话设计模式十九】状态模式(State)

白话设计模式 工厂模式 单例模式 【白话设计模式一】简单工厂模式(Simple Factory) 【白话设计模式二】外观模式(Facade) 【白话设计模式三】适配器模式(Adapter) 【白话设计模式四】单例模式...

陶邦仁 ⋅ 2016/04/11 ⋅ 0

hjimce算法类博文目录 个人博客:http://blog.csdn.net/hjimce 个人qq:1393852684 知乎:https://www.zhihu.com/people/huang-jin-chi-28/activities 一、深度学习 深度学习(七十)darknet...

hjimce ⋅ 2016/01/24 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

从零开始搭建Risc-v Rocket环境---(1)

为了搭建Rocke环境,我买了一个2T的移动硬盘,安装的ubuntu-16.04 LTS版。没有java8,gcc是5.4.0 joe@joe-Inspiron-7460:~$ java -version程序 'java' 已包含在下列软件包中: * default-...

whoisliang ⋅ 13分钟前 ⋅ 0

大数据学习路线(自己制定的,从零开始学习大数据)

大数据已经火了很久了,一直想了解它学习它结果没时间,过年后终于有时间了,了解了一些资料,结合我自己的情况,初步整理了一个学习路线,有问题的希望大神指点。 学习路线 Linux(shell,高并...

董黎明 ⋅ 19分钟前 ⋅ 0

systemd编写服务

一、开机启动 对于那些支持 Systemd 的软件,安装的时候,会自动在/usr/lib/systemd/system目录添加一个配置文件。 如果你想让该软件开机启动,就执行下面的命令(以httpd.service为例)。 ...

勇敢的飞石 ⋅ 21分钟前 ⋅ 0

mysql 基本sql

CREATE TABLE `BBB_build_info` ( `community_id` varchar(50) NOT NULL COMMENT '小区ID', `layer` int(11) NOT NULL COMMENT '地址层数', `id` int(11) NOT NULL COMMENT '地址id', `full_......

zaolonglei ⋅ 30分钟前 ⋅ 0

安装chrome的vue插件

参看文档:https://www.cnblogs.com/yulingjia/p/7904138.html

xiaoge2016 ⋅ 33分钟前 ⋅ 0

用SQL命令查看Mysql数据库大小

要想知道每个数据库的大小的话,步骤如下: 1、进入information_schema 数据库(存放了其他的数据库的信息) use information_schema; 2、查询所有数据的大小: select concat(round(sum(da...

源哥L ⋅ 55分钟前 ⋅ 0

两个小实验简单介绍@Scope("prototype")

实验一 首先有如下代码(其中@RestController的作用相当于@Controller+@Responsebody,可忽略) @RestController//@Scope("prototype")public class TestController { @RequestMap...

kalnkaya ⋅ 今天 ⋅ 0

php-fpm的pool&php-fpm慢执行日志&open_basedir&php-fpm进程管理

12.21 php-fpm的pool pool是PHP-fpm的资源池,如果多个站点共用一个pool,则可能造成资源池中的资源耗尽,最终访问网站时出现502。 为了解决上述问题,我们可以配置多个pool,不同的站点使用...

影夜Linux ⋅ 今天 ⋅ 0

微服务 WildFly Swarm 管理

Expose Application Metrics and Information 要公开关于我们的微服务的有用信息,我们需要做的就是将监视器模块添加到我们的pom.xml中: 这将使在管理和监视功能得到实现。从监控角度来看,...

woshixin ⋅ 今天 ⋅ 0

java连接 mongo伪集群部署遇到的坑

部署mongo伪集群 #创建mongo数据存放文件地址mkdir -p /usr/local/config1/datamkdir -p /usr/local/config2/data mkdir -p /usr/local/config3/data mkdir -p /usr/local/config1/l......

努力爬坑人 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部