您的位置:首页 > 其它

.Net项目如何在三种类型之间切换

2016-11-20 12:04 288 查看
工具为Visual Studio,语言就拿C#来说吧,其他的不知道一不一样。

三种项目类型:窗体应用程序、控制台应用程序和类库。

本文理论性不强,娱乐而已,大神别看了真的……(无贬义)

【Start】

刚开始的时候,我想要创建一个窗体应用程序,就创建个WinForm项目,我想要创建个控制台程序,就创建个ConsoleApplication,我想要创建个类库,就创建一个类库,后来,我想把类库改成控制台程序,改成窗体程序,想把窗体程序改成控制台程序,想把控制台程序改成窗体程序,甚至想把控制台程序改成运行之后既可以是窗体程序又可以是控制台程序。除去最后一条,只好重新创建个对应的项目,而最后一条简直没法实现。

首先要知道的是.Net程序的程序入口点EntryPoint(类库除外),(直(接瞎)说吧)就是一个静态的无返回值或返回int类型的名字叫Main的可以带可变长度字符串数组参数的一个方法,比如说

或者

或者

或者

等等,不管在哪个类里放着,也不管这个方法的受保护级别如何(public, protected, private, internal),并且有且只能有一个。

接下来在VS自动生成的(可执行程序)项目中找找Main函数的位置。

首先创建一个Windows窗体应用程序



打开Program.cs,发现了Main函数



已经写上了“应用程序的主入口点。”几个大字

那为啥还要看这个呢?因为如果换做WPF的话,很多人(像我一样的新手)(可能)就找不到了。

新建一个WPF项目



选择“显示所有文件”



然后随便打开App.g.cs或者App.g.i.cs,翻到最后面,就可以找到Main函数了



接下来看看控制台应用程序。

新建一个控制台应用程序项目



这个相当好找,连之前的那些提示都省了……



现在开始按部就班地实现开头所说的那些“转换”。

新建一个类库



窗体程序和控制台程序就用之前创建的那两个

后面每一步初始状态都是各项目新建完的状态

① 类库 → 控制台

给自动生成的类Class1里面加Main方法



(故意写成这样就是为了表达不管写在哪都能找到,但是有且只能有一个)

然后双击

打开(或者用其他方法打开)属性,修改输出类型



运行程序



② 类库 → WinForm

首先添加引用



然后加Main函数,加显示窗体的对应代码



修改属性里的输出类型为

,然后运行



③ 类库 → WPF

添加引用



然后加Main函数,加显示窗体的对应代码



注意这里要加上STAThreadAttribute,因为窗体应用程序只能有一个主线程(即UI线程),而控制台程序默认是多线程的,想了解真相(因为我是瞎说的/(ㄒoㄒ)/~~),就去(谷歌或MSDN或必应)搜索STAThreadAttribute和MTAThreadAttribute。

修改属性里的输出类型为

,然后运行



④ 窗体 → 控制台

首先以WinForm项目为例

直接修改Main函数



修改属性里的输出类型为

,然后运行



然后以WPF项目为例(如果使用VS自动生成的代码的话)

按之前的方法找到

,然后打开修改代码



修改属性里的输出类型为

,然后运行,结果就不贴图片了,一样的

需要注意的是如果你改完代码并保存之后,只要代码被编译过,VS就会重新输出自动生成的代码,换句话说,如果在运行之前重新编译,修改的代码会被覆盖,如果直接运行,那么修改的代码还是会被覆盖,不过会有是否覆盖的可选提示,所以会比较麻烦。

⑤ 控制台 → 窗体

和类库到窗体差不多一个意思,就不重复说明了。

⑥ 控制台 → 窗体 或 控制台

为啥会有这种情况呢?

试想有一种文件转换的单一流程,也就是选择一个文件读取然后转换为另一个格式的文件并保存,需要的参数可能有:输入文件路径,输出文件路径,转换配置参数

假设需要开发一个程序,既可以在界面上处理这个流程,也可以写个bat批处理文件进行批处理。(这里不纠结于批处理这个问题上,因为实现方式很多。)

那么这个程序就有两种情况,如果在入口函数Main中没有传任何参数进来,那么就启动界面进行处理,如果传了参数并且验证无误,就启动控制台界面来处理。

具体例子可以看看我写的这篇文章

下面补充一些细节或者说上面没提到的。

Winform程序中不加STAThreadAttribute也可以运行,但是在使用一些系统组件的时候,会抛出异常,下面用一个小例子加以说明。

新建一个简单的窗体



然后不使用STAThread,进行初始化和显示

class Class1
{
//[STAThread]
static void Main()
{
System.Windows.Forms.Application.Run(new AWindow());
}
}


之前运行过,没有问题

窗体显示后给里面加一个新的label控件

protected override void OnShown(EventArgs e)
{
base.OnShown(e);

Label lb = new Label();
lb.Text = "label1";
this.Controls.Add(lb);
}


运行,没有报错



证明WinForm中创建控件并将其加入到主窗体不需要STAThread

当点击第一个按钮时,修改窗体标题

//ProofRead
private void button1_Click(object sender, EventArgs e)
{
this.Text = DateTime.Now.ToString("hh:MM:ss.fff");
}


运行,无误



当点击第二个按钮时,弹出OpenFileDialog

//Open
private void button2_Click(object sender, EventArgs e)
{
using (OpenFileDialog dialog = new OpenFileDialog())
{
dialog.ShowDialog();
}
}


运行,报错



也就是说如果出现了这个错误提示,你就得好好想想是不是哪个地方使用了非STA的线程创建了一个需要在STA模式中创建的控件,如果是新开的线程,那么目前为止我只知道Thread支持修改ApartmentState(SetApartmentState方法),TPL(Task)中没有找到,具体用法可以在上面第⑥条结尾提到的那篇文章中找到。

同样地,如果你需要在控制台程序中调用例如OpenFileDialog和SaveFileDialog等控件,

要么自己用STA线程包装下,例如

public class STAOpenFileDialog : IDisposable
{
private OpenFileDialog dialog = new OpenFileDialog();
private DialogResult dialogResult;

public DialogResult ShowDialog()
{
Thread dialogThread = new Thread((ThreadStart)this.showDialogMethod);
dialogThread.SetApartmentState(ApartmentState.STA);
dialogThread.Start();
dialogThread.Join();
return this.dialogResult;
}

private void showDialogMethod()
{
this.dialogResult = this.dialog.ShowDialog();
}

public void Dispose()
{
if (this.dialog != null)
{
this.dialog.Dispose();
this.dialog = null;
}
}

#region OpenFileDialog Property
public string Filter
{
get { return this.dialog.Filter; }
set { this.dialog.Filter = value; }
}

public string FileName
{
get { return this.dialog.FileName; }
set { this.dialog.FileName = value; }
}

//and so on...
#endregion OpenFileDialog Property
}


要么就直接在Main函数上加STAThreadAttribute



WPF窗体程序一定要加STAThreadAttribute,否则初始化时就会抛出异常



剩下的如果有啥不清楚的,自行测试自行搜索吧(谷歌、MSDN等,不太推荐百度)

【The End】

转载请不必注明,就说是你写的
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: