【问题标题】:Windows theme affecting ListView header影响 ListView 标头的 Windows 主题
【发布时间】: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# 解决方案都将受到赞赏。

【问题讨论】:

    标签: c# winforms visual-c++ listview windows-xp


    【解决方案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 以前不起作用。它现在有效,请查看我的答案;)
      • 恭喜卡米尔获得赏金 :)
      猜你喜欢
      • 2020-06-03
      • 2012-09-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-07
      • 1970-01-01
      相关资源
      最近更新 更多