Leaderboard
Popular Content
Showing content with the highest reputation on 07/01/23 in all areas
-
TImage inside TComponent serializaton
programmerdelphi2k replied to isola's topic in RTL and Delphi Object Pascal
... -
TImage inside TComponent serializaton
programmerdelphi2k replied to isola's topic in RTL and Delphi Object Pascal
or more simple way; you can use only "Memory streams instead save on file" procedure TForm1.Button1Click(Sender: TObject); var LMemStream: TMemoryStream; LStrStream: TStringStream; begin LMemStream := TMemoryStream.Create; try LMemStream.WriteComponent(Image1); LMemStream.SaveToFile('myTImage.bin'); // LStrStream := TStringStream.Create; try LMemStream.Position := 0; ObjectBinaryToText(LMemStream, LStrStream); LStrStream.Position := 0; Memo1.Text := LStrStream.DataString; // showing the propers finally LStrStream.Free; end; // Image1.Picture.Bitmap := nil; // just for test... finally LMemStream.Free; end; end; procedure TForm1.Button2Click(Sender: TObject); var LMemStream: TMemoryStream; begin LMemStream := TMemoryStream.Create; try LMemStream.LoadFromFile('myTImage.bin'); LMemStream.ReadComponent(Image1); finally LMemStream.Free; end; end; -
TImage inside TComponent serializaton
Uwe Raabe replied to isola's topic in RTL and Delphi Object Pascal
Make the Owner of that TImage instance your component and set csSubComponent in the TImages ComponentStyle. That means, the image should be created like this: FImage := TImage.Create(Self); FImage.SetSubComponent(True); -
The '%f' specifier in ...printf() style functions expects 'double' (or 'long double' if you use '%Lf'), not 'float'. There is no specifier that accepts 'float'. So, you will have to cast the values when passing them in, eg: snprintf(buffer, sizeof(buffer), "%f", (double) *zMap); // or: double(*zMap), or static_cast<double>(*zMap) Alternatively, you can format the 'float' to a C++ 'std::string' first, and then copy it into your buffer, eg: std::ostringstream oss; oss << *zMap; strncpy(buffer, oss.str().c_str(), sizeof(buffer)-1); Or: strncpy(buffer, std::format("{}", *zMap).c_str(), sizeof(buffer)-1); Of course, being a Delphi guy, you could just use the Delphi RTL in C++Builder to convert the 'float' to a 'System::String' first, and then copy it into your buffer, eg: strncpy(buffer, AnsiString(FloatToStr(*zMap)).c_str(), sizeof(buffer)-1); // or simpler: strncpy(buffer, AnsiString(*zMap).c_str(), sizeof(buffer)-1); Or, simply get rid of the buffer altogether and use the 'std::string'/'System::(Ansi)String' as-is instead. That being said, your two examples are logically equivalent, so if the above approaches don't fix the issue, then make sure the pointer being returned by SomeFunctionThatReturnsABuffer() is actually valid to begin with (ie, that the memory buffer is not being freed before you copy the data, etc).
-
Load HTML from string in TWebBrowser in Firemonkey
programmerdelphi2k replied to jerik's topic in FMX
as rule, any "critical action" should be in another place... but if strictly necessary use it! -
Load HTML from string in TWebBrowser in Firemonkey
programmerdelphi2k replied to jerik's topic in FMX
procedure TForm1.Button1Click(Sender: TObject); var HTML: string; BURL: string; begin BURL := ''; // // HTML := TResourceStream.Create(HInstance, 'index', RT_RCDATA).ToString(); HTML := '<!DOCTYPE html> <html lang="en">' + { } '<head> <meta charset=utf-8> ' + { } '<title>Test</title><style>body {background:red;}</style> ' + { } '</head><body> ' + { } '<h1>TEST</h1> <p>Dette er en tekst!</p> ' + { } '</body> </html>'; // WebBrowser1.LoadFromStrings(HTML, BURL); end; CONST X LITERAL -
Unless you have a flawless crystal ball that sees very far into the future and all of the ways the class might be used, you have no way to predict which, if any, of the state variables you're adding to your class might change over time. By using a property to expose all of these state variables, you allow the class to evolve without impacting existing clients of the class in any way. Using properties is more of an on-going "best practice" as far as I'm concerned, that's simply rooted in years of having to deal with the consequences of NOT using them in large projects. It's the same reason people use #define for constants in C rather than the values themselves. They're read-only, but you can change them in one place without impacting all of the code. In fact, this is one thing I find totally insane about CSS -- you cannot define symbolic constants for anything, and so if you want to change a property of a common thing, you have to edit the value of that property everywhere! Properties in Delphi take it another step by allowing you to simply refer to the variable name itself if you don't need to do anything with it, or refer to a proc or func that can do pretty much anything with it. This is particularly handy in situations where you want to use Property Injection after a object has been created. Every assignment can have a setter that sets a flag saying whether a required parameter was set. Then if you try using a method and a required parameter has NOT been set, it's easy to raise an exception that uses very little overhead in that determination. Thinking about these things becomes second nature after a while, and it's great to have a practice to follow that makes it easy to change your mind about things later on.
-
If there's anything you take away from this discussion, it should be that understanding Properties (with their getter and setter methods) is helpful, but when you get into discussions involving passing data in and out of Forms, it usually turns into something more akin to a religious debate. There are lots of common practices that apply to interacting with classes, starting with the basic principles of "objects" which are: encapsulation, inheritance, and polymorphism. The whole point of classes is twofold: (1) model behavior; and (2) maintain state. In most GUI-based apps, a form's behavior is going to vary with how it's used: maybe to simply display a message; get an input value; display and/or edit some data; and more complex forms that combine several of these. However, there are several ways to get and set the state (field values) inside of that form class. Worse, the way people employ these methods is very inconsistent, which can be observed by looking at a lot of different code written by may different people. Properties are properties, and Dalija did a great job explaining them. The "why" part wasn't really addressed: it's because properties afford the programmer some latitude in terms of 'encapsulation'. For instance, you put 'i' and 's' in front of some property names to clue the programmer into how they're used (handy if you're not using an IDE that already ensures the types match up). The point here is, right now you might have implemented those as one type that makes sense today; but in a month or a year down the road, it might make sense to change those types or where the values are stored. And that's where Getters come in -- they enforce the promise that the property is whatever type you say it is, regardless of how it occurs inside the class. So an integer could change to a string, or vice versa, because of some other change. If you accessed that variable directly by name, and its type changed, that could have incredibly wide-ranging repercussions on the rest of the application. But by using a property that says CustomerNum (with or without an 'i' in front) is always an Integer, you don't have to worry about whether the underlying variable changes to a string or a field in some other record or object -- references to CustomerNum will always refer to an integer. Even if you replace it with a DB query. As for Setters, they have a totally different set of uses that can be equally important. Their main use is to ensure the integrity of the internal state of the object. If the state of a given variable can only have three distinct values, you'd use a Setter to make sure it isn't assigned something else, which can cause problems for the behavior of the entire object later on. So the Setter can act as a sort of "door bouncer" to keep disreputable characters out of the club, so to speak. That's the "why" part in a nutshell. Here's where we get into what's behind the religious debate part... In Delphi, a "form" is a TForm which is a class derived from something in the VCL that's intended to act as a container for visual components -- hence VCL = Visual Component Library. When you create a 'class' in Delphi, it inherits from TObject and it's pretty much a blank slate. You get to define what's in it and how its clients are supposed to use it. When you create a 'form' in Delphi, it's actually a TForm, a class in itself, and it's FAR from a blank slate. It's part of a complex inheritance tree and it acts as a container for VCL components that depend on things present in the TForm inheritancy hierarchy. This imposes a bunch of fixed rules as well as guidelines to follow, some of which are required and some optional. How you get data into and out of a form should be guided by how you do that with any other class. However, in practice, people tend to not follow those rules very much. And you can tell from all of the back-and-forth between different folks here that some hold rather strong opinions about what to do and not do. I did a presentation for CodeRage 9 where I discussed this very topic in some detail, entitled "Have You Embraced Your Inner Software Plumber Yet?" The essence of this presentation is that getting data in and out of forms is more aligned with the whole domain of Dependency Injection than anything else. Unfortunately, when people hear that term, they tend to think of Unit Testing, not managing the lifetime of forms in their apps, including Dephi's forms. There are three types of DI for classes: (1) constructor injection; (2) property injection; and (3) setter/getter injection. People have described all of these earlier in the thread. Neither of them is "best", although some are better for certain situations and needs than others. Complicating things, the data they represent can be input only, output only, and bi-directional. So the DI part needs to account for this dynamic as well. (FYI: a DI framework / Service Locator lets you save and lookup different instances of things is a totally different and unrelated discussion. It would have been nice if the original developers built Delphi's form management on top of a DI framework, as that would have codified how to get data in and out of forms consistently, but ... they didn't go that way. I've seen it done in large applications, and it simplifies the hell out of things because the way you interface with every form is the same. It's quite refreshing to see!) And in my talk, I don't even address the situation where the form is interacting with a database maintained implicitly in another unit than the one that's dealing with it. This is the situation where you say, "Open this form to edit the current record in some DB based on whatever the user is doing right now." It's extremely common, and takes very little effort on your part because the form is simply mirroring both data and state being managed by some database components that probably live on a Data Module elsewhere. You just call Create and ShowModal and everything else is handled automatically. A guy named Mark Seemann has written a lot on this topic; he has a blog, has written some books, and gives talks on the subject. He has written a book, Dependency Injection for .NET that's quite popular, and while the code is focused on .NET, the principles apply equally well to Dephi. He seems to have co-authored a newer version of it recently that I haven't seen but is probably worth checking out. There's also the book Dependency Injection in Delphi by Nick Hodges. You can use Mark's book to learn the principles and Nick's book to see a lot of the examples in Delphi. I don't think Nick intended that, but it just shows how universal these patterns are. (It looks like all of Nick's books are on sale at Amazon right now.)
-
Oops... I forgot to copy-paste that part of the code... constructor TTracksAddEditForm.Create(AOwner: TComponent; const AsMode: string; AiAlbum: integer; const AsTrack: string); begin inherited Create(AOwner); FsMode := AsMode; FiAlbum := AiAlbum; FsTrack := AsTrack; end;
-
Your code can be simplified in several ways. I will write each step separately, so you can more easily adapt your code if you need additional functionality beyond the code shown here. First, as I mentioned in previous post, you don't need to write getter and setter functions at all, if you just read or write field in those and noting more. First optimization would be removing all that redundant code. This is not just speed optimization, but also makes cleaner code that is easier to read and follow. This code is functionally completely equivalent to your declaration and you would create and initialize the form the same way you do now. TTracksAddEditForm = class(TForm) private FsMode: string; FiAlbum: integer; FsTrack: string; public property sMode: string read FsMode write FsMode; property iAlbum: integer read FiAlbum write FiAlbum; property sTrack: string read FsTrack write FsTrack; end; Your next question was whether you need to have both getters and setters. If you are just setting property, then you don't need getter. Similarly, if you have read only property that you will just read, then you don't need setter. So next optimization would be following. There is no difference in how you create and initialize your form. TTracksAddEditForm = class(TForm) private FsMode: string; FiAlbum: integer; FsTrack: string; public property sMode: string write FsMode; property iAlbum: integer write FiAlbum; property sTrack: string write FsTrack; end; procedure TAudioForm.btnInsertTrackClick(Sender: TObject); begin TracksAddEditForm := TTracksAddEditForm.Create(Self); TracksAddEditForm.sMode := 'Insert'; TracksAddEditForm.iAlbum := Albums.FieldByName('Index').AsInteger; TracksAddEditForm.sTrack := TracksView.FieldByName('Title').AsString; TracksAddEditForm.Show; end; But, the best way to initialize any class that you will construct through code (without bringing in dependency injection) would be declaring all required information as parameters to the constructor, Such code would also require different code when constructing form. TTracksAddEditForm = class(TForm) private FsMode: string; FiAlbum: integer; FsTrack: string; public constructor Create(AOwner: TComponent; const AsMode: string; AiAlbum: integer; const AsTrack: string); reintroduce; end; procedure TAudioForm.btnInsertTrackClick(Sender: TObject); begin TracksAddEditForm := TTracksAddEditForm.Create(Self, 'Insert', Albums.FieldByName('Index').AsInteger, TracksView.FieldByName('Title').AsString); TracksAddEditForm.Show; end; The advantage of passing all required data as parameters during construction process is that you cannot accidentally forget to initialize some required field. If some fields are optional, then you can stick to initializing through properties, but using simplified examples before this last one.
-
Getters and Setters are methods called when you get (read) or set (write) property. In Delphi you don't need to have getter and setter methods. Properties can be directly connected to a field. You use getters or setters when you want to run additional code besides directly reading or writing some field value. Following is basic property declaration without any getters or setters. When you access property, compiler just directly reads or writes from associated field. TFoo = class protected FValue: Integer; published property Value: Integer read FValue write FValue; end; Following is property declaration with setter method (you can have any combination you need and you don't need to declare both methods if you just need one of them) TFoo = class protected FValue: Integer; procedure SetValue(AValue: Integer); published property Value: Integer read FValue write SetValue; end; Because getters and setters are methods, they will be a bit slower than directly using a field. If your setter is just setting a field and does nothing more, then you don't really need it. Same goes for getter. If it just returns the field then it is not needed. procedure TFoo.SetValue(AValue: Integer); begin FValue := AValue; end; If for instance you don't want to allow setting Value to zero, you might have following code in a setter. procedure TFoo.SetValue(AValue: Integer); begin if AValue <> 0 then FValue := AValue; end; You can even raise an exception if you set property to invalid value or similar. The only time when you would need to use getters and setters that just read or write to a field, is when your property needs to be accessible through interface. Interfaces require that getters and setters are methods.