Archive
HowTo: show a confirmation dialog with Delphi FireMonkey UI library
Here’s my answer at https://stackoverflow.com/a/71728787/903783 on how to show a confirmation dialog with Delphi’s cross-platform FireMonkey UI library.
Based on other answers, I’ve added a “Confirm” class function (adapted to return a Boolean) to TApplicationHelper (which also has an untested function to get the executable filename), currently maintained under the READCOM_App repository.
Update: had to modify the answer to use a callback so that it also works on platforms where FMX supports only asynchronous dialogs, like Android
unit Zoomicon.Helpers.FMX.Forms.ApplicationHelper;
interface
uses
FMX.Forms; //for TApplication
type
TApplicationHelper = class helper for TApplication
public
function ExeName: String;
class function Confirm(Prompt: String): Boolean;
end;
implementation
uses
{$IFDEF ANDROID}
System.SysUtils, //TODO: is this needed?
Androidapi.Helpers, //for TAndroidHelper, JStringToString
Androidapi.Jni.JavaTypes, //to avoid "H2443 Inline function 'JStringToString' has not been expanded"
{$ENDIF}
System.UITypes, //for TMsgDlgType
FMX.DialogService, //for TDialogService
FMX.Dialogs; //for mbYesNo
{$region 'TApplicationHelper'}
//from https://gist.github.com/freeonterminate/2f7b2e29e40fa30ed3c4
function TApplicationHelper.ExeName: String;
begin
Result := ParamStr(0);
{$IFDEF ANDROID}
if (Result.IsEmpty) then
Result := JStringToString(TAndroidHelper.Context.getPackageCodePath);
{$ENDIF}
end;
//based on https://stackoverflow.com/questions/42852945/delphi-correctly-displaying-a-message-dialog-in-firemonkey-and-returning-the-m
class procedure TApplicationHelper.Confirm(const Prompt: String; const SetConfirmationResult: TProc<Boolean>);
begin
with TDialogService do
begin
PreferredMode := TPreferredMode.Platform;
MessageDialog(Prompt, TMsgDlgType.mtConfirmation, mbYesNo, TMsgDlgBtn.mbNo, 0,
procedure(const AResult: TModalResult)
begin
//Note: assuming this is executed on the main/UI thread later on, so we just call the "SetResult" callback procedure passing it the dialog result value
case AResult of
mrYes: SetConfirmationResult(true);
mrNo: SetConfirmationResult(false);
end;
end
);
end;
end;
{$endregion}
end.
usage example is in the MainForm of READCOM_App
procedure TMainForm.HUDactionNewExecute(Sender: TObject);
begin
TApplication.Confirm(MSG_CONFIRM_CLEAR_STORY, //confirmation done only at the action level //Note: could also use Application.Confirm since Confirm is defined as a class function in ApplicationHelper (and those can be called on object instances of the respective class too)
procedure(Confirmed: Boolean)
begin
if Confirmed and (not LoadDefaultDocument) then
NewRootStoryItem;
end
);
end;
where MSG_CONFIRM_CLEAR_STORY is declared as
resourcestring
MSG_CONFIRM_CLEAR_STORY = 'Clearing story: are you sure?';
Fix: Microworlds Pro installer showing gibberish instead of Greek
A friend sent me the other day a screenshot from the installer of Microwords Pro Greek version on Windows 10 that was showing gibberish instead of Greek. At their school on Windows 10 it was showing up fine, so they wondered what the issue was.
They mentioned that on Windows 10 english it was showing fine. So we did try changing the preferred language order of Windows (at Laguage preferences in settings), to put English first (clicking on a language there shows arrows to reorder them, but has no drag action allowed btw), then Greek and try installing again, but the issue was still the same.
Note that at first I had wondered if it was a similar issue with the OwnCloud client installation that was showing an unexpected language instead of English, but since the screenshot was showing gibberish chars I guessed it was indeed showing Greek, but at some unexpected encoding for the system’s codepage. Note that codepages are a thing of the past and not needed for Unicode programs, but they’re still needed for older programs that just used a single byte (that couldn’t fit all posisble characters in the world) to encode a character.
I noticed it was showing a WinRar icon at that installer, so wondered if they had WinRar installed, but since installers are usually self-contained, I guessed the installer had been built with some older non-unicode WinRar engine version or something.
So I suggested going to Control Panel > Change Display Language > Administrative > Change System Locale for non Unicode programs and choose Greek there instead of English and indeed it worked.
Note that to find Control Panel on recent versions of Windows 10, the easiest way is to press the Search icon on the taskbar and then type “Control Panel” (or “Πίνακας Ελέγχου” if you’re on a Greek system). On the first Windows 10 editions you could just right click the Windows 10 Start/flag button on the taskbar and select Control Panel from the popup menu shown, but it seems they’ve removed that with recent updates.
This is useful to know since it usually affects most older non-Unicode programs that expect the system to work with a specific codepage. Microsoft used to have a utility that allowed one to switch the system codepage separately for various programs instead of doing a system-wide change, but it was hard to find even back then (plus didn’t allow redistribution) and most probably doesn’t work on newer systems.
Fix: make ownCloud installer display in English language
OwnCloud is an interesting solution for setting up a file sharing cloud for a group of people.
However,one issue I’ve found with its Windows desktop client’s current version (which looks clean of any viruses since I always check first) is that if your Windows 10 is configured with a preferred language that the desktop client’s installer doesn’t have localization support for, then it doesn’t show up in English as you’d expect, but in Czech or someother language that most of us don’t know how to read.
So I tried running it’s MSI installer (ownCloud-2.6.1.13407.13049.msi) with –? parameter from the command-line and the /g languageCode parameter mentioned there looked promising, but trying /g en for English didn’t work. I guessed it needed some specific language code number (and not double-letter language code like en for English), since the help text was mentioning to see Windows Installer SDK for more help.
After a quick search I found an article that suggested passing the parameter Productlanguage=1033 to an msi installer on the command-line for it to ALWAYS show in English. And indeed it worked.
To open a command window one can click the Search icon on the windows taskbar and type CMD then press ENTER.
Then they can drag-drop the .MSI file of ownCloud installer onto the black command-line window that opens up and type an extra space char and then Productlanguage=1033 before pressing ENTER to launch the ownCloud installer in English. After that they can close the command-line window at anytime.
Since many users may be uncomfortable with such instructions, one could provide an msiEnglish.bat file that just contains
%1 Productlanguage=1033
User could drag-drop the .msi they want onto that msiEnglish.bat file and it would run the msi installer being displayed in English language, irrespective of any preferred language settings at the Windows operating system.
Of course the best thing would be if ownCloud fixed their desktop client installer to fallback to the Engish language (set it as default) if it can’t find localization strings for the currently prefered language of the user. Have filed an issue at https://github.com/owncloud/client/issues/7825
Fix: WordPress administration UI content area showing up blank
Some time ago, at a WordPress 3.3 blog, a friend was getting a blank main area at the administration UI (just the menu was showing up there) when they visited the classic URL of the form http://somesite/wp-admin/. Here is a writeup of some notes I had kept while troubleshooting that issue and then upgrading to newer WordPress, with updated theme and plugins.
Visiting the problematic admin UI page online, I right-clicked and selected View Source in the browser and browsed to the bottom of it where it was showing:
That div tag wasn’t not closed with a matching closing div. Obviously some PHP code that was outputing that tag failed at that point.
For starters, I copied all files via FTP (with the FireFtp Firefox plugin) from the remote server locally (into my Dropbox). Useful for backup too in case I messed up something.
Could use grepWin tool (http://stefanstools.sourceforge.net/grepWin.html) to search all locally copied php files for “contextual-help-sidebar” and see which one was outputing this (if that string was assigned to some variable could then search again where that variable was being used).
However, WordPress also has debugging mode, so I edited wp-config.php and uploaded the edited version to the server replacing the old file. I changed WP_DEBUG setting to true below:
At the problematic admin UI online page again, I right-clicked and selected View Source in the browser and browsed to the bottom of it, this time reading:
This led to indentifying the exact place in the PHP code that was causing this issue:
So, I commented out the buggy PHP code, converting it to an HTML comment block instead of a PHP block:
and the admin UI was working again after I pressed ENTER at the address bar again to refresh the admin UI (http://somesite/wp-admin/) and it was not showing up fine (F5 function key didn’t seem to really refresh the site when using Firefox btw, probably some caching issue).
Then installed (the free version) of the WP Database Backup plugin for WordPress:
http://www.wpseeds.com/documentation/docs/wp-database-backup/
by searching for “Backup” at
http://somesite/wp-admin/plugins.php
and evaluating the different backup plugins listed there (judging from both their votes and by checking out if WordPress wasn’t saying at the details page of a plugin that it hasn’t been tested with that [old] 3.3 WordPress version that was on that site) and backed up from Tools/WP-DB Backup menu:
http://somesite/wp-admin/tools.php?page=wp-database-backup
Then Downloaded the MySQL backup (.sql) file, so now I could update Themes, then Plugins, then update WordPress to new Core from
http://somesite/wp-admin/update-core.php
Did keep copies of the wp-content/themes and wp-content/plugins folders before and after updating them of course.
After WP updated, it asked to update the DB, all went ok
Then did backup up again the database via the WP-DB Backup tool and went again to
http://somesite/wp-admin/update-core.php
and installed an update for one of the plugins (can also do update per-plugin from http://somesite/wp-admin/plugins.php)
Then backed up again every file (db not included there) via FTP and done.
HowTo: Hide HTML markup from non-signedin users at MonoX Social CMS
At MonoX Social CMS, which I use at both ClipFlair Social and Trafilm websites, I was in the need of hiding some HTML markup when the user is not signed-in.
The solution for this is to add runat="server" to the HMTL element one wants to hide and then set the Visible property that the object acquires due to the runat clause. The Visible property is set using some special syntax to access the MonoX API like below:
<ul runat="server"
Visible="<% $Code: Page.User.Identity.IsAuthenticated %>" >
…
</ul>
HowTo: Drop files onto Silverlight controls
I was recently adding drop-files support to ClipFlair Studio, so I had to do some research on the related API that is available to Silverlight apps.
Silverlight supports a limited set of Drag-and-Drop interaction with the operating system (supposedly for security reasons, but most probably because of the classic cross-platform implementation pains).
For example it allows you to drop a collections of file objects from the operating system’s file explorer (or other similar application) onto a Silverlight drop target, but not the other way around.
Also, it doesn’t allow dropping other flavors of content, like text, images etc., only collections of file objects.
To allow dropping files onto a Silverlight control you set AllowDrop property to true and handle the Drop and optionally the DragEnter/DragOver/DragLeave events to provide visual feedback during the drop operation (mostly when over the drop target, unless you do Mouse Capturing).
<RichTextBox
x:Name="rtb"
…
AllowDrop="True"
Drop="rtb_Drop"
DragEnter="rtb_DragEnter"
DragOver="rtb_DragOver"
DragLeave="rtb_DragLeave"
/>
#region DragAndDrop
private void rtb_Drop(object sender, System.Windows.DragEventArgs e)
{
VisualStateManager.GoToState(this, "Normal", true);//the Drop event passes in an array of FileInfo objects for the list of files that were selected and drag-dropped onto the RichTextBox.
if (e.Data == null)
return;IDataObject f = e.Data as IDataObject;
if (f != null) //checks if the dropped objects are files
{
object data = f.GetData(DataFormats.FileDrop); //Silverlight only supports FileDrop
FileInfo[] files = data as FileInfo[]; //…GetData returns null if format is not supportede.Handled = true;
if (files != null)
//Walk through the list of FileInfo objects of the selected and drag-dropped files and parse the .txt and .docx files
//and insert their content in the RichTextBox.
foreach (FileInfo file in files)
Load(file, false);
}}
private void rtb_DragEnter(object sender, System.Windows.DragEventArgs e)
{
VisualStateManager.GoToState(this, "DragOver", true);
e.Handled = true;
}
private void rtb_DragOver(object sender, System.Windows.DragEventArgs e)
{
e.Handled = true;
//NOP
}private void rtb_DragLeave(object sender, System.Windows.DragEventArgs e)
{
VisualStateManager.GoToState(this, "Normal", true);
e.Handled = true;
}#endregion
For the visual feedback you can use VisualStateManager and respective VisualStates at say a Grid that wraps the drop target in the XAML layout.
<Grid x:Name="LayoutRoot">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="DragStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.3">
<VisualTransition.GeneratedEasingFunction>
<CircleEase EasingMode="EaseIn"/>
</VisualTransition.GeneratedEasingFunction>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal"/>
<VisualState x:Name="DragOver">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Control.Background).(SolidColorBrush.Color)" Storyboard.TargetName="rtb">
<EasingColorKeyFrame KeyTime="0" Value="#FFFFF7D1"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
Note that on MacOS-X Safari doesn’t pass drop events automatically to plugins and you have to catch them via Javascript and pass them to Silverlight, plus I’ve read that in MacOS-X Firefox doesn’t support this at all.
Even worse, Microsoft’s workaround article has a typo in the Javascript (should call dragDrop instead of drop) and there is a chance it doesn’t work in latest version of Safari. For more see:
http://msdn.microsoft.com/en-us/library/ee670998%28v=vs.95%29.aspx
http://www.telerik.com/forums/dropping-files-using-safari-on-mac (this says one can use HTML5 events and the Silverlight HTML/Javascript Bridge to notify Silverlight)
http://www.thebuzzmedia.com/html5-drag-and-drop-and-file-api-tutorial/
HowTo: find max ZIndex from a collection of UIElements with LINQ
Based on a relevant answer at
and some digging into LINQ’s Aggregate method documentation is what I’ve just added to my enhanced version of SilverFlow library’s FloatingWindowHost (copying from FloatingWindowHost.cs at http://clipflair.codeplex.com source code)
/// <summary> /// Sets the specified UIElement topmost. /// </summary> /// <param name="element">UIElement to set topmost.</param> /// <exception cref="ArgumentNullException">UIElement is null</exception> private void SetTopmost(UIElement element) { if (element == null) throw new ArgumentNullException("element"); Canvas.SetZIndex(element, MaxZIndex + 1); } public int MaxZIndex { get { return FloatingWindows.Aggregate(-1, (maxZIndex, window) => { int w = Canvas.GetZIndex(window); return (w > maxZIndex) ? w : maxZIndex; }); //Math.Max would be cleaner, but slower to call } }
Worth noting regarding the code above is that Canvas.ZIndex is an attached property available for UIElements in various containers, not just used when being hosted in a Canvas (see http://stackoverflow.com/questions/569751/controlling-rendering-order-zorder-in-silverlight-without-using-the-canvas-con)
Guess one could even make a SetTopmost and SetBottomMost static extension method for UIElement easily by adapting this code.
Gotcha: .NET Point and PointConverter inconsistency in string format used
I have submitted the following issue to Microsoft Connect (product feedback center):
Point class issue with two-way databinding
In Silverlight, when using databinding code like the following:
<StackPanel Orientation="Vertical" Name="propPosition">
<sdk:Label Style="{StaticResource PropertyLabelStyle}" Content="Position:" Target="{Binding ElementName=edPosition}" />
<TextBox Name="edPosition" Text="{Binding Position, Mode=TwoWay, ValidatesOnExceptions=True, NotifyOnValidationError=true}" />
</StackPanel>and binding (via DataContext) to a View that has the following property:
Point Position { get; set; }
then what you see on the UI is values like 500; 100
but when you try to edit the position and enter say 400; 100 it shows a red message that it is invalid format (caught an exception that is and showing on the UI automatically cause of ValidatesOnExceptions and NotifyOnValidationError being true)
if you enter 400, 100 it works ok (it moves a ClipFlair Studio [http://ClipFlair.net] window arround in my case), so it outputs the two numbers (X, Y) with a ";" but expects to get them separated with a "," (also wonder if it copes OK with numbers in say format used in Greece where decimal point separator is , instead of .)
I managed to reproduce the same behaviour in a .NET console program:
//Console program in C# to demonstrate Point.ToString and PointConverter (from string) inconsistency in string format
//(the former outputs X;Y, the later expects X,Y)
using System;
using System.Windows; //for "Point" class (needs WindowsBase.dll reference)
namespace ConsoleApplication1
{
class Program
{
static PointConverter conv = new PointConverter();
static void Main(string[] args)
{
Point p = new Point(10, 20);
Console.WriteLine(p); //outputs "10;20"
//——————
Point p1 = (Point)conv.ConvertFrom("10, 20");
Console.WriteLine(p1); //outputs "10;20"
//——————
Point p2 = (Point)conv.ConvertFrom("10; 20"); //unhandled exception "System.FormatException" in mscorlib.dll
Console.WriteLine(p2); //never reached cause of exception above
}
}
}
HowTo: use a Timer component for delayed execution in WinForms
This is my contribution to:
http://stackoverflow.com/questions/303116/system-windows-threading-dispatcher-and-winforms
Sometimes a Timer component is useful and easy to setup in WinForms, just set its interval and then enable it, then make sure the first thing you do in its Tick event handler is to disable itself.
I think Timer runs the code in its own thread, so you may still need to do a BeginInvoke (called upon the WinForm object [this]) to run your Action.
private WebBrowserDocumentCompletedEventHandler handler;
//need to make it a class field for the handler below
//(anonymous delegates seem to capture state at point of
// definition, so they can't capture their own reference) private string imageFilename; private bool exit; public void CaptureScreenshot(
Uri address = null,
string imageFilename = null,
int msecDelay = 0,
bool exit = false) { handler = (s, e) => { webBrowser.DocumentCompleted -= handler; //must do first this.imageFilename = imageFilename; this.exit = exit; timerScreenshot.Interval = (msecDelay>0)? msecDelay : 1; timerScreenshot.Enabled = true; }; webBrowser.DocumentCompleted += handler; Go(address); //if address == null, will use URL from UI } private void timerScreenshot_Tick(object sender, EventArgs e) { timerScreenshot.Enabled = false; //must do first BeginInvoke((Action)(() => //Invoke at UI thread { //run in UI thread BringToFront(); Bitmap bitmap = webBrowser.GetScreenshot(); if (imageFilename == null) imageFilename = bitmap.ShowSaveFileDialog(); if (imageFilename != null) { Directory.CreateDirectory(
Path.GetDirectoryName(
Path.GetFullPath(imageFilename)));
//create any parent directories needed bitmap.Save(imageFilename); } bitmap.Dispose(); //release bitmap resources if (exit) Close(); //this should close the app, this is main form }), null); }
You can see the above in action at ClipFlair‘s WebCapture tool (http://gallery.clipflair.net/WebCapture, source code at: http://ClipFlair.codeplex.com, see Tools/WebCapture folder) that grabs screenshots from websites.
BTW, if you want to call the executable from command-line make sure you go to Properties of the project and at Security tab turn-off ClickOnce security (else it can’t access command-line).