Интерфейс - создание удобных программ
Главная
Программы
Исходники
Содержание
О себе

  Вопросы

  Ответы

О правилах хорошего тона в разработке интерфейса программ

Здесь, конечно многое зависит от Ваших дизайнерских способностие и кто считает себя профи может не читать дальше, а вот для тех, кто еще только или уже хоть что-то, это, думаю, будет очень полезным. Ну во-первых, хороший интерфейс - это удобный интерфейс и все рассуждения идут, в первую очередь, в пользу удобства использования программного продукта. С хорошим интерфейсом будет та программа, которой легко пользоваться, без особых напряжений, и легко научится пользоваться. Например, меня долго бесило изменение положения объекта / обтекание текстом в Worde 2000, после 97-го. Приходилось лишний раз нажимать на кнопку (дополнительно) и заново вглядываться в новое окно. Но опять же, если у Вас все будет столкано на одно окно, где один элемент на другом, и нет ни одной подписи, то лазийте в своей проге лучше сами.
Если в Вашей программе для получения какого-либо результата требуется совершить ряд действий, то все элементы управления должны находится в одном окне, но они не должны быть мелкими, чтобы не надо было в них метиться, и не должны быть крупными, чтобы не гонять мышь по экрану. Если для нескольких действий требуется мало элементов управления, то лучше их разместить в одном окне и "озаглавить" его соответствующим образом.
В хорошей программе должно быть меню, откуда можно получить доступ ко всем командам в программе. Оформление меню тоже должно быть логичным.
Когда совсем нет необходимости использовать несколько копий программы одновременно, то лучше перед загрузкой сделать проверку на наличие уже запущенной копии программы и если уже есть, то завершить процесс загрузки и сделать активной существующую копию, либо обратить каким-либо образом на нее внимание, либо, если она минимизирована, то развернуть ее.
Если программа должна быть загружена долгое время, то будет удобнее убрать кнопку приложения с панели задач (чтобы не мешалась) и поместить иконку в TrayBar возле часиков.
Если программа работает с данными из файла или их можно сохранять в файл и т.д., то перед закрытием приложения нужно спросить о сохранение данных.
Если программа что-то показывает и при этом невозможен ввод данных, то лучше на время показа отключить устройства ввода, а после показа опять включить.
Если перед первым запуском программы требуется провести какие-либо установки в системе, лучше оснастить продукт инсталятором, который бы все сделал сам.
Программа должна содержать доступную для понимания помощь.
Если программа долго выполняет какое-либо действие и при этом на экране ничего не происходит, то необходимо поместить на форму индикатор, который показывал бы процесс выполнения и сообщение с просьбой подождать пока длится процесс.
Цветовая гамма не должна быть резкой и раздражительной. Раздражающие цвета следует применять только в тех случаях, когда требуется обратить особое внимание. Чем приятней подбор цветов, тем менее утомительна работа. Я не хочу говорить о таких окнах как у Winamp-а и т.п. это конечно здорово, но и там бывают такие скины, что хочется плюнуть да монитор жалко.
И в заключение, будет просто круто, если пользователь сможет сам настраивать интерфейс под себя. И все настройки после этого сохранялись и применялись бы.

Порядок, в котором обрабатываются события при загрузке формы

OnCreate, OnShow, OnPaint, OnActivate, OnResize, OnPaint.

Но это только основные, на самом деле при создании окна обрабатывается около двух сотен сообщений.

Как скрыть форму при старте приложения

Можно в обработчике событий OnCreate, OnShow, OnActivate вписать ShowWindow(Handle, SW_HIDE), но при это не избежать кратковременного мерцания окна.

Избавиться от этого можно, например, поместив окно где-нибудь за экраном:

Left := Screen.Width;

Затем скрыть окно и вернуть в обратное положение.

Как hint сделать многострочной

Напишем компоненту:
unit MHint;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type
  TMHint = class(TComponent)
  private
    ScreenSize: Integer;
    FActive: Boolean;
    FSeparator: Char;
    FOnShowHint: TShowHintEvent;
    procedure SetSeparator(Value: Char);
    { Private declarations }
  protected
    procedure SetActive (Value: Boolean);
    procedure NewHintInfo (var HintStr: String;
                           var CanShow: Boolean;
                           var HintInfo: THintInfo);
    { Protected declarations }
  public
    constructor Create (AOwner: TComponent); override;
    { Public declarations }
  published
    property Active: Boolean
      read FActive write SetActive;
    property Separator: Char read FSeparator write SetSeparator;
    { Published declarations }
  end;

procedure Register;

implementation

procedure TMHint.SetActive(Value: Boolean);
begin
  FActive := Value;
end;

procedure TMHint.NewHintInfo;
var I: Byte;
begin
  if FActive then
    begin
      I := Pos(FSeparator, HintStr);
      while I > 0 do
        begin
          HintStr[I] := #13;
          I := Pos(FSeparator, HintStr);
        end;
      if HintInfo.HintPos.y+10 > ScreenSize then
        HintInfo.HintPos.y := ScreenSize - 11;
    end;
end;

procedure Register;
begin
  RegisterComponents('Samples', [TMHint]);
end;

constructor TMHint.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FActive := True;
  FSeparator := '0';
  Application.OnShowHint := NewHintInfo;
  ScreenSize := GetSystemMetrics(SM_CYSCREEN);
end;

procedure TMHint.SetSeparator(Value: Char);
begin
  FSeparator := Value;
end;

end.

Системные сообщения (сокращения)

BM  - ButtonMode (BM_) 
BN  - Button (BN_) 
BS  - ButtonStyle (BS_) 
CB  - ComboBox (CB_) 
CBN - ComboBox Notification (CBN_) 
CBS - ComboBoxStyle (CBS_) 
EM  - EditMessage (EM_) 
ES  - EditStyle (ES_) 
FM  - FileManager (FM_) 
LB  - ListBox (LB_) 
LBN - ListBox Notification (LBN_) 
LBS - ListBoxStyle (LBS_) 
MB  - MessgeButton (MB_) 
PS  - PenStyle (PS_) 
SB  - ScrollBar (SB_) 
SBS - ScrollBarStyle (SBS_) 
SM  - SystemMetrics (SM_) 
SPI - SystemParametersInfo (SPI_) 
WM  - WindowMessage (WM_) 
WS  - WindowStyle (WS_)

Как программно создать ярлык

uses ShlObj, ComObj, ActiveX;

procedure CreateLink(const PathObj, PathLink, Desk, Param: string);
var
  IObject: IUnknown;
  SLink: IShellLink;
  PFile: IPersistFile;
begin
  IObject := CreateComObject(CLSID_ShellLink);
  SLink := IObject as IShellLink;
  PLink := IObject as IPersistFile;
  with SLink do
    begin
      SetArguments(PChar(Param));
      SetDescription(PChar(Desk));
      SetPath(PChar(PahtObj));
    end;
  PFile.Save(PWChar(WideString(PathLink)), false);
end;

Добавление программы в автозапузк

sProgTitle:   название для программы;
sCmdLine:  имя EXE файла с путем доступа;
bRunOnce: запускать только один раз или всегда при загрузке Windows;

procedure RunOnStartup(sProgTitle, sCmdLine: string; bRunOnce: Boolean);
var
  sKey: string;
  reg: TRegIniFile;
begin
  if bRunOnce then sKey := 'Once';
  else sKey := '';
  reg := TRegIniFile.Create('');
  reg.RootKey := HKEY_LOCAL_MACHINE;
  reg.WriteString('Software\Microsoft\Windows\CurrentVersion\Run' + sKey + #0, sProgTitle, sCmdLine);
  reg.Free;
end;

События нажатия на системные кнопки формы (минимизация, закрытие ...)

В описание класса вставить:
  protected
    procedure WMGetSysCommand(var Message: TMessage); message WM_SYSCOMMAND;

И собственно обработчик:
procedure Tform1.WMGetSysCommand(var Message: TMessage);
begin
  if Message.wParam = SC_MINIMIZE then ShowMessage('Окно минимизируется');
end;

Как поменять разрешение экрана

procedure ChangeDisplayResolution(x, y: Word);
var
  dm: TDEVMODE;
begin
  ZeroMemory(@dm, sizeof(TDEVMODE));
  dm.dmSize := sizeof(TDEVMODE);
  dm.dmPelsWidth := x;
  dm.dmPelsHeight := y;
  dm.dmFields := DM_PELSWIDTH or DM_PELSHEIGHT;
  ChangeDisplaySettings(dm, 0);
end;

Работают только стандартные разрешения.

Изменение положения указателя мыши

Чтобы установить курсор мыши в какое-либо место экрана необходимо воспользоваться функцией SetCursorPos(x, y);

И собственно получение координат:

var
  pos: TPoint;
begin
  GetCursorPos(pos);
  x := pos.x;
  y := pos.y;
end;

Как перетаскивать форму не только за Caption, но и за любое другое место

Первый способ:

procedure TForm1.MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  ReleaseCapture;
  perform(WM_SYSCOMMAND, $F012, 0);
end;

Второй способ:

В описании класса нужно добавить:
private
  procedure WMNCHitTest(var M: TWMNCHitTest); message WM_NCHITTEST;

Ну и сам обработчик:
procedure WMNCHitTest(var M: TWMNCHitTest); message WM_NCHITTEST;
begin
  inherited;
  if M.Result = htClient then M.Result := htCaption;
  {смысл такой: если мышь сидит на окне, то пусть винда думает, что мышь на Caption Bar}
end;

Как определить работает ли уже данное приложение или это его первая копия

Способ первый:

Идея такая: ищем в системе окно с таким же заголовком как у запускаемого приложения и ели такое окно есть то завершаем приложение.

procedure TForm1.FormCreate(Sender: TObject);
var
  Wnd: THandle;
  buff: array[0..127] of Char;
begin
  Wnd := GetWindow(Handle, GW_HWNDFIRST);
  while Wnd <> 0 do
    begin
      if (Wnd <> Application.Handle) and (GetWindow(Wnd, GW_OWNER) = 0) then
        begin
          GetWindowText(Wnd, Buff, SizeOf(Buff));
          if StrPas(buff) = Application.Title then
            begin
              ShowMessage('Копия');
              Halt(0);
            end;
        end;
      Wnd := GetWindow(Wnd, GW_HWNDNEXT);
    end;
end;

Способ второй (ИМХО лучший):

var
  HM: THandle;
function Check: boolean;
begin
  HM := OpenMutex(MUTEX_ALL_ACCESS, false, 'MyOwnMutex');
  Result := (HM <> 0);
  if HM = 0 then HM := CreateMutex(nil, false, 'MyOwnMutex');
end;

Привлечение внимания к окну

Заставить мигать кнопку приложения на панели задач можно используя функцию FlashWindow, если вызывать ее в таймере:

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  FlashWindow(Application.Handle, true);
end;

Как вставить ProgressBar в StatusBar

TForm1 = class(TForm)
    * * * 
  ProgressBar1: TProgressBar;
  StatusBar1: TStatusBar;  Timer1: TTimer;
    * * *
  procedure FormCreate(Sender: TObject);  procedure Timer1Timer(Sender: TObject);
  procedure FormCanResize(Sender: TObject; var NewWidth, NewHeight: Integer;
                  var Resize: Boolean);
    * * *

procedure TForm1.FormCreate(Sender: TObject);
begin
  ProgressBar1.Parent := StatusBar1;
  ProgressBar1.Top := 2;
  ProgressBar1.Left := 152; 
  ProgressBar1.Height := ProgressBar1.Height + 1;
end;

procedure TForm1.Timer1Timer(Sender: TObject); 
begin 
  if ProgressBar1.Position < 100 then ProgressBar1.Position := ProgressBar1.Position + 1
  else ProgressBar1.Position := 0; 
end;
 
procedure TForm1.FormCanResize(Sender: TObject; var NewWidth, NewHeight: 
      Integer; var Resize: Boolean); 
begin
  ProgressBar1.Width := Form1.Width - 180; 
end;

Как выделить окошко DBGrid другим цветом

Идея простая: обрабатываем OnDrawCellData - рисуем в нужной ячейке залитый 
прямоугольники выводим текст.
procedure TForm1.DBGridDrawCellDataCell(Sender: TObject; const Recr: TRect; 
        Field: TField; State: TGridDrawState);
begin
  if gdFocused in State then
    wiht (Sender as TDBGrid).Canvas do
      begin
        Brush.Color := Red;
        FillRect(Rect);
        TextOut(Rect.Left, Rect.Top, Field.Asstring);
      end;
end;

Как использовать новую форму курсора

В OnCreate для формы:
Button.Cursor := LoadCursor(hInstance, 'Curcor file name');

или можно попробовать вот так:
var
  h : THandle;
begin
  h := LoadImage(0,'C:\TheWall\Magic.ani',
    IMAGE_CURSOR, 0, 0,
    LR_DEFAULTSIZE or LR_LOADFROMFILE);
  if h = 0 then ShowMessage('Cursor not loaded')
  else
    begin
      Screen.Cursors[1] := h;
      Form1.Cursor := 1;
    end;
end;

Как установить минимальные размеры окна

В описании класса:
procedure WMGetMinMaxInfo(var Msg: TMessage); message WM_GETMINMAXINFO;

И собственно обработчик:
procedure WMGetMinMaxInfo(var Msg: TMessage);
begin
  PMinMaxInfo(Msg.lParam)^.ptMinTrackSize.x := 600;
  PMinMaxInfo(Msg.lParam)^.ptMinTrackSize.y := 350;
end;

Как заставить окно не разворачиваться из иконки

В описании класса:
procedure WMQueryOpen(var Msg: TMessage); message WM_QUERYOPEN;

И собственно обработчик:
procedure WMQueryOpen(var Msg: TMessage);
begin
  Msg.Result := 0;
end;

Как получить горизонтальную прокрутку в ListBox

Посылаем сообщение и все готово:
procedure TForm1.FormCreate(Sender: TObject);
begin
  ListBox1.Perform(LB_SETHORIZONTALEXTENT, 1000{ширина прокрытки в пикселах}, LongInt(0));
end;
 Как    сделать так, что при нажатии на Enter происходил переход к следующему элементу    формы
Свойство формы KeyPreview установить в true и обработать OnKeyPress:
procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
begin
  if Key = #13 then
    begin
      Key :=#0;
      Perform(WM_NEXTDLGCTL, 0, 0);
    end;
end;

Перетаскивание файлов в окно

Для начала в разделе Uses необходимо подключить модуль ShellAPI.

В private области окна нужно вставить следующую строку:
procedure WMDropFiles(var Msg: TWMDropFiles); message WM_DROPFILES; //получение сообщений о переносе файла в окно приложения

Процедура обработки этого сообщения будет выглядеть следующим образом:
procedure TForm1.WMDropFiles(var Msg: TWMDropFiles);
var
  CFileName: array[0..MAX_PATH] of Char; // переменная, хранящая имя файла
begin
try
  If DragQueryFile(Msg.Drop, 0, CFileName, MAX_PATH)>0 then // получение пути файла
    begin
      Form1.Caption:=CFileName; // имя файла в заголовок окна
      Memo1.Lines.LoadFromFile(CFileName); // открываем файл
      Msg.Result := 0;
    end;
finally
  DragFinish(Msg.Drop); // отпустить файл
end;
end;

Для того, чтобы форма знала, что может принимать такие файлы, необходимо в процедуре создания окна указать:
procedure TForm1.FormCreate(Sender: TObject);
begin
  DragAcceptFiles(Handle, True);
end;

Как сделать свою кнопку в заголовке формы

Вот есть такой примерчик:
//Непосредственно такой функции нет, но можно изловчиться.
//Нарисовать там кнопку вручную и обрабатывать команды нажатия
//мышки на Caption Bar.
unit Unit1;

interface

uses
  Windows, Buttons, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type
  TForm1 = class(TForm)
    procedure FormResize (Sender: TObject);
  private
    CaptionBtn: TRect;
    procedure DrawCaptButton;
    procedure WMNCPaint (var Msg: TWMNCPaint); message WM_NCPaint;
    procedure WMNCActivate (var Msg: TWMNCActivate); message WM_NCActivate;
    procedure WMSetText (var Msg: TWMSetText); message Wm_SetText;
    procedure WMNCHitTest (var Msg: TWMNCHitTest); message WM_NCHitTest;
    procedure WMNCLButtonDown (var Msg: TWMNCLButtonDown); message WM_NCLButtonDown;
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

const
  htCaptionBtn = htSizeLast + 1;
{$R *.DFM}

procedure TForm1.DrawCaptButton;
var
  xFrame, yFrame, xSize, ySize: Integer;
  R: Trect;
begin
  //Dimensions of Sizeable Frame
  xFrame := GetSystemMetrics (SM_CXFrame);
  yFrame := GetSystemMetrics (SM_CYFrame);
  //Dimensions of Caption Buttons
  xSize := GetSystemMetrics (SM_CXSize);
  ySize := GetSystemMetrics (SM_CYSize);
  //Define the placement of the new caption button
  CaptionBtn := Bounds (Width - xFrame - 4 * xSize + 2,
                        yFrame + 2,
                        xSize - 2,
                        ySize - 4);
  //Get the handle to canvas using Form's device context
  Canvas.Handle := GetWindowDC (Self.Handle);
  Canvas.Font.Name := 'Symbol';
  Canvas.Font.Color := clBlue;
  Canvas.Font.Style := [fsBold];
  Canvas.Pen.Color := clYellow;
  Canvas.Brush.Color := clBtnFace;
  try
    DrawButtonFace (Canvas, CaptionBtn, 1, bsAutoDetect, False, False, False);
    //Define a smaller drawing rectangle within the button
    R := Bounds (Width - xFrame - 4 * xSize + 2,
                 yFrame + 3,
                 xSize - 6,
                 ySize - 7);
    with CaptionBtn do
      Canvas.TextRect (R, R.Left + 2, R.Top - 1, 'W');
   finally
     ReleaseDC (Self.Handle, Canvas.Handle);
     Canvas.Handle := 0;
   end;
end;

procedure TForm1.WMNCPaint (var Msg: TWMNCPaint);
begin
  inherited;
  DrawCaptButton;
end;

procedure TForm1.WMNCActivate (var Msg: TWMNCActivate);
begin
  inherited;
  DrawCaptButton;
end;

procedure TForm1.WMSetText (var Msg: TWMSetText);
begin
  inherited;
  DrawCaptButton;
end;

procedure TForm1.WMNCHitTest (var Msg: TWMNCHitTest);
begin
  inherited;
  with Msg do
    if PtInRect (CaptionBtn, Point(xPos - Left, yPos - Top)) then
      Result := htCaptionBtn;
end;

procedure TForm1.WMNCLButtonDown (var Msg: TWMNCLButtonDown);
begin
  inherited;
  if (Msg.HitTest = htCaptionBtn) then
    ShowMessage ('You hit the button on the caption bar');
end;

procedure TForm1.FormResize (Sender: TObject);
begin
  //Force a redraw of caption bar if form is resized
  Perform (WM_NCActivate, Word(Active), 0);
end;

end.

Как в программе переключить раскладку клавиатуры

Для переключения языка применяется вызов LoadKeyboardLayout:

var
  russian, latin: HKL; 
begin
  russian := LoadKeyboardLayout('00000419', 0);
  latin := LoadKeyboardLayout('00000409', 0);
end;

-- -- -- -- -- где то в  программе --- --- --- 
 SetActiveKeyboardLayout(russian);

Включение / выключение устройств ввода из программы

Элементарно Ватсон:
  EnableHardwareInput(Enable: Boolean);  
   //Enable - требуемое состояние: true - включены; false - выключены.
   Правда отключенное состояние можно снять Ctrl + Alt + Del-ом.
   
   

Hmelev2002@yandex.ru

Написать в гостевую книгу

[поиграй в крестики-нолики]

Сайт создан в системе uCoz