VCL에서는
메인폼(Application->MainForm)은
Application->CreateForm 이라는 함수로 제일먼서 생성한 폼이 메인폼이 됩니다.
그런데 작업표시줄에 나타나는 버튼은 Application->MainForm의 버튼이 아니라
Application->Handle이라는 보이지 않는 윈도우의 버튼입니다.
참조
http://cbuilder.borlandforum.com/impboard/impboard.dll?action=read&db=bcb_tip&no=602
VCL에서는 Application->Handle로 들어오는 메세지중 일부를
Application->MainForm->Handle 보내버리죠
메인폼과 Application은 매우 유착관계에 있습니다.
ShowMessage도 T
MessageForm이라는 TForm을 상속받은 폼을 ShowModal한것 뿐인데요...
VCL에서 생성하는 모든 폼은 기본적으로 WndParent를 Application->Handle로 설정되게 됩니다.
님께서 sub-form도 작업표시줄에 나타나도록 CreateParams를 에서 수정하셨다면..
http://cbuilder.borlandforum.com/impboard/impboard.dll?action=read&db=bcb_tip&no=18
메세지 박스 (ShowMessage) 뿐만 아니라 몇가더 문제가 발생합니다.
1. ShowMessage문제
2. Hint문제
(서브폼에서 hint가 뜰때 메인폼이 앞으로 튀어나옴)
3. OpenDialog같은 공통Dialog를 열때..
(서브폼에서 opendialog를 띄울때 메인폼이 앞으로 튀어나옴)
기타..
이 모든 경우가 새로띄우는 윈도우의 WndParent기 기본적으로 Application->Handle로 되기때문입니다.
CBuilder 및 Delphi버젼이 2005이상인경우에는
Application->ModalPopupMode 라는것을 이용해서 문제를 해결할수 있습니다.
http://cbuilder.borlandforum.com/impboard/impboard.dll?action=read&db=bcb_tip&no=748
http://www.codeway.co.kr/board/bbs/board.php?bo_table=Delphi_QNA&wr_id=4528&page=&sca=&sfl=&stx=&spt=&page=&cwin=#c_4543
그런데 CBuilder6 이하 Delphi7 이하의 경우 직접 해결해야 합니다.
간단한 해결방법은?
* 메인폼의 WndParent도 Application->Handle이 아니라 GetDesktopWindow로 설정하구
메인폼이 Show될때 Application->Handle을 Hide시켜 버립니다.
또다른 해결방법은?
* ShowMessage의 경우
TMessageForm을 새로만들어서 TMessageForm의 WndParent를 변경해줘야 하구요
* Hint의 경우 에도
THintWindow클래스를 상속받아서 hint윈도우를 새로 만들고 (예 THintWindowEx )
hint윈도우의 WndParent를 변경해줘야 합니다.
그리고 Application에서 Hint를 띄우는데
힌트 띄울때 직접만든 hint-class를 사용하도록 HINTWINDOWCLASS를 지정해주면 됩니다.
* 그리고 OpenDialog도
직접 Comdlg32.dll에 있는 GetOpenFileNameA 를 사용해서 직접 만드시면 되구요...
님께서 원하는게 정확하게 뭔지 잘 이해가 안가는데..
여튼 메인폼위에 다른 폼을 작업표시줄에 나타나게 하면 위와같은 문제가 발생할수 있습니다.
그럼...
조안 님이 쓰신 글 :
: 그룹설정폼과 직원입력및검색폼은 동일 레벨에 존재하는 폼입니다
: 둘다 메인폼에서 띄우고요
: Show 로 띄우고 있습니다.
:
: 이상황에서 메시지 박스를 띄워도
: 다른폼에 포커스가 갈수가 있습니다;
: 제가말했던 alt+tab 이나 taskbar 선택에 의해서요
: 물론 disabled 되어있어서 클릭도안되고 아무것도 안되나
: 포커스는 가게되고, 맨앞으로 오게됩니다.(메인은 안오구요)
:
: 그리고 메인에 다시 포커스를 줘야만 메시지창이 제일위로 올라오게됩니다.
:
: 메시지창은 ShowModal 로 띄우고 있구요
:
: 대신 메시지창은 함수를 쓰는게 아니고 컴포넌트를 사용하는 방식이라
: HWND 를 설정할 수 없는듯합니다.
:
: 현재 createParams 에
: Params.WndParent = GetDesktopWindow(); 이게 모두 주석처리가 되어있는데
: 태스크바에서 없어지지 않고 뜹니다(일부러 뜨게 코딩해놓은듯..)
:
: 지금 현재의 문제점 두개는
:
: 1.차일드폼이 메인폼위에 스태이온탑으로 올라가버립니다.
: -createparams 에서 screen->active->handle = wndparent 로 바꾸기 전에는
: 메인폼에 포커스가 가면 메인폼이 최상위로 왔습니다.
: 어느부분을 수정해야 할 지 잘 모르겠네요
:
: 2.메시지박스가 뜰때 다른 폼을 태스크바 or Alt+Tab 으로 선택하면 메시지박스가 가려집니다.
: 항상 나오던지(Stay on Tob), 아니면 메시지박스를 띄운 폼을 클릭할시 나오던지
: 둘중에 하나가 되야하는데. 이것도 잘 모르겠네요.
:
: 써주신것중에 답이 있을지도 모르지만. 여러번 읽어보았는데 아직 이해가 잘 안갑니다.
: 귀찮으시겠지만 한번더 자세한 설명 부탁드립니다.
:
: 현재 메시지박스를 만들어서 쓸수는 없는 상황이나
: 컴포넌트를 조금 수정해서 해결할 수 있는 문제라고 한다면 컴포넌트는 수정 할 수 있습니다.
:
: 아래 메시지박스를 띄우는 컴포넌트를 올리겠습니다
:
:
: int __fastcall TUCommon::Get_MessageBox(AnsiString Msg, AnsiString Caption,
: TMsgDlgType DlgType, TMsgDlgButtons Buttons)
: {
: int rtn_value;
:
: TbsSkinMessage *My_MsgBox;
:
: My_MsgBox = new TbsSkinMessage(Application);
:
: My_MsgBox->CtrlSkinData = MySkin;
: My_MsgBox->SkinData = MySkin;
: My_MsgBox->UseSkinFont = false;
: My_MsgBox->DefaultButtonFont = MyFont;
: My_MsgBox->DefaultFont = MyFont;
:
: rtn_value = My_MsgBox->MessageDlg2(Msg, Caption, DlgType, Buttons, 0);
:
: delete My_MsgBox;
:
: return rtn_value;
: }
:
:
:
:
: function TbsSkinMessage.MessageDlg2;
: begin
: with CreateMessageDialog2(Msg, ACaption, DlgType, Buttons,
: FSD, FCtrlFSD, FButtonSkinDataName,
: FMessageLabelSkinDataName, FDefaultFont, FDefaultButtonFont, FUseSkinFont,
: FAlphaBlend, FAlphaBlendAnimation, FAlphaBlendValue, FShowAgainFlag, FShowAgainFlagValue,
: -10001, -10001) do
: begin
: try
: HelpContext := HelpCtx;
: Result := ShowModal;
: finally
: if DisplayCheckBox <> nil
: then
: begin
: Self.ShowAgainFlagValue := not DisplayCheckBox.Checked;
: end;
: Free;
: end;
: end;
: end;
:
:
:
: function CreateMessageDialog2(const Msg: string; ACaption: String;
: DlgType: TMsgDlgType;
: Buttons: TMsgDlgButtons; ASkinData, ACtrlSkinData: TbsSkinData;
: AButtonSkinDataName: String; AMessageLabelSkinDataName: String;
: ADefaultFont: TFont; ADefaultButtonFont: TFont; AUseSkinFont: Boolean;
: AAlphaBlend, AAlphaBlendAnimation: Boolean; AAlphaBlendValue: Byte;
: AShowAgainFlag, AShowAgainFlagValue: Boolean; MX, MY: Integer): TbsMessageForm;
: var
: BI, ButtonWidth,
: ButtonHeight, ButtonSpacing, ButtonCount, ButtonGroupWidth, X, Y: Integer;
: B, DefaultButton, CancelButton: TMsgDlgBtn;
: IconID: PChar;
: W: Integer;
: begin
: Result := TbsMessageForm.CreateEx(Application, MX, MY);
: with Result do
: begin
: with BSF do
: begin
: SkinData := ASkinData;
: MenusSkinData := ACtrlSkinData;
: AlphaBlend := AAlphaBlend;
: AlphaBlendAnimation := AAlphaBlendAnimation;
: AlphaBlendValue := AAlphaBlendValue;
: end;
:
: ButtonWidth := 70;
: //
: if (ACtrlSkinData <> nil) and (not ACtrlSkinData.Empty)
: then
: begin
: BI := ACtrlSkinData.GetControlIndex(AButtonSkinDataName);
: if (BI <> -1) and
: (TbsDataSkinControl(ACtrlSkinData.CtrlList.Items[BI]) is TbsDataSkinButtonControl)
: then
: begin
: with TbsDataSkinButtonControl(ACtrlSkinData.CtrlList.Items[BI]) do
: ButtonHeight := SkinRect.Bottom - SkinRect.Top;
: end
: else
: ButtonHeight := 25;
: end
: else
: ButtonHeight := 25;
: //
: ButtonSpacing := 10;
:
: ButtonCount := 0;
: for B := Low(TMsgDlgBtn) to High(TMsgDlgBtn) do
: if B in Buttons then Inc(ButtonCount);
:
: ButtonGroupWidth := 0;
: if ButtonCount <> 0 then
: ButtonGroupWidth := ButtonWidth * ButtonCount +
: ButtonSpacing * (ButtonCount - 1);
:
: Caption := ACaption;
:
: // add label
: Result.Message := TbsSkinStdLabel.Create(Result);
: with Result.Message do
: begin
: Font := ADefaultFont;
: DefaultFont := ADefaultFont;
: UseSkinFont := AUseSkinFont;
: SkinDataName := AMessageLabelSkinDataName;
: SkinData := ACtrlSkinData;
: Name := 'Message';
: Parent := Result;
: AutoSize := True;
: Caption := Msg;
: Left := 50;
: Top := 15;
: X := Left + Width;
: end;
:
: IconID := IconIDs[DlgType];
: with TImage.Create(Result) do
: begin
: Name := 'Image';
: Parent := Result;
: Picture.Icon.Handle := LoadIcon(0, IconID);
: Y := Result.Message.Top + Result.Message.Height div 2 - 16;
: if Y < 10 then Y := 10;
: SetBounds(5, Y, 32, 32);
: end;
:
: ClientHeight := 50 + ButtonHeight + Result.Message.Height;
:
: if ButtonGroupWidth < X
: then
: ClientWidth := X + 40
: else
: ClientWidth := ButtonGroupWidth + 40;
:
:
: // "Do not display this message again" flag
:
: if AShowAgainFlag
: then
: begin
: ClientHeight := ClientHeight + 40;
: Result.DisplayCheckBox := TbsSkinCheckRadioBox.Create(Result);
: with Result.DisplayCheckBox do
: begin
: Name := 'checkbox';
: DefaultHeight := 30;
: Checked := not AShowAgainFlagValue;
: Parent := Result;
: DefaultFont := ADefaultFont;
: UseSkinFont := AUseSKinFont;
: SkinData := ACtrlSkinData;
: Y := Result.ClientHeight - ButtonHeight - 50;
: if (ACtrlSkinData <> nil) and (ACtrlSkinData.ResourceStrData <> nil)
: then
: begin
: Caption := ACtrlSkinData.ResourceStrData.GetResStr('MSG_CAP_SHOWFLAG');
: if Caption = '' then Caption := BS_MSG_CAP_SHOWFLAG;
: end
: else
: Caption := BS_MSG_CAP_SHOWFLAG;
: if (FIndex <> -1) and UseSkinFont
: then
: begin
: Result.Canvas.Font.Name := FontName;
: Result.Canvas.Font.Height := FontHeight;
: end
: else
: Result.Canvas.Font.Assign(ADefaultFont);
: W := Result.Canvas.TextWidth(Caption);
: W := W + 30;
: if Result.ClientWidth < W + 30 then Result.ClientWidth := W + 30;
: SetBounds(5, Y, W + 25, DefaultHeight);
: end;
: end;
:
: if Width > Result.BSF.GetMaxWidth
: then
: Width := Result.BSF.GetMaxWidth
: else
: if Width < Result.BSF.GetMinWidth
: then
: Width := Result.BSF.GetMinWidth;
:
: // add buttons
: if mbOk in Buttons then DefaultButton := mbOk else
: if mbYes in Buttons then DefaultButton := mbYes else
: DefaultButton := mbRetry;
: if mbCancel in Buttons then CancelButton := mbCancel else
: if mbNo in Buttons then CancelButton := mbNo else
: CancelButton := mbOk;
: X := (ClientWidth - ButtonGroupWidth) div 2;
: for B := Low(TMsgDlgBtn) to High(TMsgDlgBtn) do
: if B in Buttons then
: with TbsSkinButton.Create(Result) do
: begin
: Parent := Result;
: Name := ButtonNames[B];
: CanFocused := True;
: Caption := GetButtonCaption(B, ACtrlSkinData);
: ModalResult := ModalResults[B];
: if B = DefaultButton then Default := True;
: if B = CancelButton then Cancel := True;
: DefaultHeight := ButtonHeight;
: SetBounds(X, Result.ClientHeight - ButtonHeight - 10,
: ButtonWidth, ButtonHeight);
: DefaultFont := ADefaultButtonFont;
: UseSkinFont := AUseSkinFont;
: Inc(X, ButtonWidth + ButtonSpacing);
: if B = mbHelp then
: OnClick := Result.HelpButtonClick;
: SkinDataName := AButtonSkinDataName;
: SkinData := ACtrlSkinData;
: end;
: end;
: end;
:
:
: 장성호 님이 쓰신 글 :
: : 메인폼에서 => 그룹 설정폼 => 직원입력 및 검색폼 => 메세지 박스
: : 이런식으로 띄운다구요
: :
: : 메인폼에서 => 그룹설정폼 띄울때
: : Show로 하나요 ? 아니면 ShowModal로 띄우나요?
: :
: : 그룹 설정폼 에서 => 직원입력 및 검색폼 을 띄울때는 Show인가요?
: :
: : 질문으로 봤을때는 ..
: :
: : 그룹 설정폼 과 직원입력 및 검색폼 을 TaskBar(작업표시줄)에 나타나도록 수정하신것 같구
: : 그룹 설정폼 과 직원입력 및 검색폼 두개다 띄울때 ShowModal이 아닌 Show로 띄우는것 같은데
: : 맞습니까?
: :
: : 여기까지 상황에서는 어떤폼이 active되어있더라도
: : 작업표시줄에서 다른 윈도우의 버튼을 클릭하면 그 폼이 Active 됩니다.
: :
: : 그런데 마지막 "메세지 박스"를 띄울때는
: : "메세지 박스"를 어떤 폼에서 띄웠다고 하더라도 ShowModal로 뜨기 때문에..
: : 현재 쓰레드의 모든 폼이 disable됩니다.
: :
: : 이때는 작업표시줄에서 다른 폼을 선택하더라도 그 폼이 active되지 않습니다.
: : ----------------------------------------------------------------
: :
: : 만약 메세지박스를 띄운폼이 그 폼에서만 Modal이 되게 하려면...
: :
: : ShowMessage와 같은 것을 직접 만드시면 됩니다.
: : Win32API "MessageBox" 는 쓰시면 안되구요
: :
: : ----------------------------------------------------------------
: : 메세지 박스가 특정폼에서만 Modal이 되게하려면 ..
: : 이건 약간 신경을 써야 하는데....
: :
: : ShowModal의 원리를 이해하셔야 합니다.
: :
: : ShowModal이란 (showModal함수 코드는?)
: :
: : 1) 현재 쓰레드에 있는 모은 윈도우 핸들을 Disable시키고 메세지 박스만 띄웁니다.
: : 2) 그리고 메세지 박스가 Close될때까지(ModalResult가 none이 아닐때까지) 계속 대기하면서
: : 메세지 펌프 루프(while문 )를 돌립니다.
: : 3) 메세지박스가 clsoe되면 (또는 ModalResult가 None이 아니면) ..
: : 아까전에 diable시킨 모든 윈도우를 다시 Active시키고
: : 가장 최근에 Active되어있었던 폼에 Focus를 주고 함수를 빠져나옵니다.
: :
: : -------------------------------------------------------------------
: : ShowModal이 위와같은 원리로 되어있는데..
: : 특정 윈도우에 대해서만 Modal이 되려면
: : 메세지 박스를 띄울때 쓰레드의 모든 윈도우를 disable시키는것이 아니라
: : 메세지박스를 띄운 윈도우만 disable시키면 됩니다. ****************************>>>>> 핵심
: : 문론 메세지 박스가 close될땐 해당 윈도우만 enable시키구요
: :
: : 이런식으로 ShowMessage를 새로 만들어서 사용하셔야 합니다.
: : 제가 이해한것이 많다면 같은 쓰레드에서 특정 폼에서만 Modal로 되는 메세지 박스는
: : 제가 알기로는 아직까지 VCL에서 지원하지 않습니다.
: :
: : 문론 Win32API도 지원하지 않구요..
: :
: :
: : 즉 만들어 써야 합니다.
: :
: : 그럼..
: :
: :
: :
: :
: : 조안 님이 쓰신 글 :
: : : 위의말은 문제가 되는걸 말하는게 아니라 현재 메시지창을 띄우는 방식을 말씀드린것입니다.
: : :
: : : 예를들어 설명하면. 직원관리 시스템에서
: : :
: : : 직원 입력및 검색하는 폼이있고
: : : 직원 그룹설정하는 폼이 있다고 하면
: : : 메인폼에서 그룹설정폼을 띄우고, 직원입력및검색 폼을 띄웁니다
: : :
: : : 그럼
: : :
: : : 메인폼위에 그룹설정폼,그위에 입력및검색 폼이 올라가 있는 상태가 되겠죠
: : : 그상황에서 직원입력폼에서 직원명을 입력해달라는 메시지박스를 띄웁니다.
: : :
: : : 그럼 메인폼과 그룹설정폼,입력및검색폼 모두 선택할 수는 없지만
: : : Alt+Tab 또는 윈도우 작업표시줄에서는 각 폼을 선택할수가 있게 되어있습니다
: : : 그럼 선택된 폼(그룹설정(이 최상위로 올라오게 되고. 메시지박스와 다른폼은 뒤로 밀려나게 됩니다.
: : : 그상황에서 다시 메시지박스를 띄웠던 직원입력폼을 다시 작업표시줄을 사용해 선택합니다.
: : : 그상황에서 메시지박스가 최상위로 올라왔으면 하나, 메시지박스는 여전히 직원입력폼,그룹설정폼 뒤에 위치하게 됩니다. 그래서 메시지박스 폼이 보이지않죠.
: : : 이상황에서 메인입력폼을 선택하게 되면 메시지박스가 최상위로 나옵니다. 이부분이 왜그런지 아직 잘 모르겠네요.
: : :
: : : 그리고 두번째로. 메인폼에서 아무폼이나 하나 띄운뒤
: : : 아무것도 하지않고 다시 메인폼을 선택하면, 메인폼이 최상위로 와야하는데
: : : 최상위로 오지않고 액티브상태만 되게 됩니다.
: : : 그럼 띄운폼에 가려서 메인폼에서 작업을 할 수가 없게되죠.
: : :
: : : 제가좀 두서없이 말을 하네요. 어떻게 설명을 드려야할지 잘 모르겠어서
: : :
: : : 부족하시면 그림으로 그려서 올려보겠습니다..
: : :
: : : 장성호 님이 쓰신 글 :
: : : : : 메인 폼이 뜨고. 그위에 차일드폼을 띄우고. 그위에 얼럿이 뜨거나. 세부폼이 한번더뜨고 그위에 얼럿이뜨는
: : : : : 그런형식으로 짜여져 있습니다
: : : :
: : : : 위 말이 무슨 말인지 잘 모르겠네요...
: : : :
: : : : ShowMessage나 MessageBox를 사용하면
: : : : 현재 Thead즉 메인쓰레드에서 는 메세지 박스가 Modal 로 뜨는것 아시죠?
: : : : 메세지 박스를 닫기 전까지는 다른 폼을 선택할수 없습니다.
: : : :
: : : : --------------------------------------------------------------------------------
: : : : 그리고 중요한 사실한가지가 있는데요
: : : : 왜 저런 현상이 나타나는가 하니..
: : : : VCL에서는 작업표시줄에 나타나는 Application버튼이 메인윈도우 핸들에 해당하는 버튼이 아닙니다.
: : : : (2005이상에서는 조절 가능)
: : : :
: : : : 작업표시줄에 나타나는 버튼은 Application->Handle입니다.
: : : : VCL에서 만든 모든 폼은 Application->Handle을 Params.WndParent 으로 가지게 됩니다.
: : : : 메인폼이나 다른 폼이나 모두 Application->Handle을 WndParent로 가지게 되는것입니다.
: : : :
: : : : 만약 sub-form의 WndParent는 메인폼->Handle로 되면
: : : : sub-form은 항상 메인폼 앞에 만 오죠(2005이상에서는 옵션으로 가능합니다.)
: : : :
: : : :
: : : : Win32API MessageBox는
: : : :
http://msdn.microsoft.com/en-us/library/ms645505(VS.85).aspx
: : : : 첫번째 인자가 HWND입니다.
: : : :
: : : : MessageBox(..ActiveForm->Handle , ...) 이렇게 하면 되게죠..
: : : :
: : : : 그리고 아래 child 의 변경은
: : : : Params.WndParent = GetDesktopWindow();
: : : : child폼을 작업표시줄에 버튼이 나타나게 하기 위한것 같네요.
: : : :
: : : : 그럼...
: : : :
: : : :
: : : : 조안 님이 쓰신 글 :
: : : : : 이전 글 :
http://cbuilder.borlandforum.com/impboard/impboard.dll?action=read&db=bcb_qna&no=57169
: : : : :
: : : : : 답해주신 장성호님 감사합니다.
: : : : :
: : : : : 장성호님 말씀대로 시도해보다가. createparams 에서
: : : : :
: : : : :
: : : : : void __fastcall TfrmF_014::CreateParams(TCreateParams &Params)
: : : : : {
: : : : : TForm::CreateParams(Params);
: : : : : Params.ExStyle = WS_EX_APPWINDOW;
: : : : : // Params.WndParent = GetDesktopWindow(); //Before
: : : : : Params.WndParent = Screen->ActiveForm->Handle; //After
: : : : : }
: : : : :
: : : : : Before 를 After 로 고쳐서 해결보았습니다.
: : : : :
: : : : : 그러나 여기서 문제가 발생했습니다
: : : : :
: : : : : 메인 폼이 뜨고. 그위에 차일드폼을 띄우고. 그위에 얼럿이 뜨거나. 세부폼이 한번더뜨고 그위에 얼럿이뜨는
: : : : : 그런형식으로 짜여져 있습니다
: : : : :
: : : : : 그러나 createparams 를 고치니 차일드 폼에 액티브가 가있는상태에서 메인폼에 액티브를 줘도, 스크린폼이 뒤로가지 않아 메인폼을 확인할수가 없더군요.(한마디로 차일드폼이 stay on top 이 되어있는 것과 같이 동작)
: : : : : 물론 모든 폼의 FormStyle 은 fsNormal 입니다.
: : : : :
: : : : : 참고로 메인폼은 createparams 를 코딩하지 않았습니다.
: : : : :
: : : : : 게다가 페어런트를 액티브폼으로 바꿔주었는데도. 차일드폼에서 띄운 메시지창이 다른 차일드폼에 의해서 가려졌을때,
: : : : : 메인창을 선택해야만 (윈도우 바 or Alt+Tab) 메시지창이 뜨고, 메시지창을 띄운 차일드폼을 선택해도
: : : : : 메시지창이 뒤에있게됩니다.
: : : : :
: : : : :
: : : : : 페어런트 설정에서 뭔가 잘못이 있었는건지. 순서의 잘못인지. 잘모르겠네요
: : : : :
: : : : : 또다시 염치불구하고 질문을 드립니다. 도와주세요 ^^