基于托管的C++在一定程度上去除了Native C++语法的复杂性,并且提供了灵活多变的代码组织方式,以下就以一个进行数据库CRUD的小程序来管窥一下C++,我写的这个小程序其实是利用了C#WinForm编程的功力。

先看下软件运行截图吧:

(图1,当加载大数据量的数据时,界面会采用异步方式加载,并给用户提示,涉及到异步调用)

(图2,数据加载完成,会给用户提示)

(图3,更新操作的界面,更新完毕会自动刷新主窗体,涉及到委托回调)

首先来说明连接数据库问题,这里以Sybase为例:

对于Sybase来说,.net没有提供专门的类来操作,所以需要用到ODBC来操作。具体的操作步骤就是在系统DSN中创建一个数据库连接串,然后通过如下的代码来进行操作:

System::String^ connStr = "Driver={Sybase ODBC Driver ASE 12.0};Srvr=MYTestDB;database=TESTDB;uid=sa;pwd=*****;";

然后利用System::Data::Odbc命名空间下的一些方法类来进行操作。

这里我们先来创建数据库,并循环向其中添加1W条数据记录:

create table myTemp

(

userId int identity,

userName varchar(30),

userPass varchar(30),

userAddress varchar(500),

userSex bit,

userPhone varchar(13),

regDate datetime,

)

lock allpages

with identity_gap = 1 on 'default'



set IDENTITY_INSERT myTemp off

set IDENTITY_UPDATE myTemp off



insert into myTemp ( userId,userName,userPass,userAddress,userSex,userPhone,regDate)

values(1,'scy','2511','china, shanghai',1,'13101993996',getdate())



exec createDefaultObjectPermissions @objectName='myTemp'



declare @count int

select @count = 10000

begin

while @count > 0

insert into myTemp ( userName,userPass,userAddress,userSex,userPhone,regDate) values('scy','2511','china, shanghai',1,'13101993996',getdate())

select @count = @count - 1

end

先看查询数据库的代码:

System::String^ querySQL = "select * from myTemp";
System::String^ connStr = "Driver={Sybase ODBC Driver ASE 12.0};Srvr=MYTestDB;database=TESTDB;uid=sa;pwd=*****;";
          System::Data::Odbc::OdbcConnection^ conn = gcnew System::Data::Odbc::OdbcConnection(connStr);
conn->Open();
System::Data::Odbc::OdbcDataAdapter^ oda = gcnew System::Data::Odbc::OdbcDataAdapter(querySQL,conn);
System::Data::DataSet^ ds =gcnew System::Data::DataSet();
try
{
oda->Fill(ds,"trade");
}
catch(System::Exception^ ex)
{
MessageBox::Show(ex->Message,"Notification Error",MessageBoxButtons::OKCancel,MessageBoxIcon::Error);
}
conn->Close();

通过将查询的代码填充到DataSet容器中,可以很方便的用来进行离线数据的操作。

需要注意的是,如果是.net自身提供的静态类,需要用::(双冒号)来进行操作,如果想自动释放不用的对象,就在申明的时候加上^符号,加上了^符号以后,访问其方法得用->符号。

上面的这段代码,是查询数据库的,如果数据库记录非常多,那么当程序加载的时候,肯定会非常慢,可能会阻塞UI显示达数十秒之久,这个在用户体验上面是非常不友好的,如果解决这个问题呢?当然得用到异步加载功能,其实在Visual C++中实现异步加载和在C#中实现,没有什么区别:

首先,我们的耗时的代码在上面,我们把他写到了一个BindData()的方法里面。然后我们声明一个委托,以便能够对这个方式实现异步调用:

delegate void BindDelegate();

这样,我们就可以通过这个BindDelegate委托的BeginInvoke方法来实现,具体代码如下:

 BindDelegate^ bindDelegate = gcnew BindDelegate(this,&CPlusApp::Form1::BindData);
IAsyncResult^ result = bindDelegate->BeginInvoke(gcnew System::AsyncCallback(this,&CPlusApp::Form1::CallBack),bindDelegate);
toolStripStatusLabel1->Text="Loading the Data to dataGridView now, pls wait...";

当委托在加载的时候,用户界面会出现提示“Loading the Data to dataGridView now, pls wait... “,那么当加载完毕以后,我们该怎么处理呢?我们首先应该还原委托对象,然后获取返回值,由于这里的BindData()方法返回空值,我们无需过多的业务处理,代码如下:

private: System::Void CallBack(System::IAsyncResult^ iar)
{
BindDelegate^ asyncResult = (BindDelegate^)iar->AsyncState;
asyncResult->EndInvoke(iar);
toolStripStatusLabel1->Text="Loading Complete...";
}

当然,写到这里,只能说明数据已经全部加载到了DataSet中,如何将数据集中的数据绑定到数据列表控件dataGridView1上呢?

说到这里,就出现了一个问题,如果直接绑定的话,势必需要跨越线程操作(从异步处理线程跨越到界面线程中),会出现错误提示。这里就需要我们通过判断dataGridView1控件是否需要跨线程,来通过委托方式进行,代码如下:

private:System::Void BindGridViewCrossThreads(DataSet^ ds)
{
if(dataGridView1->InvokeRequired)
{
dataGridViewCrossThreadsDelegate^ crossThreads = gcnew dataGridViewCrossThreadsDelegate(this,&CPlusApp::Form1::BindGridViewCrossThreads);
dataGridView1->Invoke(crossThreads,ds);
}
else
{
dataGridView1->DataSource = ds->Tables["trade"];
}
}

其中dataGridViewCrossThreadsDelegate委托定义如下,需要与函数Void BindGridViewCrossThreads(DataSet^ ds)参数数目、类型以及返回方式保持一致:

delegate void dataGridViewCrossThreadsDelegate(DataSet^ ds); // used for crossing threads

 

好了,做到这里,我们的界面已经能够正常的显示了,并且在加载数据和加载完成的时候,均有用户提示。

下面是添加和更新操作:

     
System::String^ userName = textBox1->Text;
System::String^ userPass = textBox2->Text;
System::String^ userAddr = textBox3->Text;
System::String^ userPhone = textBox5->Text;
int sex = rbtnMale->Checked?1:0;

System::String^ connStr = "Driver={Sybase ODBC Driver ASE 12.0};Srvr=MYTestDB;database=TESTDB;uid=sa;pwd=*****;";
System::Data::Odbc::OdbcConnection^ conn = gcnew System::Data::Odbc::OdbcConnection(connStr);
conn->Open();
System::Data::Odbc::OdbcCommand^ cmd =nullptr;
if(button1->Text->Equals("Add"))
{
System::String^ insertSQL = "insert into myTemp (userName,userPass,userAddress,userSex,userPhone,regDate) values('"+userName+"','"+userPass+"','"+userAddr+"',"+sex+",'"+userPhone+"',getdate())";
cmd = gcnew System::Data::Odbc::OdbcCommand(insertSQL,conn);
}
else if(button1->Text->Equals("Update"))
{
if(userId == 0) return ;
System::String^ insertSQL = "update myTemp set userName = '"+userName+"',userPass = '"+userPass+"',userAddress = '"+userAddr+"',userSex = "+sex+",userPhone = '"+userPhone+"' where userId = "+userId;
cmd = gcnew System::Data::Odbc::OdbcCommand(insertSQL,conn);
}

int result = cmd->ExecuteNonQuery();
if(result > 0)
{
MessageBox::Show(""+button1->Text+" Successfully!","Success",System::Windows::Forms::MessageBoxButtons::OK,System::Windows::Forms::MessageBoxIcon::Information);
}
else
{
MessageBox::Show(""+button1->Text+" Fail,Please check!","Fail",System::Windows::Forms::MessageBoxButtons::OK,System::Windows::Forms::MessageBoxIcon::Error);
return;
}
conn->Close();
TransEvent();
this->Close();

注意这里的TransEvent()实现了委托回调,目的是添加完毕后,自动刷新主窗体数据。

好了,暂时介绍到这里,希望在以后项目中能够提供一定的指导作用。

代码下载:https://files.cnblogs.com/scy251147/CPlusApp.zip

posted on 2011-11-29 19:01  程序诗人  阅读(1530)  评论(0编辑  收藏  举报