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);
      }
    }
}

次の画像は、単一行編集の問題を示しています (画像は見やすくするために拡大されています)。黄色の背景は強調表示のみを目的としており、実際のコードではCOLOR_WINDOWシステム カラーを使用しています。単一行の編集にテキストがあり、入力がある場合、左側の画像が塗りつぶされていることがわかります。SetRectこれは、書式設定の四角形を正しく設定する複数行編集では発生しません。

ここに画像の説明を入力

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);
      }
    }
}

同様の編集コントロールの他の実装を見てきましたが、現在はすべて同じ問題があります。

明らかに問題は、コントロールの入力領域から画像領域を除外するにはどうすればよいかということです。

4

2 に答える 2

0

何が起こっているのかというと、 が呼び出さCPaintDCれ、編集ボックスにBeginPaint()a が送信されます。WM_ERASEBKGND無視できなかったので、おそらく内部STATICウィンドウに送信されているのでしょうか?わからない。

ハンドラーを呼び出しExcludeClipRect()ても、クリッピング領域がいずれかまたは独自のハンドラーのクライアント領域全体にリセットされるOnPaint()ため、何もしません。EDITBeginPaint()WM_PAINT

ただし、それ自体をペイントする直前に親に を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);
}

EDIT次に、ウィンドウをサブクラス化して独自のアイコンを に描画WM_PAINTし、メッセージを転送したので、他のすべてを自分で描画する必要はありませんでした。

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()and EndPaint()( a の構築と同等) をCPaintDC呼び出すことができなかったことに注意してください。WM_PAINT私はそれがBeginPaint()2回の呼び出し(手動で1回、 で1回EDIT)と の処理に関係していると推測していWM_ERASEBKGNDます。YMMV、特に MFC で。

最後に、次を作成した直後にマージンを設定しEDITます。

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

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

于 2016-08-29T18:45:15.573 に答える