منتخب بازارچه

منوها - یک نگاه نسبتا ساده

knowhow

مدیران قدیمی
تاریخ عضویت
25 دسامبر 2002
نوشته‌ها
3,477
لایک‌ها
21
littlerabbit

abolfazlerfani wrote:
سلام
يه سوال داشتم در مورد اينكه چطور ميشه برنامه اي با منوهاي فارسي طوري طراحي كرد
كه منوها در ساير ويندوزهاي عربي ( و يا اونايي كه فارسيساز دارن) درست نمايش داده بشن؟

من برنامه اي با VB توي ويندوزي با فارسي ساز پارسا 2001 (يا پارسا99) نوشتم.

وقتي اونو توي ويندوز "پايا" و يا در ويندوز عربي ساده (بدون فارسي ساز) نصب ميكنم

حروف "گچپژ" فقط روي button ها و textbox ها و label ها درست نمايش داده ميشه ولي

روي منوها بصورت "؟" نمايش داده ميشه؟

به نظر شما چيكار ميشه كرد.؟



سوال دوم:
دنبال يك تابع ميگردم كه كليد فشرده شده را به فارسي تبديل كنه. يكيشو خودم نوشتم كه خيلي مبتدي و ساده است و فقط با پارسا 99 درست كار ميكنه.
لطفا اگه ممكنه منو راهنمايي كنيد.

متشكرم.

url]http://abolfazlerfani.persianblog.com [/url]

در مورد منو بايد بگويم که آنها در حالت عادي يکي از انعطاف پذيرترين ابزار برنامه نويسي هستند اما اگر نخواهيم وارد مباحث پيشرفته شويم راهي براي تغيير دادن فونت منوهايي که با پرچم MF_STRING (منوهاي عادي- مثل منوهاي وي بي يا منوهاي ويژوال سي) مشخص شده اند وجود ندارد.
تنها راه ساده موجود اين است که فونت تمام منوهاي سيستم را عوض کنيم - از طريق Display Properties \Appearance يا از طريف دستکاvي مستقيم در رجيستري (HKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics\MenuFont البته اين مقدار به صورت باينري است نه رشته اي- حالا مشکلي پيش ميايد و آن مساله عوض شدن فونت در کل سيستم است -اين براي امثال من که ترجيح ميدهم همه چيز توسط خودم انتخاب شود کمي ناخوشايند است که يک برنامه تنظيمات سيستم مرا به هم بريزد-

راه ديگري هم هست که متاسفانه بدون ترس از Crash در برنامه هاي ويژوال بيسيک نميتوان از آن استفاده کرد
توابع API منو تنها دو تابع دارند که ميتواند اطلاعات مربوط به يک منو و آيتم هاي آن را بگيرد اما هيچکدام راهي براي تغيير فونت ارايه نداده اند.(همه توابع ديگر نيز توسط اين دو تابع قابل جايگزيني هستند منتها با کمي دردسر بيشتر ) يکي GetMenuInfo و يکي GetMenuItemInfo .

شايد براي شما هم مساله اين است که برنامه هايي مثل IncrediMail يا Axialis IconWorkshop (که دومي حتي سيستم منوي آن هم دستکاري شده ) چه طور اينکار را ميکنند؟
آنها همه منوهاي خود را با فلگ MF_OWNERDRAW مشخص ميکنند و در هنگام اجراي برنامه به پيغام هاي WM_MEASUREITEM و WM_DRAWITEM جواب ميدهند که اين کار در وي بي امکان ندارد ( در يک تاپيک دوستي ادعا کرده بود که اين کار - پردازش پيغامهاي Custome در وي بي بدون Hook يا SubClass ممکن است اما هيچوقت اين راه را نشان نداد- متاسفانه- ) به هر حال اگر کسي هست که اين مطلب را ميحواند و ميتواند اين کار را بکند (بدون Hook و SubClass ) آنوقت به من هم ياد بدهد (البته بهتر است وقت خودتان را تلف نکنيد!)
حالا ميرويم سراغ دلفي منو و فونت اگر ديده باشيد دلفي ميتواند فونت ها را براي منو عوض کند اين به معناي نقض گفته هاي بالايي نيست. منو هاي دلفي استاندارد نيستند (گر چه دقيقا با منو هاي استاندارد مشابه هستند) حتي خصيصه Handle که در Help دلفي ادعا شده براي استفاده در توابع Api مفيد است به درد اين کار نميخورد مثلا نميتوان توسط توابع AppendMenu يا InsertMenuItem گزينه اي به اين منو ها اضافه کرد.

و اما مشکل اين دوست. اگر تمام کنترل هاي ديگر مثل Command و يا Label درست نمايش داده ميشوند يعني اينکه فونت مربوط به منوها در برنامه شما نميتواند علائم گچپژ را نشان دهد اما فونت مربوط به کنترل ها ميتواند. (ميتوانيد براي رفع اين مشکل از Display Properties \Appearance فونت منوها را عوض کنيد. (احتمالا فونت کنترل ها MS SansSerif است و فونت مربوط به منو ها Tahoma ) اگر مشکلتان حل نشود من متاسفانه راه ديگري بلد نيستم.

درمورد مساله دوم. ميتوانيد يک تابع ساده (با يک Switch ساده) که کل کد هاي معادل دار را به کد معادلشان تبديل کند را بنويسيد و شايد من مشکل شما را درست متوجه نشده ام؟؟


براي درک بيشتر مساله منو هاي Owner Draw يک مثال با دلفي نوشتم که از چند تا Api ساده استفاده ميکند. چون ميخواستم که توابع مربوط به Device Context يا DC را اضافه نکنم از Canvas دلفي استفاده کردم.

تابع اول
کد:
function GetSystemMenu(hWnd: HWND; bRevert: BOOL): HMENU; stdcall; 
function GetSystemMenu; external 'user32.dll' name 'GetSystemMenu';
اين تابع منوي سيتم يک پنجره را برميگرداند (منوي کنترل هم گفته ميشود) آرگومان اول دستگيره پنجره است و آرگومان دوم اگر True باشد منو به حالت اول برميگردد و اگر False باشد يک کپي از منو به عنوان بازگشتي در اختيار شماست.

کد:
function AppendMenu(hMenu: HMENU; uFlags, uIDNewItem: UINT;  lpNewItem: PChar): BOOL; stdcall; 
function AppendMenu; external 'user32.dll' name 'AppendMenuA';
اين يکي هم يک گزينه به انتهاي منو اضافه ميکند. hMenu دستگيره منو است (که مثلا با تابع GetSystemMenu ميتواند گرفته شود) آرگومان دوم خصيصه منو را مشخص ميکند (ميتوانيد MSDN را براي همه گزينه ها ببينيد) آرگومان بعدي مشخصه منو که منو با آن شناسايي ميشود و آرگومان سوم متني که در منو نمايش داده ميشود (اگر منو با پرچم MF_STRING ساخته شود)
دو پيغام استاندارد هم استفاده شده يکي WM_MEASUREITEM و يکي WM_DRAWITEM که اولي براي تنظيم مشخصات اوليه مثل اندازه و دومي براي رسم آيتم استفاده ميشود براي توضيحات بيشتر به MSDN مراجعه کنيد .

اينهم کد فرم به صورت کامل. البته مثال فقط طريقه را نشان ميدهد و مطمئنا کار خاصي انجام نميدهد.(اين مثال آيکون برنامه را در سيستم منو به عنوان يک آيتم اضافه ميکند و رويداد آن را مديريت ميکند. FOldAppMessage هم متغيري است که فقط محض احتياط اضافه شده.تا اگر در زنجيره پردازش پيغام تابع ديگري هم هست سرش بي کلاه نماند)
کد:
unit Unit1; 

interface 

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

type 
  TForm1 = class(TForm) 
    procedure FormCreate(Sender: TObject); 
  private 
    { Private declarations } 
    DelphiCanvas : TCanvas; 
    FOldAppMessage : TMessageEvent; 

    procedure OnWM_MEASUREITEM(var Msg : TMessage);message WM_MEASUREITEM; 
    procedure OnWM_DRAWITEM(var Msg : TMessage);message WM_DRAWITEM; 
    procedure OnAppMessage (var Msg: TMsg; var Handled: Boolean); 
  public 
    { Public declarations } 
  end; 

var 
  Form1: TForm1; 
const 
    SC_NEWITEM = 200; 
implementation 

{$R *.dfm} 

procedure TForm1.FormCreate(Sender: TObject); 
var 
    SysMnu : HMENU; 
begin 
    SysMnu:=GetSystemMenu(Handle,False); 
    AppendMenu(SysMnu,MF_SEPARATOR,SC_NEWITEM+1,nil); 
    AppendMenu(SysMnu,MF_OWNERDRAW ,SC_NEWITEM,nil); 

    FOldAppMessage:=Application.OnMessage; 
    Application.OnMessage:=OnAppMessage; 

    DelphiCanvas:=TCanvas.Create; 
end; 

procedure TForm1.OnWM_MEASUREITEM(var Msg : TMessage); 
var 
    MeItem : PMeasureItemStruct; 
begin 
    inherited; 
    if Msg.WParam<>0 then 
        Exit; 
    MeItem:=Pointer(Msg.LParam); 
    if MeItem^.itemID<>SC_NEWITEM then 
        Exit; 
    MeItem^.itemHeight:=Application.Icon.Height; 
end; 

procedure TForm1.OnWM_DRAWITEM(var Msg : TMessage); 
var 
    MePaint : PDrawItemStruct; 
    Rect : TRect; 
begin 
    inherited; 
    if Msg.WParam<>0 then 
        Exit; 
    MePaint:=Pointer(Msg.LParam); 
    if MePaint^.itemID<>SC_NEWITEM then 
        Exit; 
    DelphiCanvas.Handle:=MePaint^.hDC; 

    Rect:=MePaint^.rcItem; 
    if (MePaint^.itemState and ODS_SELECTED)<>0  then 
    begin 
        DelphiCanvas.Brush.Color:=clMaroon ; 
        DelphiCanvas.FillRect(Rect); 
        DelphiCanvas.Draw(Rect.Left-(Application.Icon.Width div 2)+(Rect.Right-Rect.Left) div 2 , 
                            Rect.Top,Application.Icon); 
    end 
    else 
    begin 
        DelphiCanvas.Brush.Color:=clMenu ; 
        DelphiCanvas.FillRect(Rect); 
        DelphiCanvas.Draw(Rect.Left-(Application.Icon.Width div 2)+(Rect.Right-Rect.Left) div 2 , 
                            Rect.Top,Application.Icon); 
    end; 

end; 

procedure TForm1.OnAppMessage(var Msg: TMsg; var Handled: Boolean); 
begin 

    Handled:=False; 
    if Msg.message=WM_SYSCOMMAND then 
    begin 
    if (Msg.wParam and $FFFFFFFF ) =SC_NEWITEM then 
            ShowMessage('You click on my delphi icon. what you want?'); 
    end; 

    if Assigned(FOldAppMessage) then 
        FOldAppMessage(Msg,Handled); 

end; 

end.

حالا اگر بخواهيد از يک فونت خاص استفاده کنيد يا حتي اگر بخواهيد به صورت نقاشي عمل کنيد کار سختي نيست. يعني مشکل فارسي به راحتي حل ميشود (البته در دلفي و به طرز مشابهي سي ).


موفق باشيد
خرگوش کوچولو
( ;<
 

littlerabbit

مدیر بازنشسته
کاربر فعال
تاریخ عضویت
13 جولای 2003
نوشته‌ها
667
لایک‌ها
5
سن
38
محل سکونت
Iran
اينو تازه ديدم! گرچه مربوط به خيلي وقت پيشه. فكر ميردم تو جريان هك كذايي از بين رفته. اما هنوز سر جاشه! اما با يه نويسنده جديد :lol: خوب منم ميارمش تو بخش دلفي.
 
بالا