Тема Windows, влияющая на заголовок ListView

Я создал новое приложение Windows Forms (С#) с одной простой формой, содержащей ListView. Затем я изменил Просмотр свойства" в "Детали" и увеличил размер шрифта, используемого в этом ListView, и вот результат:

Вот как это выглядит в Windows XP с темой Windows Classic:
enter image description here

и вот результат с темой Windows XP:
enter image description here

Я могу предотвратить появление на моем приложении визуальных стилей путем удаления Application.EnableVisualStyles() вызова или путем изменения Application.VisualStyleState: enter image description here
Хотя это изменение приводит к тому, что ListView имеет желаемый внешний вид, он также влияет на внешний вид других элементов управления. Я бы хотел, чтобы мой ListView был единственным элементом управления, на который не влияют стили Visual.

Я также нашел похожие вопросы, которые пытаются справиться с этим:
Можете ли вы отключить визуальные стили/темы для всего одного элемента управления Windows?
Как отключить визуальные стили только для одного элемента управления, а не для его детей?

К сожалению, ни одно из упомянутых решений не работает. Похоже, что сам заголовок будет состоять из некоторых элементов управления, на которые влияют визуальные стили, даже если визуальные стили для элемента управления ListView отключены.

Любое решение С#, которое препятствовало бы визуальным стилям влиять на внешний вид заголовка ListView, было бы оценено.

Ответ 1

После изнурительных исследований я узнал об этом. Дело в том, что когда вы вызываете

SetWindowTheme(this.Handle, "", "");

в пределах пользовательского класса ListView, он не позволяет визуальным стилям влиять на появление элемента управления ListView, но не заголовок ListView (SysHeader32), который является дочерним окном ListView. Поэтому при вызове функции SetWindowTheme нам нужно предоставить дескриптор окна заголовка вместо дескриптора ListView:

[DllImportAttribute("uxtheme.dll")]
private static extern int SetWindowTheme(IntPtr hWnd, string appname, string idlist);

[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);

// Callback method to be used when enumerating windows:
private static bool EnumWindow(IntPtr handle, IntPtr pointer)
{
    GCHandle gch = GCHandle.FromIntPtr(pointer);
    List<IntPtr> list = gch.Target as List<IntPtr>;
    if (list == null)
        throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
    list.Add(handle);
    return true;
}

// delegate for the EnumChildWindows method:
private delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);

// get first child:
private static void DisableVisualStylesForFirstChild(IntPtr parent)
{
    List<IntPtr> children = new List<IntPtr>();
    GCHandle listHandle = GCHandle.Alloc(children);
    try
    {
        EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
        EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
        if (children.Count > 0)
            SetWindowTheme(children[0], "", "");
    }
    finally
    {
        if (listHandle.IsAllocated)
            listHandle.Free();
    }
}

protected override void OnHandleCreated(EventArgs e)
{
    DisableVisualStylesForFirstChild(this.Handle);
    base.OnHandleCreated(e);
}

Ответ 2

Похоже, что это известная ошибка, для которой нет простого решения. Согласно ответу:

После большого количества исследований с этой проблемой мы обнаружили, что это ошибка ОС Windows, которую элемент управления ComCtl6.0 забывает применять информацию о шрифте на чертеж DC.

Однако вы могли бы нарисовать заголовок столбца самостоятельно. Подробнее о том, как это сделать, см. в этой статье в MSDN, а также посмотрите listView1_DrawColumnHeader.

Ответ 3

Как отключить визуальные стили?

с помощью этого кода вы можете отключить стиль для одного элемента управления (просто используйте ListViewConstrol вместо ListView):

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

public class ListViewControl : ListView {
  [DllImportAttribute("uxtheme.dll")]
  private static extern int SetWindowTheme(IntPtr hWnd, string appname, string idlist);

  protected override void OnHandleCreated(EventArgs e) {
    SetWindowTheme(this.Handle, "", "");
    base.OnHandleCreated(e);
  }
}

Ответ 4

Как Botz3000 упоминал в своем ответе, что это хорошо известная проблема с ListView в Windows XP. Другим обходным решением является регистрация события ListView.DrawColumnHeader и reset шрифта заголовка в нем. Вы должны установить для свойства ListView.OwnerDraw значение true. Код MSDN выглядит следующим образом:

// Draws column headers.
private void listView1_DrawColumnHeader(object sender,
    DrawListViewColumnHeaderEventArgs e)
{
    using (StringFormat sf = new StringFormat())
    {
        // Store the column text alignment, letting it default
        // to Left if it has not been set to Center or Right.
        switch (e.Header.TextAlign)
        {
            case HorizontalAlignment.Center:
                sf.Alignment = StringAlignment.Center;
                break;
            case HorizontalAlignment.Right:
                sf.Alignment = StringAlignment.Far;
                break;
        }

        // Draw the standard header background.
        e.DrawBackground();

        // Draw the header text.
        using (Font headerFont =
                    new Font("Helvetica", 10, FontStyle.Bold))
        {
            e.Graphics.DrawString(e.Header.Text, headerFont,
                Brushes.Black, e.Bounds, sf);
        }
    }
    return;
}

В этом случае шрифт заголовка всегда будет "Helvetica", 10, FontStyle.Bold и не будет выполняться шрифтом listview. Проверьте MSDN для получения более подробной информации.