This question already has answers here:
Adjust Column width DBGrid
(6 answers)
Closed 8 years ago.
I am new to delphi :). I have made a simple app that is able to write and read from SQL server database. I am displaying query results in DBGrid.
Currently, I have only 3 columns in my DB (ID, Name, Surname). DBGrid displays them in a waz that I dont like. I can see ID (its width is accurate) and Name, which is stretched all the way to the right, so I have to use horizontal scrollbar to see Surname, which is also extremely wide.
How can I tell DBGrid to adjust width of columns to the widest row?
My app looks like this:
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
ADOQuery1.SQL.Text := 'INSERT INTO [dbo].[client] ([Meno],[Priezvisko]) ' +
'VALUES(:Meno, :Priezvisko)';
ADOQuery1.Parameters.ParamByName('Meno').Value := Edit1.Text;
ADOQuery1.Parameters.ParamByName('Priezvisko').Value := Edit2.Text;
ADOQuery1.ExecSQL;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
ADOQuery2.SQL.Text := 'SELECT * FROM [dbo].[client] WHERE Meno = :Meno';
ADOQuery2.Active := True;
ADOQuery2.Parameters.ParamByName('Meno').Value := Edit1.Text;
ADOQuery2.ExecSQL;
end;
end.
I have found this TUTORIAL but according to this, you have t double click on chosen coumn to adjust its width, and I had also problems with this line
ColumnWidthHelper.MaxWidth := Max(ColumnWidthHelper.MaxWidth, DBGrid1.Canvas.TextWidth(Column.Field.DisplayText)) ;
It is not recognizing the Max function
Any help would be appreciated :)
The Max function is in the Math unit. Add this to your Uses clause.
Related
I populate ScrollBoxin the alike way:
procedure TForm1.FormCreate(Sender: TObject);
var
i: word;
begin
for i := 1 to 3 do
begin
with TLabel.Create(ScrollBox1) do
begin
Parent := ScrollBox1;
Top := 1000;
AutoSize := False;
Align := alTop;
Height := 25;
Caption := 'Label' + IntToStr(i);
end;
end;
ScrollBox1.Realign;
end;
When the code is run under Delphi I get the follwong result:
The order of items is proper.
But when I call the same code under Lazarus I get:
The order of items is reverse. I can solve the issue by reverse creation of ScrollBox children and/or adding {IFDEF ...} but I suspect this is not reliable. Adding compiler switches will double the volume of code making it bulky and difficult to read.
Is there a way to do unified reliable Delphi-Lazarus code for this purpose?
APPENDED
explanation on comment of #TomBrunberg
If I create chidren in reverse order (for instance for i := 3 downto 1) I get the opposite result: Delphi produces reverse and Lazarus - direct order. That is why I was saying about doubling of code.
APPENDED 2
on note of Tom Brunberg
When the same code is called from a Button onClick event handler the code behaviour becomes opposite (and again different in Lazarus and in Delphi).
APPENDED 3
Can I trust for i := 1 to 3... Top := 1000 + i; as it gives the expected result?
I've read this manual , and follow it , but the highlighting not working.
What I have?
Create new VCL application.
Drop a TEdit and TButton components in the form (To pass the value).
Drop a TfrxReport on the form.
Open the report in design mode.
Drop a ReportTile band in the report page.
Drop a TfrxMemoView in the ReportTitle band.
Add conditions:
1- Value <= 0 -> Red color
2- Value > 0 -> Green color
The fill color of the memo still Black even the value is >0 or <=0.
The question:
Why this conditions not working? and how can I make the conditions work?
Update:
The value was passed to the TfrxMemoView component as :
procedure TForm1.Button1Click(Sender: TObject);
Var Mem : TfrxMemoView;
begin
Mem := frxReport1.FindObject('Memo1') as TfrxMemoView;
Mem.Text := Edit1.Text;
frxReport1.ShowReport();
end;
None of the rules is applied because the Value property remains NULL. To assign a constant value from Delphi code you can either write a constant expression, for example:
procedure TForm1.Button1Click(Sender: TObject);
var
Memo: TfrxMemoView;
begin
Memo := frxReport1.FindObject('Memo1') as TfrxMemoView;
Memo.Text := Format('[%s]', [Edit1.Text]);
frxReport1.ShowReport;
end;
In the above code I've omitted check if the control was found. And, you need to be careful with the input text. It accepts only floating point values in format that won't collide in decimal separator with separator(s) defined in the ExpressionDelimiters property.
Or simply set the Value property as well:
procedure TForm1.Button1Click(Sender: TObject);
var
Memo: TfrxMemoView;
begin
Memo := frxReport1.FindObject('Memo1') as TfrxMemoView;
Memo.Text := Edit1.Text;
Memo.Value := StrToFloat(Edit1.Text);
frxReport1.ShowReport;
end;
In this one the check if the control was found is missing as well. And the conversion to float is not necessary there. The Value can be just a string convertible to float.
I have dbgrid which display column subtotal and column percentage, how to display column percentage from this formula : (subtotal / grandtotal) * 100% ? for detail information please see below picture
I couldnt modify my SQL, because my SQL is very complicated, so i think solution maybe use calculated field, doesnt it? Could someone help me to solve this problem.
Thanks in advanced.
The following assumes that your dataset doesn't actually contain the last row you've shown, the one that contains "111077, 100" - if it does, then the steps I show below to calculate the GrandTotal are unnecessary, and you only need to populate the Percent calculated field, which is trivial.
If your DataSet is a TClientDataSet, you can implement the Percent values quite easily using the
combination of a TAggregateField to represent the GrandTotal and a calculated field to represent each data row's contribution towards the GrandTotal. See code below.
If you are not using a TClientDataSet already then you have several options,
including
If your DataSet is of a type which supports aggregate fields then you can do the equivalent of the code below.
Use your existing DataSet as the dataset source of a TDataSetProvider, and use the TDataSetProvider as the Provider of a TClientDataSet and use the TClientDataSet to supply the data to your grid.
Don't use a TClientDataSet and/or TAggregateField and instead do similar to what is shown below with your existing DataSet, but make the Percent field an fkInternalCalc field if your DataSet type supports it, or an fkCalculated one if not, omit the GrantTotal TAggregateField field and calculate the GrandTotal in code. One way to do this would be to calculate it by a once-only traversal of the dataset (while not DataSet.Eof ...) after you open it.
In the code below, I've created all the fields in code, rather than using the Object Inspector's Fields editor, so you can easily see exactly what are the minimum settings necessary to get a TAggregateField to work.
Note: I could be wrong but don't think you could get a standard TDBGrid to display the final, 100%, row of your screenshot. Somthething similar could be done using the Developer Express TcxGrid, amongst others, but if you need a TDBGrid to do this, you should ask how to in a new question.
Code
TForm1 = class(TForm)
CDS: TClientDataSet;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
procedure CDSCalcFields(DataSet: TDataSet);
procedure FormCreate(Sender: TObject);
private
CDSID : TIntegerField;
CDSTotal : TCurrencyField;
CDSPercent : TFloatField;
CDSGrandTotal : TAggregateField;
public
procedure SetUp;
end;
[...]
procedure TForm1.SetUp;
var
i : Integer;
begin
CDSID := TIntegerField.Create(Self);
CDSID.FieldName := 'ID';
CDSID.FieldKind := fkData;
CDSID.DataSet := CDS;
CDSTotal := TCurrencyField.Create(Self);
CDSTotal.FieldName := 'Total';
CDSTotal.FieldKind := fkData;
CDSTotal.DataSet := CDS;
CDSPercent := TFloatField.Create(Self);
CDSPercent.FieldName := 'Percent';
CDSPercent.FieldKind := fkInternalCalc;
CDSPercent.DataSet := CDS;
CDSGrandTotal := TAggregateField.Create(Self);
CDSGrandTotal.FieldName := 'GrandTotal';
CDSGrandTotal.FieldKind := fkAggregate;
CDSGrandTotal.Expression := 'Sum(Total)';
CDSGrandTotal.DataSet := CDS;
CDSGrandTotal.Active := True;
CDS.OnCalcFields := CDSCalcFields;
CDS.IndexFieldNames := 'ID';
CDS.CreateDataSet;
for i := 1 to 2 do begin
CDS.InsertRecord([i, i]);
end;
CDS.First;
end;
procedure TForm1.CDSCalcFields(DataSet: TDataSet);
var
Value : Double;
V : Variant;
begin
V := CDSGrandTotal.Value;
if not VarIsNull(V) then begin
Value := CDSTotal.AsFloat;
Value := Value * 100 / V;
CDSPercent.Value := Value;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
SetUp;
end;
I have a cxGrid where I apply a filter to select certain records. When that is done I want to be able to update a field/column in the grid to mark each record that is to be used for the next operation.
I haven't been able to figure this out
Maybe I haven't been specific enough when describing my problem.
I have the cxGrid where I have applied a filter selecting some records.
What I then need to do is to click a columnheader and then have a field called fldselected set to True for these records.
What your updated q is asking for is straightforward and as usual with Devex stuff, it's
all in the OLH as long as you can find your way into it.
A way to find which rows currently match the filter is to use the
cxGrid1DBTableView1.DataController.FilteredRecordIndex[]
property. You can then find that record in the dataset to process it in some way using
cxGrid1DBTableView1.DataController.LocateByKey().
Update: The original version of this answer assumed that the dataset had an integer ID field.
As the OP has said he uses GUIDs instead, I've upddated it accordingly.
Assuming the TClientDataSet CDS1 has fields Guid : TGuidField, Name : TStringfield, size 32
and Selected : TBooleanField and is connected to
a cxDBTableView, with filtering enabled, of a TcxGrid.
Make sure the cxGrid1DBTableView1.DataController.KeyFieldNames is set to 'Guid'.
Add a regular TDBGrid to the form and point it at the same datasource as the TcxGrid. The point
of this is to make it easy to verify that the code is working as required.
Add the code below to the unit, and point cxDBTableView1's OnColumnHeaderClick at
the handler cxGrid1DBTableView1ColumnHeaderClick, and the form's OnCreate at the FormCreate.
Compiler & run
Code:
procedure TForm1.cxGrid1DBTableView1ColumnHeaderClick(Sender: TcxGridTableView;
AColumn: TcxGridColumn);
begin
if AColumn = cxGrid1DBTableView1Name then
ProcessFilteredRecords;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
AGuid : TGuid;
i : Integer;
lResult : Longint;
begin
CDS1.IndexFieldNames := 'Name';
CDS1.CreateDataSet;
for i:= 0 to 6 do begin
lResult := SysUtils.CreateGUID(AGuid);
CDS1.Insert;
CDS1.FieldByName('Name').AsString := Chr(Ord('A') + i);
CDS1.FieldByName('Guid').AsString := GuidToString(AGuid);
CDS1.FieldByName('Selected').AsBoolean := False;
CDS1.Post;
end;
CDS1.First;
end;
procedure TForm1.ProcessFilteredRecords;
var
V : Variant;
i,
Index: Integer;
BM : TBookMark;
begin
BM := CDS1.GetBookMark;
CDS1.DisableControls;
try
for i := 0 to cxGrid1DBTableView1.DataController.FilteredRecordCount - 1 do begin
Index := cxGrid1DBTableView1.DataController.FilteredRecordIndex[i];
// Next, get the GUID value of the row
V := cxGrid1DBTableView1.DataController.Values[Index, 0];
if cxGrid1DBTableView1.DataController.LocateByKey(V) then begin
CDS1.Edit;
CDS1.FieldByName('Selected').AsBoolean := True;
CDS1.Post;
end;
end;
finally
CDS1.EnableControls;
CDS1.GotoBookmark(BM);
CDS1.FreeBookmark(BM);
end;
end;
Check out https://www.devexpress.com/Support/Center/Question/Details/A1095, the article from Dev Express. Don't let the fact that the article is 11 years old fool you. The same technique still applies. And you can set this up either in code or in the grid editor.
Create the column in the grid editor.
Set the columns DataBinding.ValueType to Boolean (if that's what you want the checkbox to represent)
Set the Data Controller's KeyFieldNames property. Very important! I have spent hours scratching my head with an non-functioning unbound column only to find that the KeyFieldNames wasn't set.
An unbound column can be referenced in your next operation using the DataController Records or Values array, depending on how you set that up. Because it is unbound you cannot reference it through the underlying DataSet though.
I use D2007 and Teechart v7.10 standard
I want to show the data per week.
I set
Series1.GetHorizAxis.Increment := DateTimeStep[dtOneWeek];
and
chart1.TopAxis.minimum = //a Monday's date
The problem is that the vertical separation lines and their dates aren't on
Mondays.
Is any way to force these marks to be at the start of week ?
thanks in advance
Using TGantSeries
I've tried with the actual version (v2013.09) and I always get a bottom axis with labels on Mondays using the following simple example:
uses GanttCh;
procedure TForm1.FormCreate(Sender: TObject);
begin
Chart1.View3D:=false;
Chart1.Legend.Visible:=false;
Chart1.AddSeries(TGanttSeries).FillSampleValues;
with Chart1.Axes.Bottom do
begin
LabelsAngle:=90;
Increment:=DateTimeStep[dtOneWeek];
DateTimeFormat:='ddd dd/mm/yyyy';
end;
end;
However, I see in TeeChart v7 the code above shows different weekdays in the labels depending on the values in the series. Then, the only solution I can think on is to use custom labels. Ie:
uses GanttCh;
procedure TForm1.FormCreate(Sender: TObject);
var tmpDate: TDateTime;
begin
Chart1.View3D:=false;
Chart1.Legend.Visible:=false;
Chart1.AddSeries(TGanttSeries).FillSampleValues;
Chart1.MarginBottom:=15;
with Chart1.Axes.Bottom do
begin
LabelsAngle:=90;
//Increment:=DateTimeStep[dtOneWeek]; //this won't take effect with custom labels
DateTimeFormat:='ddd dd/mm/yyyy';
tmpDate:=(Chart1[0] as TGanttSeries).StartValues.MinValue;
while DayOfWeek(tmpDate) <> 2 do
tmpDate:=tmpDate+1;
Items.Clear;
repeat
Items.Add(tmpDate);
tmpDate:=tmpDate+7;
until tmpDate>=(Chart1[0] as TGanttSeries).EndValues.MaxValue;
end;
end;
Note in the solution above there's no antioverlap control for the axis labels