影响 ListView 标头的 Windows 主题

Posted

技术标签:

【中文标题】影响 ListView 标头的 Windows 主题【英文标题】:Windows theme affecting ListView header 【发布时间】:2012-04-23 14:53:34 【问题描述】:

我用一个包含 ListView 的简单表单创建了新的 Windows 窗体应用程序 (C#)。然后我将 View Property 更改为 Details 并增加了此 ListView 中使用的字体大小,结果如下:

这是在带有 Windows Classic 主题的 Windows XP 上的外观:

这是 Windows XP 主题的结果:

我可以通过删除 Application.EnableVisualStyles() 调用或更改 Application.VisualStyleState: 尽管此更改使 ListView 具有所需的外观,但它也会影响其他控件的外观。我希望我的 ListView 成为唯一不受视觉样式影响的控件

我也发现了类似的问题试图解决它:Can you turn off visual styles/theming for just a single windows control?How do I disable visual styles for just one control, and not its children?

很遗憾,上述解决方案均无效。 看起来标题本身将由一些受视觉样式影响的控件组成,即使 ListView 控件的视觉样式被禁用。

任何可以防止视觉样式影响 ListView 标头外观的 C# 解决方案都将受到赞赏。

【问题讨论】:

【参考方案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);

【讨论】:

由于它基于 Kamil Lach 建议的解决方案,因此我将用我提供的赏金奖励他的答案。【参考方案2】:

这里是如何使 ListView 标题具有您想要的高度:

C# - Listview colum header height (Windows Form)

【讨论】:

【参考方案3】:

正如Botz3000 在他的回答中提到的那样,在 Windows XP 中,ListView 是一个众所周知的问题。另一种解决方法是注册ListView.DrawColumnHeader 事件并重置其中的标题字体。您必须将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,并且不受列表视图字体的影响。 Check MSDN 了解更多详情。

【讨论】:

不行,OwnerDraw 不是办法,不能用它来影响 ListView header 的高度。 @LihO 它对标题的高度没有任何作用,因为它在 XP 中是不可能的。请仔细阅读我的回答,我们的想法是让标题文本更小,以便它适合标题。 但我的问题是如何“使 ListView 标题具有正确的高度”,而不是如何使标题文本更小... 我发现了为什么 SetWindowTheme 以前不起作用。它现在有效,请查看我的答案;)【参考方案4】:

看起来这是一个known bug,没有简单的解决方法。根据那里的答案:

在对这个问题进行了大量研究后,我们发现这是一个 Windows 操作系统错误,ComCtl6.0 标头控件忘记应用该错误 字体信息到绘图 DC。

但是您可以自己绘制列标题。有关如何操作的详细信息,请参阅this article on MSDN,另请参阅listView1_DrawColumnHeader

【讨论】:

自己绘制根本没有帮助,因为您只能更改正在绘制的内容,而不是标题的大小。【参考方案5】:

禁用视觉样式怎么样?

使用此代码,您可以禁用一个控件的样式(只需使用 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);
  

【讨论】:

+1 指出视觉样式导致了这种变化,尽管这个解决方案根本不起作用。 我发现了为什么 SetWindowTheme 以前不起作用。它现在有效,请查看我的答案;) 恭喜卡米尔获得赏金 :)

以上是关于影响 ListView 标头的 Windows 主题的主要内容,如果未能解决你的问题,请参考以下文章

listView.setOnItemClickListener 标头项禁用

Android 添加动态 ListView 标头

来自 QAbstractListmodel headerData() 的 QML Listview 标头

用于 Xamarin.Forms 的带有标头 (LongListSelector) 的 ListView

ListView控件使用简介

如何用VB6.0制作Windows资源管理器