Arquivo da categoria ‘C/C++’

Na linguagem de programação C++ você pode usar o objeto CRecordSet através de um driver ODBC para retornar campos de um set de dados no qual poderá ser manipulado de modo que você possa escolher qualquer um dos campos da tabela contida em seu Dataset.

C++ - Database

C++ – Database

Visual Studio

Para utilizar uma linha de dados por coluna em C++ nos bancos de dados Oracle, DB2 e MSSQL, siga os seguintes passos:

1 – Crie a tabela de exemplo e insira as linhas de dados com o script para o seu banco de dados que você encontra logo abaixo. No Visual Studio crie um design como na figura abaixo, utilizando 7 Static Text, 6 Edit Control, 1 GroupBox e um Button. Mude o ID do primeiro Static Text para IDC_Rotulo e associe as respectivas variáveis aos campos da tela. Use o código abaixo para completar seu código gerado automaticamente.

C++ - MFC - Design

C++ – MFC – Design

Exemplo:

Este programa foi escrito para retornar uma linha de dados em uma tabela e exibir os campos em caixas de textos.

SQL

Oracle

create table Funcionarios
(     

    ID_Funcionario  NUMBER(5),
    Nome            VARCHAR2(30),
    Sobrenome       VARCHAR2(70),
    Cargo           VARCHAR2(30),
    Salario         NUMBER(9,2)

);

Insert into FUNCIONARIOS values (1,'Steve','Gates','Programador',2550.56);
Insert into FUNCIONARIOS values (2,'Bill','Jobs','Diretor',5143.71);

DB2

create table Funcionarios
(     

    ID_Funcionario  INTEGER,
    Nome            VARCHAR(30),
    Sobrenome       VARCHAR(70),
    Cargo           VARCHAR(30),
    Salario         NUMERIC(9,2)

);

Insert into FUNCIONARIOS values (1,'Steve','Gates','Programador',2550.56);
Insert into FUNCIONARIOS values (2,'Bill','Jobs','Diretor',5143.71);

Mssql

create table Funcionarios
(     

    ID_Funcionario  Int,
    Nome            VARCHAR(30),
    Sobrenome       VARCHAR(70),
    Cargo           VARCHAR(30),
    Salario         Decimal(9,2)

);

Insert into FUNCIONARIOS values (1,'Steve','Gates','Programador',2550.56);
Insert into FUNCIONARIOS values (2,'Bill','Jobs','Diretor',5143.71);

C++
Arquivo .h


// CamposcppDlg.h : header file
//

#pragma once
// Inclui classe de banco de dados MFC
#include "afxdb.h"
#include "afxwin.h"

// CCamposcppDlg dialog
class CCamposcppDlg : public CDialogEx
{

public:
	CCamposcppDlg(CWnd* pParent = NULL);	// standard constructor

// Dialog Data
	enum { IDD = IDD_CAMPOSCPP_DIALOG };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV support

// Implementation
protected:
	HICON m_hIcon;

	// Generated message map functions
	virtual BOOL OnInitDialog();
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()
public:

	// Cria métodos e objetos da classe
	CDatabase db;
	void conectarDB(CString banco);

	afx_msg void OnBnClickedButton1();
	CEdit m_pesquisa;
	CEdit m_codigo;
	CEdit m_pnome;
	CEdit m_snome;
	CEdit m_cargo;
	CEdit m_salario;
};

Arquivo .cpp


// CamposcppDlg.cpp : implementation file
//

#include "stdafx.h"
#include "Camposcpp.h"
#include "CamposcppDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CCamposcppDlg dialog

CCamposcppDlg::CCamposcppDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CCamposcppDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CCamposcppDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_EDIT6, m_pesquisa);
	DDX_Control(pDX, IDC_EDIT1, m_codigo);
	DDX_Control(pDX, IDC_EDIT2, m_pnome);
	DDX_Control(pDX, IDC_EDIT3, m_snome);
	DDX_Control(pDX, IDC_EDIT4, m_cargo);
	DDX_Control(pDX, IDC_EDIT5, m_salario);
}

BEGIN_MESSAGE_MAP(CCamposcppDlg, CDialogEx)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BUTTON1, &CCamposcppDlg::OnBnClickedButton1)
END_MESSAGE_MAP()

// CCamposcppDlg message handlers

BOOL CCamposcppDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	// TODO: Add extra initialization here

	return TRUE;  // return TRUE  unless you set the focus to a control
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CCamposcppDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CCamposcppDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

void CCamposcppDlg::conectarDB(CString banco)
{
	// Cria string de conexão
	CString dns, usuario, senha;

	// Define banco de dados
	if (banco == "oracle")
	{
		dns = L"OracleXE";
		usuario = L"daberto";
		senha = L"p@55w0rd";
	}

	if (banco == "db2")
	{
		dns = L"IBMDB2";
		usuario = L"db2admin";
		senha = L"p@55w0rd";
	}

	if (banco == "mssql")
	{
		dns = L"MSSQLSERVER";
		usuario = L"devaberto";
		senha = L"p@55w0rd";
	}

	// Cria string de conexão ODBC
	CString conexao;

	// Cria string de conexão
	conexao = L"DSN=" + dns + L";UID=" + usuario + L";PWD=" + senha;

	// Abre conexão
	db.OpenEx(conexao, 0);
}

void CCamposcppDlg::OnBnClickedButton1()
{
	// Define variaveis
	CString codigo;
	CString m_campo;
	CString m_db;

	// Referincia objeto statico
	CWnd * p_rotulo;

	// Esta variavel define o banco de dados
	// oracle = Oracle Database
	// db2 = IBM DB2
	// mssql = MSSQL Server
	m_db = L"oracle";

	// Associa objeto statico
	p_rotulo = GetDlgItem(IDC_Rotulo);
	p_rotulo->SetWindowTextW(L"Database - Fields - " + m_db);

	// Conecta ao banco de dados
	conectarDB(m_db);

	// Cria um set de dados
	CRecordset  dados(&db);

	// Cria pesquisa
	m_pesquisa.GetWindowTextW(codigo);

	dados.Open(CRecordset::forwardOnly,
		L"Select * From FUNCIONARIOS Where  ID_FUNCIONARIO = " + codigo);

	// Retorna dados por nome de campo e os exibe
	// Oracle e o DB2 transformam os nomes de campos para maiusculo,
	// basta efetuar um select na tabela de cada banco e você verá
	// O MSSQL não faz esta mudança sendo assim você precisa mudar os campos
	// do banco de dados MSSQL para que fique compativel com o ORACLE e DB2 ou
	// encontrará um erro ao tentar utilizar a tabela que criamos anteriormente.
	dados.GetFieldValue(L"ID_FUNCIONARIO", m_campo);
	m_codigo.SetWindowTextW(m_campo);

	dados.GetFieldValue(L"NOME", m_campo);
	m_pnome.SetWindowTextW(m_campo);

	dados.GetFieldValue(L"SOBRENOME", m_campo);
	m_snome.SetWindowTextW(m_campo);

	dados.GetFieldValue(L"CARGO", m_campo);
	m_cargo.SetWindowTextW(m_campo);

	dados.GetFieldValue(L"SALARIO", m_campo);
	m_salario.SetWindowTextW(m_campo);

	// Fecha o set de dados e a conexão
	dados.Close();
	db.Close();

}

Na linguagem C++ utilizando MFC, você pode manipular dados de qualquer banco de dados que possua um driver suportado pelo ODBC, um sistema de grande porte geralmente utiliza mais de um banco de dados, esta opção torna seu produto mais competitivo no mercado pois pode suportar varias especificações técnicas de hardware e software, assim obtendo vantagem no preço final de sua implementação.

CRecordSet

Representa um conjunto de registros selecionados a partir de uma fonte de dados. Esta classe permite que você manipule por completo um set de dados, obtendo além dos dados contidos na tabela, também seus metadados e contadores uteis para manipular o resultado de uma query em tempo de execução.

Conectando ao Oracle, IBM DB2 e Microsoft SQL Server a partir da mesma Aplicação:

Para efetuar as conexões com os diferentes bancos de dados você precisa primeiro configurar os drivers ODBC necessários no sistema operacional, depois você pode criar um design com 3 componentes RadioButton, um componente Button e um componente CListControl,  você encontra um walkthrough de como configurar os drivers ODBC na categoria SQL e C++ deste site, use a figura abaixo para referencia do design:

Design

Design

Retornando dados do Oracle:

Oracle

Oracle

Retornando dados do IBM DB2:

IBM DB2

IBM DB2

Retornando dados do Microsoft SQL Server:

MSSQL

MSSQL

Exemplo:

Neste exemplo criamos uma aplicação MFC Dialog Based e utilizamos uma única conexão para retornar um set de dados comum para cada banco de dados utilizados no programa.  Use os trechos do código abaixo para completar seu código gerado automaticamente.

C++

Arquivo .h

// Classe gerada automaticamente
// AcessocppDlg.h : header file
//

#pragma once

// Inclui classe de banco de dados MFC
#include "afxdb.h"
#include "afxcmn.h"

class CAcessocppDlg : public CDialogEx
{

public:
	CAcessocppDlg(CWnd* pParent = NULL);	// standard constructor

	enum { IDD = IDD_ACESSOCPP_DIALOG };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV support

protected:
	HICON m_hIcon;

	virtual BOOL OnInitDialog();
	afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()
public:

	// Cria métodos e objetos da classe
	CDatabase db;
	CString bancodedados;
	void conectarDB(CString dns, CString usuario, CString senha);

	afx_msg void OnBnHotItemChangeRadio1(NMHDR *pNMHDR, LRESULT *pResult);
	afx_msg void OnBnHotItemChangeRadio2(NMHDR *pNMHDR, LRESULT *pResult);
	afx_msg void OnBnHotItemChangeRadio3(NMHDR *pNMHDR, LRESULT *pResult);
	afx_msg void OnBnClickedButton1();
	CListCtrl m_tabela;
};

Arquivo .cpp

// Código gerado automaticamente
// AcessocppDlg.cpp : implementation file
//

#include "stdafx.h"
#include "Acessocpp.h"
#include "AcessocppDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

class CAboutDlg : public CDialogEx
{
public:
	CAboutDlg();

	enum { IDD = IDD_ABOUTBOX };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);   

protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()

CAcessocppDlg::CAcessocppDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CAcessocppDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CAcessocppDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_LIST2, m_tabela);
}

BEGIN_MESSAGE_MAP(CAcessocppDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_NOTIFY(BCN_HOTITEMCHANGE, IDC_RADIO1, &CAcessocppDlg::OnBnHotItemChangeRadio1)
	ON_NOTIFY(BCN_HOTITEMCHANGE, IDC_RADIO2, &CAcessocppDlg::OnBnHotItemChangeRadio2)
	ON_NOTIFY(BCN_HOTITEMCHANGE, IDC_RADIO3, &CAcessocppDlg::OnBnHotItemChangeRadio3)
	ON_BN_CLICKED(IDC_BUTTON1, &CAcessocppDlg::OnBnClickedButton1)
END_MESSAGE_MAP()

BOOL CAcessocppDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	SetIcon(m_hIcon, TRUE);
	SetIcon(m_hIcon, FALSE);		

	// Desenvolvimento Aberto
	// Inicializa dialogo

	// Define variavel padrão para o banco de dados
	bancodedados = "oracle";

	// Cria o modelo de exibição de dados
	m_tabela.SetView(LV_VIEW_DETAILS);
	m_tabela.SendMessage(LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_GRIDLINES);

	return TRUE;
}

void CAcessocppDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialogEx::OnSysCommand(nID, lParam);
	}
}

void CAcessocppDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); 

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

HCURSOR CAcessocppDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

// **************************
// * Desenvolvimento Aberto *
// **************************
// Nosso código começa aqui

// Seleciona banco de dados Oracle
void CAcessocppDlg::OnBnHotItemChangeRadio1(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMBCHOTITEM pHotItem = reinterpret_cast<LPNMBCHOTITEM>(pNMHDR);

	bancodedados = "oracle";
	*pResult = 0;
}

// Seleciona banco de dados IBM DB2
void CAcessocppDlg::OnBnHotItemChangeRadio2(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMBCHOTITEM pHotItem = reinterpret_cast<LPNMBCHOTITEM>(pNMHDR);

	bancodedados = "db2";
	*pResult = 0;
}

// Seleciona banco de dados MSSQL
void CAcessocppDlg::OnBnHotItemChangeRadio3(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMBCHOTITEM pHotItem = reinterpret_cast<LPNMBCHOTITEM>(pNMHDR);

	bancodedados = "mssql";
	*pResult = 0;
}

// Evento de clique do botão
void CAcessocppDlg::OnBnClickedButton1()
{
	// campo de dado da tabela
	CString m_campo;

	// Abre conexão
	if (bancodedados == "oracle")
	{
		conectarDB(L"OracleXE", L"daberto", L"p@55w0rd");
	}

	if (bancodedados == "db2")
	{
		conectarDB(L"IBMDB2", L"db2admin", L"p@55w0rd");
	}

	if (bancodedados == "mssql")
	{
		conectarDB(L"MSSQLSERVER", L"devaberto", L"p@55w0rd");
	}

	// Cria um set de dados
	CRecordset  dados(&db);

	// Executa instrução SQL
	if (bancodedados == "db2")
	{
		dados.Open(CRecordset::forwardOnly, L"Select * From DA.\"Blog\"");
	}
	else
	{
		dados.Open(CRecordset::forwardOnly, L"Select * From Blog");
	}

	// Cria item
	LVITEM lvItem;

	// cria estrutura para inserir o item
	typedef struct _LVITEM
	{
		UINT mask;
		int iItem;
		int iSubItem;
		UINT state;
		UINT stateMask;
		LPTSTR pszText;
		int cchTextMax;
		int iImage;
		LPARAM lParam;
        #if (_WIN32_IE >= 0x0300)
		  int iIndent;
        #endif
	 } LVITEM, FAR *LPLVITEM;

	// Define variaveis de itens
	int InsertItem(const LVITEM* pItem);
	int nItem;

	// Converte CString para LPTSTR atraves de um TCHAR
	TCHAR sz[1024];

	// Verifica colunas
	short nFields = dados.GetODBCFieldCount();
	int colunas = m_tabela.GetHeaderCtrl()->GetItemCount();

	// Verifica colunas
	if (colunas == 0)
	{
		// Lê metadata da tabela
		CODBCFieldInfo field;
		for (UINT i = 0; i < nFields; i ++)
		{
			dados.GetODBCFieldInfo(i, field);
			m_tabela.InsertColumn(i, field.m_strName, LVCFMT_LEFT, 100);
		}

	}

	// Deleta itens do controle de lista
	m_tabela.DeleteAllItems();

	// Recupera dados da tabela
	while (!dados.IsEOF())
	{
		for (short index = 0; index < nFields; index++)
		{
			dados.GetFieldValue(index, m_campo);

			// Retorna linha do banco de dados
			if (index == 0)
			{
				// Insere linha
				lvItem.mask = LVIF_TEXT;
				lvItem.iItem = 0;
				lvItem.iSubItem = 0;
				lvItem.pszText = lstrcpy(sz, m_campo);
				nItem = m_tabela.InsertItem(&lvItem);
			}

			// Retorna colunas da linha
			m_tabela.SetItemText(nItem, index, lstrcpy(sz, m_campo));

		}
		// Move o cursor para a proxima linha
		dados.MoveNext();
	}

	// Fecha o set de dados e a conexão
	dados.Close();
	db.Close();

}

void CAcessocppDlg::conectarDB(CString dns, CString usuario, CString senha)
{
	// Cria string de conexão ODBC
	CString conexao;

	// Cria string de conexão
	conexao = L"DSN=" + dns + L";UID=" + usuario + L";PWD=" + senha;

	// Abre conexão
	db.OpenEx(conexao, 0);
}

A grande maioria das empresas necessitam de sistemas que possam processar, guardar e manipular informações e o modo mais comum de se fazer este trabalho é usando um banco de dados relacional. Um sistema de gerenciamento de banco de dados relacional (RDBMS) é um sistema de gerenciamento de banco de dados (SGBD), que é baseado no modelo relacional introduzido por EF Codd, do Laboratório de Pesquisa de San Jose da IBM. Muitos bancos de dados populares atualmente em uso são baseados no modelo de banco de dados relacional.

Os RDBMSs tornaram-se uma escolha predominante para o armazenamento de informações em bancos de dados, usados para registros financeiros, fabricação e informações logísticas, dados de pessoal, e muito mais desde 1980. Bancos de dados relacionais, muitas vezes substituindo bancos de dados hierárquicos e bancos de dados de rede, porque eles são mais fáceis de entender e usar. No entanto, os bancos de dados relacionais vem sendo substituídos por banco de dados orientados a objeto que foram introduzidos na tentativa de resolver a diferença de impedância objeto-relacional em banco de dados relacional e bancos de dados XML.

Banco de Dados Relacional

Banco de Dados Relacional

Entre os maiores banco de dados relacionais do mercado, segundo a empresa Gartner, os líderes de vendas são:

  1. Oracle (48.8%),
  2. IBM (20.2%),
  3. Microsoft (17.0%),
  4. SAP incluindo (Sybase (4.6%), e Teradata (3.7%))

Banco de dados o grande vilão

Quem já ouviu esta afirmação? “O sistema está muito lento!”. Existem vários fatores que podem causar este problema mas eu já ouvi o que considero a pior resposta do mundo de para esta afirmação e foi de um especialista da empresa líder nacional do mercado para ERP, da qual você já imaginou o nome. O sistema estava em um cliente multinacional que fabrica peças para montadoras de carros e rodava sobre o banco de dados Microsoft SQL Server, e o consultor da tal empresa  disse: “Troca o banco de dados pelo Oracle“. Instantaneamente o gerente de TI  replicou: “Neste caso é melhor eu trocar seu sistema pelo SAP!”.

Eu concordo plenamente com a resposta do gerente de TI da multinacional, o SAP roda muito bem em SQL Server e atende a maioria das empresas no qual utiliza esta configuração e benchmark dos dois bancos de dados são comuns, basta procurar no Google, segue aqui um exemplo : BenchMark DB2 1o vs SQL Server 2012 vs Oracle 11g R2 . Será que todas as empresas utilizam as novas features dos bancos de dados em seu sistema assim como faz a SAP?

Geralmente a solução do problema é trazida por um empresa de consultoria, porque este tipo de empresa lida com vários bancos de dados, de vários sistemas diferentes e seus especialistas conhecem a fundo varias bases de dados dos mais diferentes tipos, incluindo sistemas como o SAP, JDEdwardsMS Dynamics. É comum algumas consultorias que também fabricam software desenvolver soluções que completam ou interagem com estes ERPs e estes desenvolvedores ou consultores já estão calejados e sabem que nem tudo que existe por ai é uma maravilha, e nem todos podem ter o luxo de utilizar uma base de dados criada e assinada pela SAP, Oracle, Microsoft ou IBM. E as vezes ou na maioria delas a melhor solução é sim trocar o sistema.

O que acontece por ai?

Algumas empresas não sabem utilizar corretamente o banco, ressalto que utilizar um o banco de dados não é apenas possuir um desenvolvedor SQL que saiba escrever scripts. Já presenciei sistemas de empresas que existem há mais de 10 anos no mercado e ainda não aprenderam a utilizar um banco de dados relacional corretamente, muito menos já seguiram algum dia as melhores praticas. Em um caso em particular o banco de dados era Oracle e o sistema rodava em clientes de médio porte, e a frase constantemente ouvida dos clientes era: “O sistema é muito lento e toda vez que atualiza o sistema gera muitos erros”. Bom, neste caso o especialista da empresa não poderia dizer para trocar o banco de dados e ao analisar a base dados instantaneamente se notava vários problemas como: muitas chaves primarias, a não utilização de índice único, triggers em excesso, scripts SQL no mínimo duvidosos e lembro de um script do relatório de inventario que demorava mais ou menos 10 minutos para retornar 750 produtos.

É! isto existe por ai! E pergunto para você, isto é normal?

Alguns casos são demorados de resolver como o caso da empresa acima que vinha costurando scripts e programação sem metodologia a mais de 10 anos em um banco sem normalização e outros podem ser resolvidos facilmente como o caso de um empresa cooperativa de grande porte que possuía outras 98 empresas em seu grupo e precisava importar as notas fiscais de todas as empresas para um sistema de validação fiscal  que utiliza Microsoft SQL Server e os dados seriam extraídos de um sistema da IBM, este processo demorava mais de 4 horas . Utilizando novos scripts SQL, implementando técnicas de BULK foi possível reduzir o tempo do processo para importação de 5 anos de dados  de 98 empresas para 15 minutos.

Falta de Normalização

Falta de Normalização

O Verdadeiro Vilão

Podemos apontar de primeira alguns dos principais problemas como, falta de conhecimento da documentação do banco de dados em questão, pouca ou nenhuma normalização, não tratar o modelo de dados como um organismo vivo, que respira e cresce constantemente, o armazenamento inadequado dos dados de referência, não usar chaves estrangeiras ou restrições de verificação ou o uso excessivo dos mesmos, o não uso de domínios e padrões de nomenclatura  e não escolher chaves primárias adequadamente.

Em segundo lugar está o estilo de programação tanto para os scripts do banco dados como na linguagem que acessa os dados, é comum encontrar o uso excessivo de cursores, acessos repetitivos e não saber quando usar o conceito de múltiplos bancos de dados.

Vou ilustrar os parágrafos acima com um outro caso que também é muito comum, e refere-se a uso excessivo de chaves estrangeiras,  não saber a hora de optar por um sistema de banco de dados múltiplos e não incluir no projeto do sistema a previsão de crescimento do mesmo.

O problema ocorre porque inicialmente o projeto de um tipo de ferramenta que extrai dados fiscais para validação foi escrito somente para o banco de dados Oracle, ao longo dos anos com o investimento da Microsoft no MSSQL Server o banco de dados conseguiu um bom espaço no mercado e varias empresas de médio e grande porte o adquiriram, então a ferramenta da empresa em questão precisava trabalhar e rodar nos dois banco de dados. Mas o trabalho era árduo porque era preciso extrair os últimos cinco anos de dados fiscais da empresa e suas filiais e valida-los em horas.

Com o passar dos anos as obrigatoriedades vieram a ser maiores e a empresa proprietária da ferramenta precisava atender estas obrigatoriedades e o próprio mercado ditou a necessidade da ferramenta estar apta a rodar em um banco de dados que vinha ganhando cada vez mais espaço, é obvio que se um cliente possuísse uma licença para o banco de dados que rodava em seu sistema principal (MSSQL) não iria comprar uma licença mais cara (Oracle) somente para poder utilizar uma ferramenta e sim procurar outra ferramenta que atendia sua especificação técnica.

Primeiro a empresa tentou sozinha desenvolver para o novo banco de dados e encontrou varias dificuldades ao ter que descobrir também sozinha que os conceitos entre os bancos de dados não eram iguais e decidiu procurar ajuda especializada, ficou surpresa com o resultado, ao trabalhar com mais de um banco de dados a empresa conseguiu benefícios de desempenho também para o banco de dados no qual a empresa dominava e era especialista.

Porque para competir com o Oracle a Microsoft também usou como estratégia restringir os erros cometidos pelos desenvolvedores na arquitetura do banco de dados, meio que forçando o banco de dados SQL Server a ser escrito e normalizado o mais corretamente possível, assim ganhando mais performance, um exemplo são as chaves estrangeiras, estas requerem memoria e custam para serem interpretadas pelo Engine do banco de dados e a partir da versão 2005 do MSSQL a Microsoft começou a restringir chaves estrangeiras por tabela. No caso de um banco de dados único em um sistema multi-empresas é comum possuir mais de 253 tabelas que possuem uma chave estrangeira conectada a tabela de empresas, então a Microsoft começou a emitir um erro quando se tentava deletar ou alterar um registro de uma tabela que possuía tal quantidade de chaves estrangeiras, a simples clausula executada sobre esta tabela:

Delete from EMPRESA where ID_EMPRESA = 1

retorna o seguinte erro: QUERY TOO COMPLEX.

Capacidade máxima do MSSQL Server (Atente-se para a coluna chamada Foreign key table references per table):

link: http://msdn.microsoft.com/en-us/library/ms143432.aspx

Você pode ler e inserir dados na tabela normalmente, mas não pode mais apagar ou alterar os dados devido ao alto custo da query, apesar do Oracle ou outros bancos de dados permitirem tal modelagem a query executada sobre estas circunstancias também terá um custo maior para ser resolvida pelo Engine do banco de dados. Para uma modelagem mais compacta e sem excessos de objetos no banco de dados, um sistema pode possuir múltiplos bancos de dados, um exemplo de um sistema de grande porte com múltiplos bancos de dados é o JDEdwards da Oracle.

Grande Volume de Dados

Grande Volume de Dados

Processamento de grandes volumes de dados

É recomendado seguir melhores praticas para cada banco de dados e ler os White Papers escritos para suas ferramentas e quanto a regras de programação para sistemas que acessam dados você pode seguir o critério acima ou pode buscar conhecimento em empresas que já possuem este know-how como o caso da SAP.

Como é de conhecimento de todos os sistemas da SAP foram projetados para trabalhar com grande volume de dados e a SAP especifica 5 regras para se programar com os banco de dados: Oracle, Db2, Microsoft SQL Server e Informix. Apesar destas regras serem escritas para programas ABAP e Java e a arquitetura de dados do SAP R/3, elas também se aplicam a forma como outras linguagens de programação devem acessar o banco de dados:

  1. Mantenha o conjunto de resultados Pequeno.
  2. Minimizar a quantidade de dados transferidos.
  3. Minimizar o número de transferências de dados.
  4. Minimizar a Pesquisa Overhead.
  5. Reduzir a carga do banco de dados.

 

 

MFC fornece dois conjuntos distintos de classes para acessar banco de dados: um para permitir o acesso através de DAO, e outros através de ODBC. O método DAO é geralmente usado para acessar bancos de dados desktop e o método ODBC para conectar-se servidores de banco de dados relacionais através de uma fonte de dados.

MSSQL - ODBC

MSSQL – ODBC

ODBC

O Microsoft Open Database Connectivity (ODBC) é uma interface de linguagem de programação C, que torna possível que aplicativos acessem dados de uma variedade de sistemas de gerenciamento de banco de dados (SGBD). ODBC é escrita em baixo nível, e uma interface de alto desempenho que é projetado especificamente para armazenamentos de dados relacionais.

O Microsoft Foundation Classes (MFC) fornece bibliotecas de classes para programação com Open Database Connectivity (ODBC).

CDatabase

Representa uma conexão com uma fonte de dados, através do qual você pode operar com esta fonte de dados.

Conectando ao banco de dados MSSQL Server

1 – Crie um novo projeto C++ MFC Application Dialog Based, coloque no dialogo 4 componentes Static Text, 3 componentes Edit Control, e um Button, disponha os componentes como na figura abaixo:

Design

Design

2 – No painel de controle escolha a opção ferramentas administrativas e abra a opção Fontes de Dados ODBC para 32 ou 64bits, na guia DNS de Usuário, clique em adicionar e selecione o driver correspondente ao MSSQL:

Driver

Driver

3 – Crie os parâmetros de conexão contendo um nome para sua fonte de dados no qual será seu DNS e crie os parâmetros comuns para se conectar ao MSSQL, teste sua conexão e clique em OK:

MSQL -DNS

MSQL -DNS

4 – Use os trechos marcados do código abaixo para completar seu código gerado automaticamente

Exemplo:

Neste exemplo efetuamos uma conexão ao banco de dados através de um DNS ODBC usando a classe CDatabase.

C++


// MSSQLSever-cppDlg.cpp : implementation file
//

#include "stdafx.h"
#include "MSSQLSever-cpp.h"
#include "MSSQLSever-cppDlg.h"
#include "afxdialogex.h"

// Inclui classe de banco de dados MFC
#include "afxdb.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

CMSSQLSevercppDlg::CMSSQLSevercppDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CMSSQLSevercppDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CMSSQLSevercppDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_EDIT1, m_usuario);
	DDX_Control(pDX, IDC_EDIT2, m_senha);
	DDX_Control(pDX, IDC_EDIT3, m_sid);
}

BEGIN_MESSAGE_MAP(CMSSQLSevercppDlg, CDialogEx)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BUTTON1, &CMSSQLSevercppDlg::OnBnClickedButton1)
END_MESSAGE_MAP()

BOOL CMSSQLSevercppDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	SetIcon(m_hIcon, TRUE);
	SetIcon(m_hIcon, FALSE);		

	return TRUE;
}

void CMSSQLSevercppDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); 

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

HCURSOR CMSSQLSevercppDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

// Desenvolvimento Aberto
// nosso código começa aqui.

// Cria menssagem
int menssagem(bool correto)
{
	LPCWSTR msg;

	if (correto)
	{
		msg = L"Conectado com sucesso!";
	}
	else
	{
		msg = L"Erro na conexão";
	}

	int msgboxID = MessageBox(
		NULL,
		msg,
		(LPCWSTR)L"Conexão",
		MB_ICONWARNING | MB_OK | MB_DEFBUTTON2
		);

	switch (msgboxID)
	{
	case IDOK:

		break;
	}

	return msgboxID;
}

void CMSSQLSevercppDlg::OnBnClickedButton1()
{
	// Cria classe conexão ODBC
	CDatabase db;

	// Declara variaveis
	CString usuario;
	CString senha;
	CString fonte;
	CString conexao;

	// Alimenta variaveis
	m_sid.GetWindowTextW(fonte);
	m_usuario.GetWindowTextW(usuario);
	m_senha.GetWindowTextW(senha);

	// Cria string de conexão
	conexao = L"DSN=" + fonte + L";UID=" + usuario + L";PWD=" + senha;

	// Conecta ao banco de dados
	db.OpenEx(conexao, 0);

	// Verifica conexão
	if (db.IsOpen())
	{
		menssagem(true);
	}
	else
	{
		menssagem(false);
	}

}

MFC fornece dois conjuntos distintos de classes para acessar banco de dados: um para permitir o acesso através de DAO, e outros através de ODBC. O método DAO é geralmente usado para acessar bancos de dados desktop e o método ODBC para conectar-se servidores de banco de dados relacionais através de uma fonte de dados.

IBM DB2 - MFC - C++

IBM DB2 – MFC – C++

ODBC

O Microsoft Open Database Connectivity (ODBC) é uma interface de linguagem de programação C, que torna possível que aplicativos acessem dados de uma variedade de sistemas de gerenciamento de banco de dados (SGBD). ODBC é escrita em baixo nível, e uma interface de alto desempenho que é projetado especificamente para armazenamentos de dados relacionais.

O Microsoft Foundation Classes (MFC) fornece bibliotecas de classes para programação com Open Database Connectivity (ODBC).

CDatabase

Representa uma conexão com uma fonte de dados, através do qual você pode operar com esta fonte de dados.

Conectando ao banco de dados IBM DB2 Express-C

1 – Crie um novo projeto C++ MFC Application Dialog Based, coloque no dialogo 4 componentes Static Text, 3 componentes Edit Control, e um Button, disponha os componentes como na figura abaixo:

Design

Design

2 – No painel de controle escolha a opção ferramentas administrativas e abra a opção Fontes de Dados ODBC para 32 ou 64bits, na guia DNS de Usuário, clique em adicionar e selecione o driver correspondente ao IBM DB2:

Driver IBM DB2

Driver IBM DB2

3 – Crie os parâmetros de conexão contendo um nome para sua fonte de dados no qual será seu DNS e crie os parâmetros comuns para se conectar ao IBM DB2, teste sua conexão e clique em OK:

DSN - ODBC

DSN – ODBC

4 – Use os trechos marcados do código abaixo para completar seu código gerado automaticamente

Exemplo:

Neste exemplo efetuamos uma conexão ao banco de dados através de um DNS ODBC usando a classe CDatabase.

C++


// IBM-DB2cppDlg.cpp : implementation file
//

#include "stdafx.h"
#include "IBM-DB2cpp.h"
#include "IBM-DB2cppDlg.h"
#include "afxdialogex.h"

// Inclui classe de banco de dados MFC
#include "afxdb.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

CIBMDB2cppDlg::CIBMDB2cppDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CIBMDB2cppDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CIBMDB2cppDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_EDIT1, m_usuario);
	DDX_Control(pDX, IDC_EDIT2, m_senha);
	DDX_Control(pDX, IDC_EDIT3, m_sid);
}

BEGIN_MESSAGE_MAP(CIBMDB2cppDlg, CDialogEx)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BUTTON1, &CIBMDB2cppDlg::OnBnClickedButton1)
END_MESSAGE_MAP()

BOOL CIBMDB2cppDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	SetIcon(m_hIcon, TRUE);
	SetIcon(m_hIcon, FALSE);		

	return TRUE;
}

void CIBMDB2cppDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); 

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

HCURSOR CIBMDB2cppDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

// Desenvolvimento Aberto
// nosso código começa aqui.

// Cria menssagem
int menssagem(bool correto)
{
	LPCWSTR msg;

	if (correto)
	{
		msg = L"Conectado com sucesso!";
	}
	else
	{
		msg = L"Erro na conexão";
	}

	int msgboxID = MessageBox(
		NULL,
		msg,
		(LPCWSTR)L"Conexão",
		MB_ICONWARNING | MB_OK | MB_DEFBUTTON2
		);

	switch (msgboxID)
	{
	case IDOK:

		break;
	}

	return msgboxID;
}

void CIBMDB2cppDlg::OnBnClickedButton1()
{

	// Cria classe conexão ODBC
	CDatabase db;

	// Declara variaveis
	CString usuario;
	CString senha;
	CString fonte;
	CString conexao;

	// Alimenta variaveis
	m_sid.GetWindowTextW(fonte);
	m_usuario.GetWindowTextW(usuario);
	m_senha.GetWindowTextW(senha);

	// Cria string de conexão
	conexao = L"DSN=" + fonte + L";UID=" + usuario + L";PWD=" + senha;

	// Conecta ao banco de dados
	db.OpenEx(conexao, 0);

	// Verifica conexão
	if (db.IsOpen())
	{
		menssagem(true);
	}
	else
	{
		menssagem(false);
	}

}

MFC fornece dois conjuntos distintos de classes para acessar banco de dados: um para permitir o acesso através de DAO, e outros através de ODBC. O método DAO é geralmente usado para acessar bancos de dados desktop e o método ODBC para conectar-se servidores de banco de dados relacionais através de uma fonte de dados.

Oracle - ODBC

Oracle – ODBC

ODBC

O Microsoft Open Database Connectivity (ODBC) é uma interface de linguagem de programação C, que torna possível que aplicativos acessem dados de uma variedade de sistemas de gerenciamento de banco de dados (SGBD). ODBC é escrita em baixo nível, e uma interface de alto desempenho que é projetado especificamente para armazenamentos de dados relacionais.

O Microsoft Foundation Classes (MFC) fornece bibliotecas de classes para programação com Open Database Connectivity (ODBC).

CDatabase

Representa uma conexão com uma fonte de dados, através do qual você pode operar com esta fonte de dados.

Conectando ao banco de dados Oracle XE

1 – Crie um novo projeto C++ MFC Application Dialog Based, coloque no dialogo 4 componentes Static Text, 3 componentes Edit Control, e um Button, disponha os componentes como na figura abaixo:

Design

Design

2 – No painel de controle escolha a opção ferramentas administrativas e abra a opção Fontes de Dados ODBC para 32 ou 64bits, na guia DNS de Usuário, clique em adicionar e selecione o driver correspondente ao Oracle XE:

Driver Oracle

Driver Oracle

3 – Crie os parâmetros de conexão contendo um nome para sua fonte de dados no qual será seu DNS e crie os parâmetros comuns para se conectar ao Oracle XE, teste sua conexão e clique em OK:

DNS OracleXE

DNS OracleXE

4 – Use os trechos marcados do código abaixo para completar seu código gerado automaticamente

Exemplo:

Neste exemplo efetuamos uma conexão ao banco de dados através de um DNS ODBC usando a classe CDatabase.

C++


// OraclecppDlg.cpp : implementation file
//

#include "stdafx.h"
#include "Oraclecpp.h"
#include "OraclecppDlg.h"
#include "afxdialogex.h"

// Inclui classe de banco de dados MFC
#include "afxdb.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

COraclecppDlg::COraclecppDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(COraclecppDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void COraclecppDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_EDIT1, m_usuario);
	DDX_Control(pDX, IDC_EDIT2, m_senha);
	DDX_Control(pDX, IDC_EDIT3, m_sid);
}

BEGIN_MESSAGE_MAP(COraclecppDlg, CDialogEx)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BUTTON1, &COraclecppDlg::OnBnClickedButton1)
END_MESSAGE_MAP()

BOOL COraclecppDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	SetIcon(m_hIcon, TRUE);
	SetIcon(m_hIcon, FALSE);	

	return TRUE;
}

void COraclecppDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); 

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

HCURSOR COraclecppDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

// Desenvolvimento Aberto
// nosso código começa aqui.

// Cria menssagem
int menssagem(bool correto)
{
	LPCWSTR msg;

	if (correto)
	{
		msg = L"Conectado com sucesso!";
	}
	else
	{
		msg = L"Erro na conexão";
	}

	int msgboxID = MessageBox(
		NULL,
		msg,
		(LPCWSTR)L"Conexão",
		MB_ICONWARNING | MB_OK | MB_DEFBUTTON2
		);

	switch (msgboxID)
	{
	case IDOK:

		break;
	}

	return msgboxID;
}

// Clique do Botão
void COraclecppDlg::OnBnClickedButton1()
{
	// Cria classe conexão ODBC
	CDatabase db;

	// Declara variaveis
	CString usuario;
	CString senha;
	CString fonte;
	CString conexao;

	// Alimenta variaveis
	m_sid.GetWindowTextW(fonte);
	m_usuario.GetWindowTextW(usuario);
	m_senha.GetWindowTextW(senha);

	// Cria string de conexão
	conexao = L"DSN=" + fonte + L";UID=" + usuario + L";PWD=" + senha;

	// Conecta ao banco de dados
	db.OpenEx(conexao, 0); 

	// Verifica conexão
	if (db.IsOpen())
	{
		menssagem(true);
	}
	else
	{
		menssagem(false);
	}	  

}

Por padrão, o Assistente de aplicativo do MFC cria um esqueleto de aplicação com uma classe de documentos e uma classe de visão. O MFC separa o gerenciamento de dados para essas duas classes. O documento armazena os dados e gerencia a impressão dos dados e coordena a atualização das múltiplas visões dos dados. A exibição mostra os dados e gerencia a interação do usuário com eles, incluindo a seleção e edição.

Neste modelo, um objeto de documento MFC lê e grava dados para armazenamento persistente. O documento pode também proporcionar uma interface para os dados de onde ele reside (tal como numa base de dados). Um objeto de exibição separado controla a exibição de dados, de processar os dados em uma janela de seleção de usuário e edição. A visão obtém dados de exibição do documento e se comunica de volta ao documento com todas as alterações de dados.

MDI - Document/View

MDI – Document/View MFC Standard

Visual Studio

A Microsoft possui um Wizard para aplicações MFC que cria programas de exemplos que também são programas iniciais de desenvolvimento para que você não precise começar do zero. Estes exemplos são eficientes pois proporcionam as técnicas corretas de desenvolvimento e nomenclatura e uma vez que se aprende como funciona cada um dos diferentes exemplos de aplicações MFC, o desenvolvedor ganha um grande know-how da arquitetura Windows. Entre os exemplos estão os seguintes modelos de aplicações: MFC Standad, Windows Explorer, Visual Studio e Office.

Mais informações sobre como funciona a arquitetura Document/View: http://msdn.microsoft.com/en-us/library/4x1xy43a.aspx

MDI Sem o uso do Document/View

É possível criar aplicações MDI sem o uso do modelo de documento e visão, mas a Microsoft não recomenda esta pratica por não ser segura, apesar de existir um exemplo nos exemplos oficiais do Visual Studio:

MDI - Without Doc/View

MDI – Without Doc/View

 

Sample MDI without using Doc/View: http://msdn.microsoft.com/en-us/library/ahd07sdd(v=vs.90).aspx

Exemplo:

Este tipo de aplicação requer no mínimo três classes, o que em C++ significa seis arquivos, então você pode obter o código completo criando uma aplicação MFC do tipo Multi Documents com o Project Style tipo MFC Standard. O trecho de código a seguir mostra como funciona a arquitetura Document/View onde  as classes de documentos e de visão são vinculadas em tempo de execução a um Template, você pode ter um documento para diversas visões.

C++

CMultiDocTemplate* pDocTemplate;

	pDocTemplate = new CMultiDocTemplate(IDR_DocumentosMdiCpTYPE,
		RUNTIME_CLASS(CDocumentosMdiCppDoc), // Classe do Documento
		RUNTIME_CLASS(CChildFrame), // custom MDI child frame
		RUNTIME_CLASS(CDocumentosMdiCppView)); // Classe da Visão

	if (!pDocTemplate)
		return FALSE;

	AddDocTemplate(pDocTemplate);

 

Tooltip é um elemento comum de interface gráfica GUI, também conhecida como dica de contexto é aquela moldura pop up que abre quando você passa o mouse sobre um elemento da interface (normalmente uma palavra em um texto) e que contém uma explicação adicional sobre aquele elemento que recebeu o ponteiro do mouse sobre ele. É utilizado em conjunto com um cursor, normalmente um ponteiro do mouse. O usuário paira o cursor sobre um item, sem clicar nele, e uma pequena “caixa” irá aparece com informações complementares relativas a este item.

CToolTipCtrl

CToolTipCtrl

CToolTipCtrl

Encapsula a funcionalidade de um controle dica de ferramenta“, uma pequena janela pop-up que exibe uma única linha de texto que descreve a finalidade de uma ferramenta em um aplicativo.

PreTranslateMessage

Você pode efetuar um método Override para substituir esta função para filtrar mensagens de janela antes de serem enviados para Windows.

Visual Studio

Para criar um controle ToolTip para um dialogo MFC em C++ siga os seguintes passos:

  1. Crie um novo projeto MFC Dialog Based.
  2. Coloque no dialogo um componente Static Text e um componente Button, crie uma variável para o botão chamada m_botao.
  3. Disponha os componentes no dialogo conforme a imagem abaixo:

    CToolTipCtrl - Design

    CToolTipCtrl – Design

  4. Declare o método Override para a mensagem PreTranslateMessage e crie um objeto CToolTipCtrl para a classe de seu dialogo, use o primeiro código para se basear.
  5. Complete seu código gerado automaticamente para a implementação da classe, use o segundo código para se basear.

Exemplo:

Neste exemplo usamos o objeto CToolTipCtrl para exibir uma dica para o usuário ao pairar o cursor do mouse sobre o botão, interceptando a mensagem e exibindo o texto da dica na mensagem do Windows PreTranslateMessage.

C++

Classe – Definição (.h)


// tooltipcppDlg.h : header file
//

#pragma once
#include "afxwin.h"

class CtooltipcppDlg : public CDialogEx
{

public:
	CtooltipcppDlg(CWnd* pParent = NULL);	

	enum { IDD = IDD_TOOLTIPCPP_DIALOG };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);

protected:
	HICON m_hIcon;

	// Desenvolvimento Aberto
	// Declara metodo de menssagem
	BOOL PreTranslateMessage(MSG* pMsg);
	
	virtual BOOL OnInitDialog();
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()
public:

	// Declara componente tooltip
	CToolTipCtrl* m_pToolTip;

	CButton m_botao;
};

Classe – Implementação (.cpp)


// tooltipcppDlg.cpp : implementation file
//

#include "stdafx.h"
#include "tooltipcpp.h"
#include "tooltipcppDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

CtooltipcppDlg::CtooltipcppDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CtooltipcppDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CtooltipcppDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_BUTTON1, m_botao);
}

BEGIN_MESSAGE_MAP(CtooltipcppDlg, CDialogEx)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
END_MESSAGE_MAP()


BOOL CtooltipcppDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();
		
	SetIcon(m_hIcon, TRUE);			
	SetIcon(m_hIcon, FALSE);		

	// Desenvolvimento Aberto
	// Inicializa objeto tooltip e define suas propriedades	
	
	// Cria objeto tooltip 
	m_pToolTip = new CToolTipCtrl;

	if (!m_pToolTip->Create(this))
	{	
		return TRUE;
	}

	// Define objeto para a dica
	m_pToolTip->AddTool(&m_botao, 
		L"Desenvolvimento Aberto - Clique neste botão para executar uma ação.");
	
	// Ativa tooltip
	m_pToolTip->Activate(TRUE);

	return TRUE;  
}

void CtooltipcppDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); 

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
				
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;
				
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

HCURSOR CtooltipcppDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

// Desenvolvimento Aberto
// Cria override da menssagem do windows

BOOL CtooltipcppDlg::PreTranslateMessage(MSG* pMsg)
{
	m_pToolTip->RelayEvent(pMsg);

	return CDialog::PreTranslateMessage(pMsg);
}

O que a linguagem de programação Delphi tem em comum com Java e C#? A resposta é tudo? Ou nada? Quem sabe!. Estranho? Com certeza! E a resposta correta depende da versão do Delphi do qual estamos nos referindo. Ou não?

O Delphi nasceu em 1995 como uma linguagem de programação Object Pascal para o Windows 16 bits e foi o primeiro a ser descrito como ambiente RAD (em português, Desenvolvimento Rápido de Aplicações), seu nome faz alusão ao banco de dados Oracle, pois na mitologia grega o Oraculo (Oracle) podia ser encontrado no templo de Delphi.

Delphi - Win32

Delphi – Win32

O Delphi se difundiu pelo mundo na sua versão para Windows 32 bits,  justamente por fazer realmente o que prometia, era rápido para desenvolvimento em grandes equipes, podia se conectar com qualquer banco de dados e de vários modos diferentes, além de possuir uma gama de componentes de todos os tipos, desde os Open Source, como o famoso pacote de componentes Russo chamado RX, até componentes empresariais relacionado a dados e ao visual da aplicação como o InfoPower e 1st Class da empresa Woll2Woll.

O Delphi também possuía vários componentes para relatório onde podia-se  utilizar o poder da sua conexão com o banco de dados Oracle, unidos aos recursos de seus componentes, também possuía integração com o gerador de relatório mais famoso do mundo o Cristal Reports que hoje pertence a SAP.

Outro ponto forte do Delphi é que era possível programar rotinas em Assembler dentro do Object Pascal apenas abrindo um bloco ASM e dizendo direto ao hardware o que o desenvolvedor gostaria de fazer. O Delphi também possuía várias rotinas Assembler como parte de seus comandos como o caso do comando Abort, para ver estas rotinas bastava clicar em cima do comando em questão escrito de qualquer Unit pascal  e pressionando o botão Control (CTRL + Click)  e automaticamente você seria levado ao código que o originava, assim como nas IDEs de hoje em dia.

Assembler Delphi: http://docwiki.embarcadero.com/RADStudio/XE3/en/Using_Inline_Assembly_Code

Com o “Boom” do Linux em 2001 foi lançada uma versão para esta plataforma chamada Kylix,  com o lançamento do Delphi 8 a linguagem passou a suportar o .NET Framework, podendo-se escrever programas C# direto de sua IDE, mas não vingou e  muitas empresas estacionaram seu desenvolvimento na versão 7 do Delphi. O Delphi hoje é marca registrada da empresa Embarcadero e está na sua versão XE10 (emprestando a estratégia de versão do Windows 10) no qual pode-se desenvolver aplicações verdadeiramente nativas para Windows, Mac, Android e iOS a partir de uma única base de código.

Delphi 7 - Win32

Delphi 7 – Win32

Desenvolvimento em Delphi vale a pena migrar para Java ou C#?

Bom,  então, para quem migrou da versão 7 do Delphi para suas versões superiores e utilizou o suporte a plataforma .Net não temos muito o que falar, pois C# lembra muito o Java que por sua vez bebeu da fonte do C++ e assim por diante, mas para aqueles que continuam desenvolvendo em Delphi para Win32 a coisa é bem diferente.

Existe até uma URL estratégica da Embarcadero preparada para as pesquisas do Google:

UpGrade Delphi 7 para XE10: http://www.embarcadero.com/br/products/rad-studio/upgrade-from-delphi7

Programas escritos em Delphi 7, suas versões anteriores ou versões superiores mas utilizando projetos de código nativo ainda são comuns e a pergunta mais frequente é se vale a pena migrar para Java ou C#?

A resposta é simples, sim! Vale a pena mudar!

Por que ?

Ao contrario do que a maioria pensa (que me digam os proprietários de software houses que ainda desenvolve em Delphi código nativo)  desenvolver para Win32 exige um conhecimento muito técnico das APIs do Windows, apesar do Delphi transformar esta programação em uma tarefa extremamente fácil (vide Visual Studio MFC C++ para Win32) o resultado algumas vezes pode não ser aquele desejado pelos proprietários de software house, nem pelos clientes e nem pelos programadores.

Na pratica existem muitos programas escritos em Delphi para Win32 que são lentos, mesmo usando o melhor banco de dados do mundo, o Oracle, sem entrar no mérito das melhores praticas para um banco de dados. Muitos programas geram erros de memoria sozinhos, apenas com o usuário o utilizando, são difíceis de dar manutenção e não são projetados para o crescimento da base de dados, e pasmem, existem programas que param de funcionar devido a lentidão quando utilizados em bases de dados de grande porte (entrando no mérito das melhores praticas para um banco de dados), gerando para as empresas uma legião de clientes insatisfeitos, se identificou?

Mas o problema não é do Delphi e nem das APIs do Windows e sim da técnica dos programadores principalmente de programas que já foram migrados do Cobol, Clipper, DataFlex entre outras linguagens na qual se utiliza bases de dados relativamente grandes. Um erro comum era não conhecer o sistema operacional e não usar Dlls e ActiveX, criando Units de código pascal para as bibliotecas e programas reutilizáveis que eram referenciadas entre si e compiladas junto com o executável o tornando enorme, além disto, necessitando deste modo criar grande quantidade de procedimentos e funções publicas para estas Units ou Forms também referenciados (quando a convenção para qualquer linguagem orientada a objeto diz que você deve sempre criar métodos privados e usar métodos públicos somente quando realmente for necessário). A consequência deste tipo de programação é sem duvida, vazamento de memoria (Memory Leak), visto que apenas o Delphi com suporte ao .NET Framework possui o Garbage Collector, além de códigos fonte crescendo desordenadamente ao longo dos anos, sem contar outros inúmeros conceitos da programação para Win32 que continuaram sendo completamente ignorados.

Dinamic-Link Library: http://msdn.microsoft.com/en-us/library/windows/desktop/ms681914(v=vs.85).aspx

Component Object Model: http://msdn.microsoft.com/en-us/library/windows/desktop/ms694363(v=vs.85).aspx

Delphi ActiveX Framework: http://docwiki.embarcadero.com/RADStudio/XE3/en/Overview_of_COM_Technologies

Memory Leak Delphi: http://delphi.about.com/od/oopindelphi/a/memoryleak.htm

Para quem não teve estes problemas ainda possui um software plenamente funcional, rápido e com um pouco mais de trabalho pode se desenvolver qualquer coisa que é feita hoje em dia, pois a Microsoft garante a compatibilidade da API Win32 (conceito que significa código nativo e não só para 32 bits). Assim como o modelo de  programas em Delphi escritos em código nativo, nos dias hoje a Microsoft disponibiliza este tipo de  ferramenta apenas para C++, e este tipo de programação não pode ser desenvolvida à partir de uma versão Express do Visual Studio, pois a aplicação nativa é ideal para softwares coorporativos por ser consideravelmente mais rápida do que aplicações utilizando o framework.

Código Nativo – Win32 : http://msdn.microsoft.com/pt-br/library/hh875053.aspx

Delphi possui a melhor IDE RAD de todos os tempos, suporta vários tipos de conexões incluindo os melhores bancos de dados do mundo, é multi-plataforma, possui código nativo com acesso pleno a API de plataforma, incluindo plataformas mobiles.

Embarcadero: https://www.embarcadero.com/br

Delphi Language: http://docwiki.embarcadero.com/RADStudio/XE3/en/Language_Overview

Rad Studio XE7

RAD Studio XE7

Migração Delphi – Java ou C#

Muitos proprietários e gerentes de TI se deixam levar por toda propaganda em torno das linguagens Java e C#, o que você deve lembrar é que você esta lidando com a Oracle e a Microsoft e um orçamento enorme para marketing mundial, isto significa que os softwares principais ou seja, produtos de primeira linha para o segmento empresarial que fazem praticamente tudo e estão no topo da tecnologia, os “ERPs” das grandes empresas fornecidos pela Oracle e Microsoft que provavelmente são concorrentes direto, senão em um todo em algum módulo do produto que você planeja desenvolver e possivelmente são vendidos por um valor muito maior do que seu produto final será, estes produtos topo de linha não foram desenvolvidos e nem utilizam Java e .NET, o Oracle JDEdwards utiliza Event Rule Language, C e C++ e o Microsoft Dynamics AX (C++) utiliza sua IDE MorphX e a linguagem X++ e foi desenvolvido originalmente pela IBM, então você não deve tornar nenhuma das marcas registradas seu time do coração antes de conhecer as mesmas razoavelmente. Não pense que você vai desenvolver o próximo JDE ou Dynamics usando Java ou C#, até porque os bancos de dados renomados fabricados pelas mesmas empresas detentoras dos ERPs possuem Features exclusivas para os mesmos e assim também acontece com o SAP R/3 que é líder no mercado empresarial e a Oracle e a Microsoft se orgulham em dizer que seus bancos de dados são certificados para as aplicações SAP.

É claro que você pode desenvolver ótimos e robustos softwares com as linguagens Java e C# mas tenha em mente que investir em infraestrutura, conhecimento técnico e funcional é muito importante, por isto se você é testemunha de problemas descritos no tópico acima simplesmente migrar para uma destas linguagens por si só não vai resolver seu problema, muito pelo contrario, Java é baseado em C++ e C# é baseado em Java, assim o conceito de objetos, classes, coleções, iteradores entre outros, demanda uma curva de aprendizado relativamente maior que o aprendizado exigido pela linguagem Object Pascal, sem contar que o programador também deve conhecer como funciona o sistema operacional em questão independente da linguagem ser ou não multi plataforma.

Dependendo do tipo de projeto, hoje, também vale a pena utilizar linguagens que vem ganhando muito espaço no mercado internacional e no Brasil, como Ruby e Python que possuem uma comunidade muito ativa. No exterior Python é ensinado a crianças nas escolas e muitos a veem como uma linguagem do futuro. Estas linguagens são multi plataforma, muito versáteis e puramente orientadas a objeto, apesar do Python não possuir oficialmente esta classificação é bem similar ao Ruby, na verdade Ruby que foi inspirado em Python, e de qualquer modo Python e Ruby possuem uma orientação a objeto mais pura que Java e C# pois não possuem tipos primitivos possibilitando cortar alguns caminhos na hora da codificação, além do beneficio de que as duas linguagens possuem uma sintaxe simples e minimalista, fazem a mesma coisa requerendo muito menos linhas de código e ainda por cima de um modo mais fácil.

Já o Java e o C# herdam a sintaxe e conceitos do C++, inclusive o C# trouxe do C++ muita coisa que o Java aboliu, como ponteiros e Structs, as duas linguagens na pratica são muito similares e ambas já são linguagens conceituadas. Uma vantagem é que Java, Ruby e Python são linguagens que possuem muitos frameworks de código aberto sob diversas licenças, evoluem mais rápido do que o C# que vai incorporar as novidades só em sua próxima versão ou na próxima versão do Windows que agregará novas tecnologias, muitas delas vindas ou inspiradas no código aberto, como vários frameworks Java que também foram escritos para C# mais tarde, e o próprio ASP.NET MVC que foi e ainda é altamente influenciado pelo framework Rails.

Entretanto a Microsoft já se mobilizou para tentar mudar esta situação criando uma serie de medidas, lançando uma versão comunitária do Visual Studio, adotando o Python e outras linguagens e as integrando ao Visual Studio, firmando parcerias com empresas como a Apache incorporando o Apache Córdoba e a empresa Xamarim que patrocina o Mono (Linux) e é proprietária da plataforma Xamarim (OS X e Android), outra medida também foi abrir o código fonte da tecnologia ASP.NET tornando seus frameworks abertos sob a licença Apache, para que possam sofrer mais influencias da comunidade de código aberto, pretende também em um futuro próximo, abrir o código do núcleo do .Net Framework para que se torne autossuficiente em outras plataformas. Influencia esta que o Java e outras linguagens já possuem e atuam fortemente em todas as plataformas (Unix, Linux, Windows, OS X e diversos devices), inclusive na tecnologia Cloud, onde a Oracle proprietária do Java há tempos já lançou o primeiro sistema operacional exclusivo para as nuvens, o Oracle Solaris 11, em contrapartida as varias distribuições Linux Enterprise como a RedHat também estão investindo fortemente na plataforma Cloud, como por exemplo a plataforma hibrida OpenShift que suporta serviços em varias linguagens e bancos de dados diferentes, enquanto a Microsoft foca exclusivamente na sua tecnologia Azure, MSSQL Server, na interoperabilidade com o sistema operacional Linux Suse Enterprise e em suas novas parcerias.

Durante todas estas reviravoltas tecnológicas no universo do código aberto e no mundo proprietário, batalhas judiciais pelas patentes de softwares e devices, o Delphi apenas corre atrás das velhas novidades, ainda tentando mover usuários (sem perde-los) de sua plataforma Win32 para as suas novas plataformas proprietárias (livre somente de royalts) que competem com vários outros frameworks mais atuais, muitos deles de código aberto e ou gratuitos.

Conectividade Delphi: http://www.embarcadero.com/br/products/delphi/connect

Uma das mais famosas bibliotecas C++ no qual Java e C# foram baseados:

Standard Template Library: http://msdn.microsoft.com/en-us/library/c191tb28.aspx

STL/CLR Library: http://msdn.microsoft.com/en-us/library/bb385954.aspx

JCF – Framework: http://docs.oracle.com/javase/tutorial/collections/intro

No Brasil a linguagem de programação C# é a queridinha das empresas de pequeno e médio porte, isto é devido as diversas ações que a Microsoft executa no mercado nacional com seus programas, entre eles as certificações com escolas credenciadas, Microsoft Partners e inúmeras convenções e encontros, já o Java atua soberano no mercado de software para Main Frames e Devices, desenvolve-se em Java também dentro de softwares de grande porte, incluindo softwares da Oracle, IBM, Google e SAP, como no banco de dados Oracle e IBM DB2, que rodam rotinas em Java, além destas quatro companhias também possuírem uma IDE exclusiva para programar em Java, elas são NetBeans (Oracle), Eclipse (IBM), Android Studio (Google) e NetWeaver para Java da SAP, em sua nova tecnologia para as nuvens SAP HANA a SAP adota a IDE Eclipse como sua nova plataforma de desenvolvimento. Sem contar outras inúmeras tecnologias profissionais e servidores de aplicações Enterprise para Java como Oracle WebLogic, IBM WebSphere, TomCat da Apache, JBoss da Red Hat.

Fora do Brasil o C# esta qualificado com apenas 7.4% no ranking de linguagens mais procuradas pelas empresas, enquanto Java possui 19,6%, segundo o site CodeEval o ranking das linguagens mais populares de 2015 são: Ranking Code Eval.

Já no Tiobe Index 2015, a linguagem Java aparece em segundo lugar, C# ocupa a quarta posição enquanto Delphi desde 2013 se localiza abaixo da decima sétima posição, você pode conferir o ranking mensal da Tiobe aqui: Tiobe Index 2015. Nos dois índices Java e C# ocupam posições similares no ranking.

Uma ótima ilustração para os parágrafos acima pode ser utilizando a ótica da empresa SAP (Lider mundial de software corporativo). Seu principal produto o SAP R/3 para grandes empresas utiliza opcionalmente Java como linguagem de desenvolvimento e também no núcleo algumas de suas aplicações, já seu produto para pequenas empresas chamado Business One utiliza .NET. Independe dos motivos quem conhece as duas aplicações sabe da diferença enorme da tecnologia entre elas.

Para uma comparação técnica completa entre Java e C#, utilize o link a seguir:

C# for Java Developers: http://msdn.microsoft.com/en-us/library/ms228358(v=vs.90).aspx

Legados do Delphi

Java

Se tratando do Java seu conceito de objetos e sintaxe são baseados em C++ e em bibliotecas como a STL (Standard Template Library), mas o Delphi deixou um importante legado que é a IDE visual do NetBeans, originalmente denominada Xelfi e desenvolvida na República Tcheca em 1996. O objetivo era escrever uma IDE Java similar ao Delphi. O Xelfi foi a primeira IDE escrita em Java e posteriormente comprada pela SUN, que hoje pertence a Oracle e os exemplos oficiais são escritos para está IDE, inclusive a plataforma Java SEEE podem ser baixadas em pacotes (Bundle) que já contemplam o NetBeans, o servidor de aplicação GlassFish e os plugins para sua interface RAD (Desktop com Java Swing, Java FX e Web).

NetBeans History: https://netbeans.org/about/history.html

C#

Microsoft já namorava o arquiteto por trás do Delphi e de seu predecessor Turbo Pascal foi Anders Hejlsberg, onde se tornou o arquiteto responsável pelo projeto do C# e foi peça chave na criação do ambiente Microsoft .NET. Então é de se supor que o C# leva um pouco do Delphi dentro de si e podemos ver isto claramente na sintaxe de algumas instruções, principalmente no que se refere ao conceito de Eventos (Sender) e Setter (Getter e Setter).

C# Language: http://msdn.microsoft.com/pt-br/library/z1zx9t92.aspx

Métodos Getter e Setter

Em Java, C++ e C# existe o conceito de Getter e Setter  (inclusive sintaxe idênticas) para métodos de classe que em Delphi seria “equivalente” a uma Function e uma Procedure. Java e C++ seguem o padrão comum para o método Setter, em C# a Microsoft ensina o conceito como do Java quando você cria uma classe, o Visual Studio possui um atalho para criar Getter e Setter automaticamente mas não utilizando métodos, assim como os objetos do framework usam a sintaxe do Delphi, embutindo o conceito de Getter e Setter dentro de uma propriedade ou seja um método Getter para Java e C++, exemplo:

Getter e Setter  – Java: http://docs.oracle.com/javaee/6/tutorial/doc/gjbbp.html

Getter e Setter – C#: http://msdn.microsoft.com/en-us/library/w86s7x04.aspx

Então em C# é possível utilizar o conceito comum de Getter e Setter baseado em C++ e Java, utilizar o conceito de Get e Set embutido dentro de um método Getter (propriedades em C#) é meramente opcional e apenas uma questão de sintaxe do objeto, no qual desenvolvedores Delphi estão bem familiarizados. Inclusive o conceito propriedades nos objetos C#, torna a IDE do Visual Studio, na questão pratica do ponto de vista do desenvolvedor muito similar a IDE do Delphi.

Delphi Properties: http://docwiki.embarcadero.com/RADStudio/XE3/en/Properties

Código Fonte Java e C#


// Java

Objeto.setVisible(True); // Setter

// C# e Delphi (Propriedades)

Objeto.Visible = True; // Setter porem igual ao Delphi*

// * O conceito de operadores Java e C# são iguais
//   Delphi usa := para atribuir um valor

Eventos – Windows (Win32) – Java – C#

Outra similaridade do C# com o Delphi são os eventos, o conceito de eventos do Windows a grosso modo é uma fila de mensagens que pode ser utilizado pela API através da função WindowProc ou WndProc e é mais similar ao Java que utiliza tipos de mensagens implementadas na classe (ActionListener, ItemListener, MouseListner e outros) por exemplo, o Java possui um ActionListner que identifica o evento de  clique de qualquer botão independente da quantidade de botões em um formulário utilizando apenas um método, já o C# assim como Delphi e a IDE RAD do NETBEANS para JAVA tornam está funcionalidade mais fácil porem geram um código maior criando um evento de clique para cada botão da tela.

WindowProc: http://msdn.microsoft.com/pt-br/library/ms633573.aspx

Windows Messages: http://msdn.microsoft.com/pt-br/library/ms644927.aspx

Java Event Listner: http://docs.oracle.com/javase/tutorial/uiswing/events/intro.html

C# Event Handler: http://msdn.microsoft.com/en-us/library/system.eventhandler(v=vs.110).aspx

Assim, você pode testemunhar a “semelhança” do conceito de Object Sender entre Delphi e o .Net Framework (VB.NET e C#), utilize os links abaixo para entender a sintaxe final do evento de clique de um botão.

Object Sender Delphi: http://docwiki.embarcadero.com/RADStudio/XE4/en/Using_the_Sender_Parameter

Object Sender .NET Framework: http://msdn.microsoft.com/en-us/library/aa457091.aspx

Soluções e Projetos

Um ponto muito importante no desenvolvimento para o C# (Java já utilizava o Apache Ant e soluções recentes utilizam o Apache Maven) é que a Microsoft sabia da dificuldade de muitos programadores em utilizar o conceito Win32 na arquitetura do software e estruturação do código fonte, então o que em Delphi exigia abrir um projeto tipo Dll (não gerenciado) escrever uma biblioteca adequada, importar as funções para seu projeto principal e assim por diante. No Visual Studio surgiu o conceito de solução, que unido a um ambiente gerenciado torna possível criar vários projetos por solução,  onde o projeto principal contendo um Windows Form,  por exemplo gera um executável e os projetos desta solução contendo apenas biblioteca de classes geram automaticamente uma Dll (ambiente gerenciado) que é referenciada ao projeto principal e esta resolvido o problema que possuíamos no Delphi de alguns desenvolvedores criando executáveis enormes e códigos redundantes.

Solution and Project Basics: http://msdn.microsoft.com/en-us/library/b142f8e7.aspx

Concluindo

Vale a pena migrar para uma linguagem mais moderna sempre, inovação faz parte da área de software, todos nós só temos a ganhar, melhores ferramentas, técnicas mais modernas, maior produtividade, o importante é avaliar caso a caso e criar um projeto consistente e na medida para que este processo se realize da melhor maneira possível.

O que vimos neste post é apenas uma primeira visão da importância do Delphi e como o Delphi influenciou outras linguagens de programação. Para uma comparação mais precisa é necessário um projeto bem elaborado e fica a mensagem de que o Delphi  pode ter desaparecido da grande mídia mas continuará vivo por muito tempo.

Em C++ MFC não existe um componente pronto para fazer o trabalho de redimensionamento de painéis, mas existe uma classe que encapsula a API do Windows que faz exatamente este trabalho e esta classe é  chamada de CSplitterWnd, a classe fornece a funcionalidade de uma janela de divisão, que é uma janela que contém vários painéis. Um objeto CSplitterWnd geralmente é incorporado em um CFrameWnd pai ou um objeto filho CMDIChildWnd, isto significa que esta classe utiliza um tipo de aplicação MDI e não uma aplicação baseada em diálogos.

É possível criar um workaround que utiliza um componente estático Picture Control para criar um Splitter que redimensiona os componentes e não painéis como faz a classe CSplitterWnd. Existe vários componentes prontos na internet para fazer isto, mas nós vamos utilizar uma classe de código aberto para fazer este trabalho.

Splitter Control

Splitter Control

 

Visual Studio

Vamos utilizar a classe CSplitterControl que é Open Source que você encontra logo abaixo:

  1. Crie um projeto MFC Dialog Based.
  2. Coloque na tela dois componentes Picture Control e nomeie um como IDC_IMAGEM e outro como IDC_SPLITTER1.
  3. Coloque um componente Edit Control e crie uma variável chamada m_texto;
  4. No componente IDC_IMAGEM mude sua propriedade Type para Bitmap.
  5. No componente IDC_SPLITTER1 mude sua propriedade Visible para False.
  6. No componente Edit Control mude sua propriedade MultiLines para True.
  7. Disponha os componentes como na imagem abaixo:

    Splitter Control - Design

    Splitter Control – Design

  8. Adicione a classe CSplitterControl em seu projeto.
  9. Efetue o Download da imagem e a transforme em BMP, você pode usar o Paint Brush do Windows: https://desenvolvimentoaberto.org/wp-content/uploads/2014/04/balonismo.jpg
  10. Copie os trechos marcados do código abaixo para seu código gerado automaticamente.

 

Exemplo:

Neste exemplo utilizamos uma classe Open Source Splitter Control para criar um Splitter à partir de um componente Picture Control que controla o tamanho dos componentes entre eles controlando seu tamanho baseado no valor do Splitter.

Obs: Lembre-se que os exemplos abaixo utilizam classes e as classes em C++ são divididas em dois arquivos.

C++

Porgrama – Classe meusplittercpp.h

// código gerado automaticamente
// meusplittercppDlg.h : header file
//

#pragma once
#include "afxwin.h"
#include "SplitterControl.h"

class CmeusplittercppDlg : public CDialogEx
{

public:
	CmeusplittercppDlg(CWnd* pParent = NULL);	

	enum { IDD = IDD_MEUSPLITTERCPP_DIALOG };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	

	// Desenvolvimento Aberto
	virtual LRESULT DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam);

protected:
	HICON m_hIcon;

	// Desenvolvimento Aberto
	void Redimensiona(int delta);
	void Append(CString texto);
	CSplitterControl m_wndSplitter1;
	// ******

	virtual BOOL OnInitDialog();
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()
public:

	CEdit m_texto;
};

Porgrama – Classe meusplittercpp.cpp


// meusplittercppDlg.cpp : implementation file
//

#include "stdafx.h"
#include "meusplittercpp.h"
#include "meusplittercppDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CmeusplittercppDlg dialog

CmeusplittercppDlg::CmeusplittercppDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CmeusplittercppDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CmeusplittercppDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_EDIT1, m_texto);
}

BEGIN_MESSAGE_MAP(CmeusplittercppDlg, CDialogEx)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
//	ON_WM_CREATE()
END_MESSAGE_MAP()

// CmeusplittercppDlg message handlers

BOOL CmeusplittercppDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	SetIcon(m_hIcon, TRUE);
	SetIcon(m_hIcon, FALSE);		

	// Desenvolvimento Aberto
	// Inicializa componentes

	// Adiciona texto
	Append(L"O balonismo é um esporte aeronáutico praticado com um balão de ar quente.\n");
	Append(L"Possui adeptos em todo o mundo. No Brasil, o esporte começou a se popularizar\n");
	Append(L"a partir dos anos 90.\n");
	Append(L"O balão é considerado uma aeronave assim como avião, helicópteros e outros.\n");
	Append(L"Por esta razão o balão deve ter uma matricula (prefixo) registrado junto\n");
	Append(L"à ANAC, seu piloto deve possuir uma licença (brevê) específico para a pratica\n");
	Append(L"do balonismo também emitido pela ANAC.");

	// Cria componente Splitter Control baseado no componente Picture Control
	CRect rc;
	CWnd* pWnd;

	pWnd = GetDlgItem(IDC_SPLITTER1);
	pWnd->GetWindowRect(rc);
	ScreenToClient(rc);
	m_wndSplitter1.Create(WS_CHILD | WS_VISIBLE, rc, this, IDC_SPLITTER1);
	m_wndSplitter1.SetRange(50, 50, -1);

	// Carrega imagem

	CStatic * foto;
	CString imagem;

	foto = (CStatic *)GetDlgItem(IDC_IMAGEM);

	imagem = L"C:/Desenvolvimento Aberto/balonismo.bmp";

	HBITMAP pic = (HBITMAP)LoadImage(NULL, imagem, IMAGE_BITMAP,370, 260, LR_LOADFROMFILE);
	foto->SetBitmap(pic);

	// ***

	return TRUE;
}

void CmeusplittercppDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); 

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

HCURSOR CmeusplittercppDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

// Desenvolvimento Aberto
// Nosso código começa aqui

LRESULT CmeusplittercppDlg::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
	if (message == WM_NOTIFY)
	{
		if (wParam == IDC_SPLITTER1)
		{
			SPC_NMHDR* pHdr = (SPC_NMHDR*)lParam;
			Redimensiona(pHdr->delta);
		}
	}

	return CDialog::DefWindowProc(message, wParam, lParam);
}

void CmeusplittercppDlg::Redimensiona(int delta)
{
	CSplitterControl::ChangeWidth(GetDlgItem(IDC_IMAGEM), delta);
	CSplitterControl::ChangeWidth(&m_texto, -delta, CW_RIGHTALIGN);

	Invalidate();
	UpdateWindow();
}

void CmeusplittercppDlg::Append(CString texto)
{
	int tam1 = m_texto.GetWindowTextLengthW();
	m_texto.SetSel(tam1, tam1);
	m_texto.ReplaceSel(texto);

}

Splitter Control – Componente Open Source

Porgrama – Classe CSplitterControl.h

/**************CSplitterControl interface***********
*	Class name :CSplitterControl
*	Purpose: Implement splitter control for dialog
*			or any other windows.
*	Author: Nguyen Huy Hung, Vietnamese student.
*	Date:	May 29th 2002.
*	Note: You can use it for any purposes. Feel free
*			to change, modify, but please do not
*			remove this.
*	No warranty of any risks.
*	(:-)
*/
#if !defined(AFX_SPLITTERCONTROL_H__FEBBA242_B2C9_4403_B68D_E519D645CB15__INCLUDED_)
#define AFX_SPLITTERCONTROL_H__FEBBA242_B2C9_4403_B68D_E519D645CB15__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// SplitterControl.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CSplitterControl window
#define SPN_SIZED WM_USER + 1
#define CW_LEFTALIGN 1
#define CW_RIGHTALIGN 2
#define CW_TOPALIGN 3
#define CW_BOTTOMALIGN 4
#define SPS_VERTICAL 1
#define SPS_HORIZONTAL 2
typedef struct SPC_NMHDR
{
	NMHDR hdr;
	int delta;
} SPC_NMHDR;

class CSplitterControl : public CStatic
{
// Construction
public:
	CSplitterControl();

// Attributes
public:
protected:
	BOOL		m_bIsPressed;
	int			m_nType;
	int			m_nX, m_nY;
	int			m_nMin, m_nMax;
	int			m_nSavePos;		// Save point on the lbutton down
								// message
// Operations
public:

// Overrides
	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CSplitterControl)
	//}}AFX_VIRTUAL

// Implementation
public:
	static void ChangePos(CWnd* pWnd, int dx, int dy);
	static void ChangeWidth(CWnd* pWnd, int dx, DWORD dwFlag = CW_LEFTALIGN);
	static void ChangeHeight(CWnd* pWnd, int dy, DWORD dwFlag = CW_TOPALIGN);
public:
	void		SetRange(int nMin, int nMax);
	void		SetRange(int nSubtraction, int nAddition, int nRoot);

	int			GetStyle();
	int			SetStyle(int nStyle = SPS_VERTICAL);
	void		Create(DWORD dwStyle, const CRect& rect, CWnd* pParent, UINT nID);
	virtual		~CSplitterControl();

	// Generated message map functions
protected:
	virtual void	DrawLine(CDC* pDC, int x, int y);
	void			MoveWindowTo(CPoint pt);
	//{{AFX_MSG(CSplitterControl)
	afx_msg void	OnPaint();
	afx_msg void	OnMouseMove(UINT nFlags, CPoint point);
	afx_msg BOOL	OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
	afx_msg void	OnLButtonDown(UINT nFlags, CPoint point);
	afx_msg void	OnLButtonUp(UINT nFlags, CPoint point);
	//}}AFX_MSG

	DECLARE_MESSAGE_MAP()
};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_SPLITTERCONTROL_H__FEBBA242_B2C9_4403_B68D_E519D645CB15__INCLUDED_)

Porgrama – Classe CSplitterControl.cpp

// SplitterControl.cpp : implementation file
//

#include "stdafx.h"
#include "SplitterControl.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CSplitterControl

// hCursor1 is for vertiacal one
// and hCursor2 is for horizontal one
static HCURSOR SplitterControl_hCursor1 = NULL;
static HCURSOR SplitterControl_hCursor2 = NULL;

CSplitterControl::CSplitterControl()
{
	// Mouse is pressed down or not ?
	m_bIsPressed = FALSE;	

	// Min and Max range of the splitter.
	m_nMin = m_nMax = -1;
}

CSplitterControl::~CSplitterControl()
{
}

BEGIN_MESSAGE_MAP(CSplitterControl, CStatic)
	//{{AFX_MSG_MAP(CSplitterControl)
	ON_WM_PAINT()
	ON_WM_MOUSEMOVE()
	ON_WM_SETCURSOR()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSplitterControl message handlers

/****************************************************
*	Create(DWORD dwStyle, const CRect& rect, CWnd* pParent, nID)
*	Use this function instead of the CStatic::Create function
* Parameters: No need to explain (see MSDN (:-) )
*
****************************************************/
void CSplitterControl::Create(DWORD dwStyle, const CRect &rect, CWnd *pParent, UINT nID)
{
	CRect rc = rect;
	dwStyle |= SS_NOTIFY;

	// Determine default type base on it's size.
	m_nType = (rc.Width() < rc.Height())?
					SPS_VERTICAL:
					SPS_HORIZONTAL;

	if (m_nType == SPS_VERTICAL)
		rc.right = rc.left + 5;
	else // SPS_HORIZONTAL
		rc.bottom = rc.top + 5;

	CStatic::Create(L"", dwStyle, rc, pParent, nID);

	if (!SplitterControl_hCursor1)
	{
		SplitterControl_hCursor1 = AfxGetApp()->LoadStandardCursor(IDC_SIZEWE);
		SplitterControl_hCursor2 = AfxGetApp()->LoadStandardCursor(IDC_SIZENS);
	}

	// force the splitter not to be splited.
	SetRange(0, 0, -1);
}

// Set style for splitter control
// nStyle = SPS_VERTICAL or SPS_HORIZONTAL
int CSplitterControl::SetStyle(int nStyle)
{
	int m_nOldStyle = m_nType;
	m_nType = nStyle;
	return m_nOldStyle;
}
int CSplitterControl::GetStyle()
{
	return m_nType;
}

void CSplitterControl::OnPaint()
{
	CPaintDC dc(this); // device context for painting
	CRect rcClient;
	GetClientRect(rcClient);

	CBrush br, *pOB;
	CPen pen, *pOP;

	dc.Draw3dRect(rcClient, GetSysColor(COLOR_BTNHIGHLIGHT), GetSysColor(COLOR_BTNSHADOW));
	rcClient.DeflateRect(1,1,1,1);

	pen.CreatePen(0, 1, RGB(200, 200, 200));
	br.CreateSolidBrush(RGB(200, 220, 220));
	pOB = dc.SelectObject(&br);
	pOP = dc.SelectObject(&pen);

	dc.Rectangle(rcClient);

	// Restore pen and brush
	DeleteObject(dc.SelectObject(pOB));
	DeleteObject(dc.SelectObject(pOP));
}

void CSplitterControl::OnMouseMove(UINT nFlags, CPoint point)
{
	if (m_bIsPressed)
	{
		CWindowDC dc(NULL);
		DrawLine(&dc, m_nX, m_nY);

		CPoint pt = point;
		ClientToScreen(&pt);
		GetParent()->ScreenToClient(&pt);

		if (pt.x < m_nMin)
			pt.x = m_nMin;
		if (pt.y < m_nMin)
			pt.y = m_nMin;

		if (pt.x > m_nMax)
			pt.x = m_nMax;
		if (pt.y > m_nMax)
			pt.y = m_nMax;

		GetParent()->ClientToScreen(&pt);
		m_nX = pt.x;
		m_nY = pt.y;
		DrawLine(&dc, m_nX, m_nY);
	}
	CStatic::OnMouseMove(nFlags, point);
}

BOOL CSplitterControl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
	if (nHitTest == HTCLIENT)
	{
		(m_nType == SPS_VERTICAL)?(::SetCursor(SplitterControl_hCursor1))
			:(::SetCursor(SplitterControl_hCursor2));
		return 0;
	}
	else
		return CStatic::OnSetCursor(pWnd, nHitTest, message);
}

void CSplitterControl::OnLButtonDown(UINT nFlags, CPoint point)
{
	CStatic::OnLButtonDown(nFlags, point);

	m_bIsPressed = TRUE;
	SetCapture();
	CRect rcWnd;
	GetWindowRect(rcWnd);

	if (m_nType == SPS_VERTICAL)
		m_nX = rcWnd.left + rcWnd.Width() / 2;	

	else
		m_nY = rcWnd.top  + rcWnd.Height() / 2;

	if (m_nType == SPS_VERTICAL)
		m_nSavePos = m_nX;
	else
		m_nSavePos = m_nY;

	CWindowDC dc(NULL);
	DrawLine(&dc, m_nX, m_nY);
}

void CSplitterControl::OnLButtonUp(UINT nFlags, CPoint point)
{
	if (m_bIsPressed)
	{
		ClientToScreen(&point);
		CWindowDC dc(NULL);

		DrawLine(&dc, m_nX, m_nY);
		CPoint pt(m_nX, m_nY);
		m_bIsPressed = FALSE;
		CWnd *pOwner = GetOwner();
		if (pOwner && IsWindow(pOwner->m_hWnd))
		{
			CRect rc;
			int delta;
			pOwner->GetClientRect(rc);
			pOwner->ScreenToClient(&pt);
			MoveWindowTo(pt);

			if (m_nType == SPS_VERTICAL)
				delta = m_nX - m_nSavePos;
			else
				delta = m_nY - m_nSavePos;

			SPC_NMHDR nmsp;

			nmsp.hdr.hwndFrom = m_hWnd;
			nmsp.hdr.idFrom   = GetDlgCtrlID();
			nmsp.hdr.code     = SPN_SIZED;
			nmsp.delta = delta;

			pOwner->SendMessage(WM_NOTIFY, nmsp.hdr.idFrom, (LPARAM)&nmsp);
		}
	}

	CStatic::OnLButtonUp(nFlags, point);
	ReleaseCapture();
}

void CSplitterControl::DrawLine(CDC* pDC, int x, int y)
{
	int nRop = pDC->SetROP2(R2_NOTXORPEN);

	CRect rcWnd;
	int d = 1;
	GetWindowRect(rcWnd);
	CPen  pen;
	pen.CreatePen(0, 1, RGB(200, 200, 200));
	CPen *pOP = pDC->SelectObject(&pen);

	if (m_nType == SPS_VERTICAL)
	{
		pDC->MoveTo(m_nX - d, rcWnd.top);
		pDC->LineTo(m_nX - d, rcWnd.bottom);

		pDC->MoveTo(m_nX + d, rcWnd.top);
		pDC->LineTo(m_nX + d, rcWnd.bottom);
	}
	else // m_nType == SPS_HORIZONTAL
	{
		pDC->MoveTo(rcWnd.left, m_nY - d);
		pDC->LineTo(rcWnd.right, m_nY - d);

		pDC->MoveTo(rcWnd.left, m_nY + d);
		pDC->LineTo(rcWnd.right, m_nY + d);
	}
	pDC->SetROP2(nRop);
	pDC->SelectObject(pOP);
}

void CSplitterControl::MoveWindowTo(CPoint pt)
{
	CRect rc;
	GetWindowRect(rc);
	CWnd* pParent;
	pParent = GetParent();
	if (!pParent || !::IsWindow(pParent->m_hWnd))
		return;

	pParent->ScreenToClient(rc);
	if (m_nType == SPS_VERTICAL)
	{
		int nMidX = (rc.left + rc.right) / 2;
		int dx = pt.x - nMidX;
		rc.OffsetRect(dx, 0);
	}
	else
	{
		int nMidY = (rc.top + rc.bottom) / 2;
		int dy = pt.y - nMidY;
		rc.OffsetRect(0, dy);
	}
	MoveWindow(rc);
}

void CSplitterControl::ChangeWidth(CWnd *pWnd, int dx, DWORD dwFlag)
{
	CWnd* pParent = pWnd->GetParent();
	if (pParent && ::IsWindow(pParent->m_hWnd))
	{
		CRect rcWnd;
		pWnd->GetWindowRect(rcWnd);
		pParent->ScreenToClient(rcWnd);
		if (dwFlag == CW_LEFTALIGN)
			rcWnd.right += dx;
		else if (dwFlag == CW_RIGHTALIGN)
			rcWnd.left -= dx;
		pWnd->MoveWindow(rcWnd);
	}
}

void CSplitterControl::ChangeHeight(CWnd *pWnd, int dy, DWORD dwFlag)
{
	CWnd* pParent = pWnd->GetParent();
	if (pParent && ::IsWindow(pParent->m_hWnd))
	{
		CRect rcWnd;
		pWnd->GetWindowRect(rcWnd);
		pParent->ScreenToClient(rcWnd);
		if (dwFlag == CW_TOPALIGN)
			rcWnd.bottom += dy;
		else if (dwFlag == CW_BOTTOMALIGN)
			rcWnd.top -= dy;
		pWnd->MoveWindow(rcWnd);
	}
}

void CSplitterControl::ChangePos(CWnd* pWnd, int dx, int dy)
{
	CWnd* pParent = pWnd->GetParent();
	if (pParent && ::IsWindow(pParent->m_hWnd))
	{
		CRect rcWnd;
		pWnd->GetWindowRect(rcWnd);
		pParent->ScreenToClient(rcWnd);
		rcWnd.OffsetRect(-dx, dy);

		pWnd->MoveWindow(rcWnd);
	}
}

void CSplitterControl::SetRange(int nMin, int nMax)
{
	m_nMin = nMin;
	m_nMax = nMax;
}

// Set splitter range from (nRoot - nSubtraction) to (nRoot + nAddition)
// If (nRoot < 0)
//		nRoot =  <current position of the splitter>
void CSplitterControl::SetRange(int nSubtraction, int nAddition, int nRoot)
{
	if (nRoot < 0)
	{
		CRect rcWnd;
		GetWindowRect(rcWnd);
		if (m_nType == SPS_VERTICAL)
			nRoot = rcWnd.left + rcWnd.Width() / 2;
		else // if m_nType == SPS_HORIZONTAL
			nRoot = rcWnd.top + rcWnd.Height() / 2;
	}
	m_nMin = nRoot - nSubtraction;
	m_nMax = nRoot + nAddition;
}