1

I try to add an empty markup to a ListView, but when I use SetWindowTheme or AutoResizeColumns, the markup disappears. Here's an example:

internal static class ExtensionMethods
{
    public static void SetDoubleBuffered(this ListView listview, bool value)
    {
        PropertyInfo pi = typeof(ListView).GetProperty("DoubleBuffered", BindingFlags.NonPublic | BindingFlags.Instance);
        pi.SetValue(listview, value);
    }

    public static void SetWindowTheme(this ListView listview, string pszSubAppName, string pszSubIdList)
    {
        NativeMethods.SetWindowTheme(listview.Handle, pszSubAppName, pszSubIdList);
    }
}

internal static class NativeMethods
{
    public const Int32 WM_NOTIFY = 0x004e;
    public const Int32 WM_LBUTTONDBLCLK = 0x0203;
    public const Int32 WM_USER = 0x0400;
    public const UInt32 LVN_FIRST = unchecked(0u - 100u);
    public const UInt32 LVN_GETEMPTYMARKUP = LVN_FIRST - 87;
    public const Int32 L_MAX_URL_LENGTH = 2084;

    [StructLayout(LayoutKind.Sequential)]
    public struct NMHDR
    {
        public IntPtr hwndFrom;
        public IntPtr idFrom;
        public UInt32 code;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct NMLVEMPTYMARKUP
    {
        public NMHDR hdr;
        public UInt32 dwFlags;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = L_MAX_URL_LENGTH)]
        public String szMarkup;
    }

    [DllImport("uxtheme.dll", CharSet = CharSet.Unicode)]
    public static extern Int32 SetWindowTheme(IntPtr hWnd, String pszSubAppName, String pszSubIdList);
}

public partial class MainForm : Form
{
    private ListView listView1 = new ListView();

    public MainForm()
    {
        InitializeComponent();

        this.Controls.Add(listView1);

        listView1.Dock = DockStyle.Fill;
        listView1.View = View.Details;
        listView1.Columns.Add("Test");

        listView1.SetDoubleBuffered(true);

        // listView1.SetWindowTheme("Explorer", null);
        // listView1.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
    }

    protected override void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            case NativeMethods.WM_NOTIFY:
                var nmhdr = (NativeMethods.NMHDR)m.GetLParam(typeof(NativeMethods.NMHDR));
                switch (nmhdr.code)
                {
                    case NativeMethods.LVN_GETEMPTYMARKUP:
                        if (Control.FromHandle(nmhdr.hwndFrom) == listView1)
                        {
                            var markup = (NativeMethods.NMLVEMPTYMARKUP)m.GetLParam(typeof(NativeMethods.NMLVEMPTYMARKUP));
                            markup.szMarkup = "This is an empty ListView.";
                            Marshal.StructureToPtr(markup, m.LParam, false);
                            m.Result = new IntPtr(1);
                            return;
                        }
                        break;
                }
                break;
        }
        base.WndProc(ref m);
    }
}

This works. But if I uncomment either line of

    // listView1.SetWindowTheme("Explorer", null);
    // listView1.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);

the empty markup disappears. How do I set empty markup while using SetWindowTheme and AutoResizeColumns?

Update

I wrote an example of C version that confirms the empty mark can work with SetWindowTheme and something like AutoResizeColumns in Win32 level. But why not C#?

#define _CRT_SECURE_NO_DEPRECATE
#define WIN32_LEAN_AND_MEAN
#define STRICT

#include <tchar.h>
#include <Windows.h>
#include <windowsx.h>
#include <CommCtrl.h>
#include <Uxtheme.h>

#pragma comment(lib, "uxtheme.lib")
#pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")

LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL MainWindow_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct);
void MainWindow_OnDestroy(HWND hWnd);
void MainWindow_OnSize(HWND hWnd, UINT state, int cx, int cy);
void MainWindow_OnPaint(HWND hWnd);
LRESULT MainWindow_OnNotify(HWND hWnd, int idFrom, LPNMHDR pnmhdr);

#define ID_LISTVIEW 100

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
  WNDCLASSEX wcex = { 0 };
  HWND hWnd;
  BOOL ret;
  MSG msg;

  wcex.cbSize = sizeof(wcex);
  wcex.lpfnWndProc = WindowProc;
  wcex.hInstance = hInstance;
  wcex.hIcon = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
  wcex.hCursor = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
  wcex.lpszClassName = TEXT("MainWindow");

  if (!RegisterClassEx(&wcex))
  {
    return 1;
  }

  hWnd = CreateWindow(wcex.lpszClassName, TEXT("Win32Test"), WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_DESKTOP, NULL, hInstance, NULL);
  if (!hWnd)
  {
    return 1;
  }

  ShowWindow(hWnd, nCmdShow);
  UpdateWindow(hWnd);

  while ((ret = GetMessage(&msg, NULL, 0, 0)) != 0)
  {
    if (ret == -1)
    {
      return 1;
    }
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  return msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  switch (uMsg)
  {
    HANDLE_MSG(hWnd, WM_CREATE, MainWindow_OnCreate);
    HANDLE_MSG(hWnd, WM_DESTROY, MainWindow_OnDestroy);
    HANDLE_MSG(hWnd, WM_SIZE, MainWindow_OnSize);
    HANDLE_MSG(hWnd, WM_PAINT, MainWindow_OnPaint);
    HANDLE_MSG(hWnd, WM_NOTIFY, MainWindow_OnNotify);
  default:
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
  }
}

BOOL MainWindow_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct)
{
  HWND h_listview = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, NULL, WS_CHILD | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hWnd, (HMENU)ID_LISTVIEW, GetModuleHandle(NULL), NULL);
  LVCOLUMN lvc = { 0 };

  SetWindowTheme(h_listview, TEXT("Explorer"), NULL);
  ListView_SetExtendedListViewStyle(h_listview, LVS_EX_DOUBLEBUFFER);
  ListView_SetView(h_listview, LV_VIEW_DETAILS);
  lvc.mask = LVCF_TEXT;
  lvc.pszText = TEXT("Column 1");
  ListView_InsertColumn(h_listview, 0, &lvc);
  ListView_SetColumnWidth(h_listview, 0, LVSCW_AUTOSIZE_USEHEADER);

  return TRUE;
}

void MainWindow_OnDestroy(HWND hWnd)
{
  PostQuitMessage(0);
}

void MainWindow_OnSize(HWND hWnd, UINT state, int cx, int cy)
{
  MoveWindow(GetDlgItem(hWnd, ID_LISTVIEW), 11, 11, cx - 22, cy - 22, TRUE);
}

void MainWindow_OnPaint(HWND hWnd)
{
  PAINTSTRUCT ps;

  BeginPaint(hWnd, &ps);

  FillRect(ps.hdc, &ps.rcPaint, GetSysColorBrush(COLOR_3DFACE));

  EndPaint(hWnd, &ps);
}

LRESULT MainWindow_OnNotify(HWND hWnd, int idFrom, LPNMHDR pnmhdr)
{
  switch (pnmhdr->code)
  {
  case LVN_GETEMPTYMARKUP:
    {
      if (pnmhdr->hwndFrom == GetDlgItem(hWnd, ID_LISTVIEW))
      {
        NMLVEMPTYMARKUP *pmarkup = (NMLVEMPTYMARKUP *)pnmhdr;
        _tcscpy(pmarkup->szMarkup, TEXT("This is an empty ListView."));
        return TRUE;
      }
      return FORWARD_WM_NOTIFY(hWnd, idFrom, pnmhdr, DefWindowProc);
    }
  default:
    return FORWARD_WM_NOTIFY(hWnd, idFrom, pnmhdr, DefWindowProc);
  }
}
4

1 回答 1

0

我可以移动

    listView1.SetWindowTheme("Explorer", null);
    listView1.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);

intoFormLoad事件。这有效,但我不知道为什么。

我也可以覆盖OnHandleCreated,并将这两行移到OnHandleCreated. 我也不知道为什么。

于 2012-09-06T10:52:49.580 回答