2016-08-11 17 views
8

コントロールの状態とテキストに関係なく、常にコントロールウィンドウにアイコンが表示されているMFCで検索編集コントロールを作成しようとしています。私は何年も前にこのようなことを書いていて、とてもうまくいっていますが、Windows 7以降では動作しなくなりました(Vistaでも可能ですが、試していません)。何が起こるかは、コントロールに表示されている画像が入力エリアと重なっていることです(下の画像を参照)。編集コントロールの検索アイコンが入力領域と重複しています

コードの背後にある考え方に:

  • CEditから派生したクラス(つまり、のOnPaintで絵を担当)
  • アイコンが右側に表示され、編集領域はのサイズに基づいて縮小されてい
  • 単一行と複数行の編集では、サイズ変更が異なって行われます。 1行の場合はSetMarginsと呼び、複数行の編集の場合はSetRectと呼びます。
  • この編集サイズ変更がPreSubclassWindow()OnSize()OnSetFont()

で適用される。これは、編集入力サイズが適用される方法である。

void CSymbolEdit::RecalcLayout() 
{ 
    int width = GetSystemMetrics(SM_CXSMICON); 

    if(m_hSymbolIcon) 
    { 
     if (GetStyle() & ES_MULTILINE) 
     { 
     CRect editRect; 
     GetRect(&editRect); 

     editRect.right -= (width + 6); 

     SetRect(&editRect); 
     } 
     else 
     { 
     DWORD dwMargins = GetMargins(); 
     SetMargins(LOWORD(dwMargins), width + 6); 
     } 
    } 
} 

次の画像は、単一の行の編集の問題を示しています(より良い視野のために画像が拡大されている)。黄色い背景は、実際のコードでは、システムカラーを使用しています。 1行の編集にテキストがあり、入力があると、左側のイメージが塗りつぶされていることがわかります。これは、SetRectが書式指定の矩形を正しく設定する複数行編集では発生しません。

enter image description here

Iは、画像が表示されている編集の領域を除去するExcludeClipRectを用いて試みました。

CRect rc; 
GetClientRect(rc); 

CPaintDC dc(this); 
ExcludeClipRect(dc.m_hDC, rc.right - width - 6, rc.top, rc.right, rc.bottom); 

DWORD dwMargins = GetMargins(); 
SetMargins(LOWORD(dwMargins), width + 6); 

これは結果に影響しないようです。

参考までに、これは数年前に書かれた、Windows XP上でうまく動作するのに使用されていましたが、これ以上は修正されませんでした。

void CSymbolEdit::OnPaint() 
{ 
    CPaintDC dc(this); 

    CRect rect; 
    GetClientRect(&rect); 

    // Clearing the background 
    dc.FillSolidRect(rect, GetSysColor(COLOR_WINDOW)); 

    DWORD dwMargins = GetMargins(); 

    if(m_hSymbolIcon) 
    { 
     // Drawing the icon 
     int width = GetSystemMetrics(SM_CXSMICON); 
     int height = GetSystemMetrics(SM_CYSMICON); 

     ::DrawIconEx( 
      dc.m_hDC, 
      rect.right - width - 1, 
      1, 
      m_hSymbolIcon, 
      width, 
      height, 
      0, 
      NULL, 
      DI_NORMAL); 

     rect.left += LOWORD(dwMargins) + 1; 
     rect.right -= (width + 7); 
    } 
    else 
    { 
     rect.left += (LOWORD(dwMargins) + 1); 
     rect.right -= (HIWORD(dwMargins) + 1); 
    } 

    CString text; 
    GetWindowText(text); 
    CFont* oldFont = NULL; 

    rect.top += 1; 

    if(text.GetLength() == 0) 
    {  
     if(this != GetFocus() && m_strPromptText.GetLength() > 0) 
     { 
      oldFont = dc.SelectObject(&m_fontPrompt); 
      COLORREF color = dc.GetTextColor(); 
      dc.SetTextColor(m_colorPromptText); 
      dc.DrawText(m_strPromptText, rect, DT_LEFT|DT_SINGLELINE|DT_EDITCONTROL); 
      dc.SetTextColor(color); 
      dc.SelectObject(oldFont); 
     } 
    } 
    else 
    { 
     if(GetStyle() & ES_MULTILINE) 
     CEdit::OnPaint(); 
     else 
     { 
     oldFont = dc.SelectObject(GetFont()); 
     dc.DrawText(text, rect, DT_SINGLELINE | DT_INTERNAL | DT_EDITCONTROL); 
     dc.SelectObject(oldFont); 
     } 
    } 
} 

私は同様のエディットコントロールの他の実装を見てきましたが、今はすべて同じフォールトがあります。

明らかに、問題はコントロールの入力領域からイメージ領域を除外することですか?私は何が起こっているの思い

+0

あなたの 'OnPaint'オーバーライドは、編集コントロールのペイントルーチンと戦っています。 'CPaintDC'を使ってコントロールを手作業でペイントし、時には' CEdit :: OnPaint'を呼び出します。そして 'CPaintDC'を再度呼び出して、クライアントエリアを再描画するデフォルトの処理を行います。編集コントロールのフォーカスが合ったり外れたりしたとき、またはペイントメッセージが受信されたときには、これは失敗します。 –

+0

'CEdit :: OnPaint()'は複数行の編集に対してのみ呼び出されますが、これは私の心配ではありません。私は1行編集コントロールしか使用していません。この場合、境界を正しく設定すると、複数行の編集について述べました。 –

+0

'CWnd :: OnCtlColor()'をオーバーライドし、そこから 'ExcludeClipRect()'を呼び出すとどうなりますか?クイックテストではうまく動作するようですが、MFCはありません。 'OnPaint()'で再びクリッピングを変更しなければならないかもしれません。 – isanae

答えて

0

があるという編集ボックスにWM_ERASEBKGNDを送るCPaintDC通話BeginPaint()、。私はそれを無視することができませんでしたので、おそらく内部STATICウィンドウに送信されていると思いますか?わからない。 EDITBeginPaint()または独自のWM_PAINTハンドラのいずれかで全体のクライアント領域にクリッピング領域をリセットしますので、何もしません、あなたのOnPaint()ハンドラでExcludeClipRect()を呼び出す

しかし、EDITは、塗り潰す直前に親にWM_CTRCOLOREDITを送信しますが、一見してクリッピング領域を設定した後に送信されます。だから、ExcludeClipRect()に電話することができます。共通コントロールの将来のバージョンで変更される可能性のある実装の詳細のように聞こえます。確かに、それは既にそうしているようです。

私はここに私のウィンドウプロシージャだ、Windows 7でMFCなしで​​簡単なテストをしました:

LRESULT CALLBACK wnd_proc(HWND h, UINT m, WPARAM wp, LPARAM lp) 
{ 
    switch (m) 
    { 
     case WM_CTLCOLOREDIT: 
     { 
      const auto dc = (HDC)wp; 
      const auto hwnd = (HWND)lp; 

      RECT r; 
      GetClientRect(hwnd, &r); 

      // excluding the margin, but not the border; this assumes 
      // a one pixel wide border 
      r.left = r.right - some_margin; 
      --r.right; 
      ++r.top; 
      --r.bottom; 

      ExcludeClipRect(dc, r.left, r.top, r.right, r.bottom); 

      return (LRESULT)GetStockObject(DC_BRUSH); 
     } 
    } 

    return ::DefWindowProc(h, m, wp, lp); 
} 

私はその後、WM_PAINTに自分のアイコンを描画するためにEDITウィンドウをサブクラス化して、私は」didnのメッセージを転送します私は他のすべてを自分で描く必要があります。私はBeginPaint()EndPaint()境界線が引かれませんでしたので、WM_PAINTで(CPaintDCの構築に相当)を呼び出すことができませんでした

LRESULT CALLBACK edit_wnd_proc(
    HWND h, UINT m, WPARAM wp, LPARAM lp, 
    UINT_PTR id, DWORD_PTR data) 
{ 
    switch (m) 
    { 
     case WM_PAINT: 
     { 
      const auto dc = GetDC(h); 

      // draw an icon 

      ReleaseDC(h, dc); 
      break; 
     } 
    } 

    return DefSubclassProc(h, m, wp, lp); 
} 

注意。私はそれがBeginPaint()を2回(手動で1回、EDITで1回)呼び出し、処理をWM_ERASEBKGNDと呼ぶことと何か関係があると推測しています。 YMMV、特にMFCで。

は最後に、私は右のEDITを作成した後に余白を設定します。

SendMessage(
    e, EM_SETMARGINS, 
    EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELPARAM(0, margin)); 

をあなたはまた、システムフォントが変更された場合、再び余白を更新する必要がある場合があります。

関連する問題