additional part shouldn't visible
The last post was about using ListView controls of the VCL as the standard stream for C++. Now there is no support for the Windows control in the FMX- Framework and it needs another solution. This can be a StringGrid.
Before I start with this one, I rearrange the previous solution. I have taken over the uppermost part of the header file "MyStreamBuf.h" once again completely, in order to make a comparison possible.
#ifndef MyStreamBuf
#define MyStreamBuf
#if !defined BUILD_WITH_VCL && !defined BUILD_WITH_FMX
#error A framework variant must be selected for this application
#endif
#if defined BUILD_WITH_VCL
#include <Vcl.StdCtrls.hpp>
#endif
#if defined BUILD_WITH_FMX
#include <Fmx.StdCtrls.hpp>
#include <FMX.Grid.hpp>
#include <FMX.Grid.Style.hpp>
#endif
#include < MyDelphiHelper.h >
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <vector>
#include <tuple>
#include <map>
enum class EMyAlign : int { undefined, left, center, right, unknown };
using tplList = std::tuple;
Since the list columns in the previous approach were built using a tuple in which the VCL enumeration type TAlignment was used for column alignment, which is not available in FMX, or which is structured differently, I first define my own enumeration EMyAlign with possible column alignments. With the help of this I redefine the tuple type tplList for the definition of list columns.
Since an important element of the implementation lies in the overfflow() method, and since the character '\n' and the character '\t' must be handled here, I define a new base class MyListStreamBufBase for all implementations of list displays. Here the overflow() method is implemented centrally.
Since the respective line feed is not central, a new pure virtual method NewLine() is defined, which must be implemented in the respective concrete classes.
class MyListStreamBufBase : public MyStreamBufBase {
public:
MyListStreamBufBase(void) { }
virtual ~MyListStreamBufBase(void) { }
virtual int overflow(int c) {
switch(c) {
case '\n':
Write();
NewLine();
break;
case '\t':
Write();
break;
default:
os.put(c);
}
return c;
}
virtual void NewLine(void) = 0;
};
Now some changes to the existing class and auxiliary methods have to be made. First of all, method AddColumns must be newly implemented for the ListViews.
inline void AddColumns(TListView* lv, std::vector<tplList> const& captions) {
static std::map<EMyAlign, TAlignment> Align2 = {
{ EMyAlign::undefined, taLeftJustify },
{ EMyAlign::left, taLeftJustify },
{ EMyAlign::center, taCenter },
{ EMyAlign::right, taRightJustify },
{ EMyAlign::unknown, taLeftJustify } };
for(auto const& caption : captions) {
TListColumn* nc = lv->Columns->Add();
nc->Caption = std::get<0>(caption).c_str();
nc->Alignment = Align2[std::get<1>(caption)];
nc->Width = std::get<2>(caption);
}
}
To do this, I define a static variable with an associative array in which I assign a matching value from the VCL enumeration TAlignment to each value from my previously defined EMyAlign enumeration. I use this in the loop to assign the new framework dependent enumeration to the concrete value. So I eliminated the dependency on a concrete framework in a simple step.
Finally I have to adapt the previous class TMyListViewStreamBuf for the VCL. For this the previous base class is exchanged, the previous method overflow() is deleted here and instead the line feed is performed by the new virtual method NewLine(), which is implemented here.
class MyListViewStreamBuf : public MyListStreamBufBase {
private:
TListView* lvValue;
TListItem* lvItem;
bool boNewItem;
public:
MyListViewStreamBuf(TListView* para, bool boClean = true) : MyListStreamBufBase() {
lvValue = para;
lvItem = 0;
if(boClean) lvValue->Items->Clear();
lvValue->ViewStyle = vsReport;
lvValue->RowSelect = true;
boNewItem = true;
}
virtual ~MyListViewStreamBuf(void) {
lvValue = 0;
lvItem = 0;
}
virtual void NewLine(void) { boNewItem = true; }
virtual void Write(void) {
if(boNewItem) {
lvItem = lvValue->Items->Add();
lvItem->Caption = os.str().c_str();
boNewItem = false;
}
else {
lvItem->SubItems->Add(os.str().c_str());
}
os.str("");
return;
}
};
Now I can take care of the implementation for StringGrids as output for C++ standard streams. For this purpose I add the following source code to the header file "MyStreamBuf.h" and use the conditional translation again.
Damit kann ich mich jetzt um die Implementierung für StringGrids als Ausgabe für C++ Standard- Streams kümmern. Dazu füge ich den folgenden Quelltext in die Headerdatei "MyStreamBuf.h" ein und nutze hier auch wieder die bedingte Übersetzung.
class MyListViewStreamBuf : public MyListStreamBufBase {
private:
TStringGrid* lvValue;
int iColumn, iRow;
public:
MyListViewStreamBuf(TStringGrid* para, bool boClean = true) : MyListStreamBufBase() {
lvValue = para;
iColumn = 0;
iRow = 0;
lvValue->ReadOnly = true;
lvValue->Options <<= TGridOption::RowSelect;
lvValue->Options >>>= TGridOption::ColumnMove;
lvValue->Options <<<= TGridOption::AlwaysShowSelection;
if(boClean) lvValue->RowCount = 0;
}
virtual ~MyListViewStreamBuf(void) {
lvValue = 0;
}
virtual void NewLine(void) { iColumn = 0; }
virtual void Write(void) {
if(iColumn == 0) {
lvValue->RowCount += 1;
iRow = lvValue->RowCount - 1;
}
lvValue->Cells[iColumn++][iRow] = os.str().c_str();
os.str("");
return;
}
};
The conversion is done with the help of the private data element lvValue of the type of a pointer to a TStringGrid. In addition there is an integer with the current column number. These are passed as parameters in the constructor and the column number is set to 0. The 0 means a new empty row, so the value always shows the number of already added columns in a new row. There is also a variable with the current number of rows, which is used for the Write() method. This is also initialized with 0 in the structure. Then there are some settings for the StringGrid to ensure a uniform appearance as a list. Since it is only about output, the StringGrid is set to read only, and the whole row is always selected and displayed even if the focus is not active. I also prevent columns from being moved at runtime to ensure that output is not correct.
In the destructure, the pointer to the concrete data element is set to 0, not only to prevent deletion, but also to simplify a review. Everyone will recognize that no deletion is allowed here.
In the method NewLine() only the column number iColumn is set to 0.
The actual output to the concrete StringGrid control is also done here in the virtual Write() method. If the column number iColumn == 0, a new row is inserted by accessing the RowCount property of the control. After this is done, it is stored in the iRow variable. In the second paragraph, the actual output is done using the Cells property. The Postfix Increment Operator is used to increase the column number after the output. The buffer for the new column is then emptied.
Here, too, we need an auxiliary method AddColumns() to build the columns. Here, too, a concrete control is passed as the first parameter, followed by a vector with our, by introducing the enumeration EMyAlign platform independent column definitions.
inline void AddColumns(TStringGrid* lv, std::vector<tplList> const& captions) {
static std::map<EMyAlign, TTextAlign> Align2 = {
{ EMyAlign::undefined, TTextAlign::Leading },
{ EMyAlign::left, TTextAlign::Leading },
{ EMyAlign::center, TTextAlign::Center },
{ EMyAlign::right, TTextAlign::Trailing },
{ EMyAlign::unknown, TTextAlign::Leading } };
for(auto const& caption : captions) {
TStringColumn* col = new TStringColumn(lv);
//col->TextSettings->HorzAlign = Align2[std::get<1>(caption)];
col->Header = std::get<0>(caption).c_str();
col->Width = std::get<2>(caption);
lv->AddObject(col);
}
}
Unfortunately the FMX- Framework is still changing. Thus properties are moved, the access is partly by special interfaces following the COM syntax a little bit getting used to. Unfortunately, in the current version of C++Builder 10.3 this affects the column alignment in the grid. I have simply commented this out, since it is not necessary for the representation of the procedure. These constant changes in frameworks are the main reason for the encapsulation of these frameworks from their business source code and are shown here.
Finally, the special Activate()- method for the template in the class TMyStreamWrapper is added.
#if defined BUILD_WITH_FMX
template<>
inline void TMyStreamWrapper::Activate<TStringGrid>(TStringGrid* element) {
Check();
old = str.rdbuf(new MyListViewStreamBuf(element));
}
#endif
Keine Kommentare
3 days
8 Participants
3 Days
8 Participants
In this seminar you will get a compact introduction to the latest version of C++Builder (currently 10.3.3). C++Builder is a cross-platform development tool that allows you to develop high-performance, native applications for the target platforms Windows (32bit, 64bit), Mac OS (32bit), iOS and Android.
5 Days
8 Participants
As the title of the course says, this course is about developing native applications for mobile devices (app) with C++Builder (currently 10.3.3).
2 Days
8 Participants
This course is designed for C++ developers who want to use the C++Builder and deepen their knowledge. In addition to using the components and the properties of the C++ language, it is especially important to combine both in a sensible way.
3 Days
8 Participants
This seminar is aimed at C++ programmers who want to extend access to databases in their applications. This seminar explains the main components of the C++ Builder for database access, but also shows how to integrate them into a clean application architecture.
3 Days
8 Participants
This seminar is aimed at C++ programmers who have gained experience with the database components of the C++ Builder and now want to develop scalable database applications. In this seminar the importance of ANSI C++ - interfaces between the business logic of an application and the database is shown.
3 Days
6 Participants
This course is aimed at software developers who want to learn the elements of the C programming language as a basis for a later entry into object-oriented programming with C++.
3 Days
8 Participants
This seminar is aimed at programmers who want to switch from C to C++. The most important extensions of the C++ language and the differences to C are discussed in detail.
3 Days
5 Participants
This seminar is aimed at programmers who want to switch from C to C++. The most important extensions of the C++ language and the differences to C are discussed in detail.
5 Days
8 Participants
We were often asked if we could do a training "with everything". A larger example, starting with an idea, developing an architecture from it, and then implementing it independently in C++. Just use the extensions without letting them determine our structure and architecture, and if possible, combine this with developing access for mobile applications.
5 Days
8 Participants
In this seminar you will learn which Open Source tools you can use for your daily work with the current C++ Builder 10.3.3. This includes solutions for documenting, metrics, code analysis and code management.
2 Days
6 Participants