C # переменная высота для узлов в TreeView - PullRequest
3 голосов
/ 02 октября 2009

Несмотря на мои усилия в Google, я не смог найти решение использовать дерево по умолчанию .NET и иметь переменную высоту для каждого узла в этом дереве.

Мне нужен способ иметь 2 типа узлов с разной высотой.

В идеале мне бы хотелось, чтобы один тип узла также увеличивался при наведении курсора мыши.

Есть какой-нибудь умный парень? :)

Ответы [ 4 ]

3 голосов
/ 02 октября 2009

Я не нашел ответа на ваш вопрос. @Frank прав в том, что это невозможно в WinForms.

Тем не менее, рекомендации Microsoft по проектированию предлагают некоторые альтернативы TreeViews ЕСЛИ ваша иерархия имеет только два уровня.

http://msdn.microsoft.com/en-us/library/aa511496.aspx

2 голосов
/ 26 марта 2016

Я понимаю, что это довольно старо ... но я нашел кое-что интересное вчера. Эта ветка содержит ответ Криса Форбса, который предполагает, что действительно возможно иметь элементы переменной высоты, если древовидное представление имеет стиль TVS_NONEVENHEIGHT . Я поэкспериментировал с этой идеей и фрагментом кода github , на который он ссылался, и обнаружил, что это действительно работает, но не обеспечивает 100% гибкости (см. Список ограничений ниже). Я также не уверен, что было бы целесообразно изменить высоту элемента при наведении мыши.

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

Ограничения и предостережения:

  • Требуется много работы, так как узел должен быть полностью нарисован владельцем. Этот пример кода не является полностью функциональным в этом отношении.
  • Вы можете установить высоту элемента только кратно свойству ItemHeight элемента управления (на самом деле вы фактически устанавливаете для него нужный коэффициент 1, 2, 3, ...). Я экспериментировал с установкой свойства ItemHeight элемента управления на 1, а затем установил высоту узла равной желаемой высоте пикселя. Кажется, это работает, но если вы добавляете элементы во время разработки, это просто приводит к странным и искаженным результатам в конструкторе. Я не проверял это полностью, хотя.
  • Невозможно установить высоту, если узел не был добавлен в коллекцию TreeNodeCollection, так как дескриптор TreeNode не будет создан.
  • Вы не можете изменять высоту элемента во время разработки.
  • Я не часто использую pinvoke, поэтому некоторые из этих определений могут потребовать некоторой работы. Например, исходное определение TVITEMEX содержит несколько условных записей, которые я не знал, как скопировать.

Вот фрагмент кода из 247 строк, демонстрирующий это, просто замените Program.cs приложения Windows Forms этим кодом.

Это все еще требует большой работы, так как код OwnerDraw ничего не делает в отношении рисования значков, линий деревьев, флажков и тому подобного, но я подумал, что это была довольно удивительная находка, достойная публикации здесь.

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

namespace TreeTest
{
  static class Program
  {
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);
      Application.Run(new TreeForm());
    }
  }

  public static class NativeExtensions
  {
    public const int TVS_NONEVENHEIGHT = 0x4000;

    [DllImport("user32")]
    //private static extern IntPtr SendMessage(IntPtr hwnd, uint msg, IntPtr wp, IntPtr lp);
    private static extern IntPtr SendMessage(IntPtr hwnd, uint msg, IntPtr wp, ref TVITEMEX lp);

    private const int TVM_GETITEM = 0x1100 + 62;
    private const int TVM_SETITEM = 0x1100 + 63;

    [StructLayout(LayoutKind.Sequential)]
    private struct TVITEMEX
    {
      public uint mask;
      public IntPtr hItem;
      public uint state;
      public uint stateMask;
      public IntPtr pszText;
      public int cchTextMax;
      public int iImage;
      public int iSelectedImage;
      public int cChildren;
      public IntPtr lParam;
      public int iIntegral;
      public uint uStateEx;
      public IntPtr hwnd;
      public int iExpandedImage;
      public int iReserved;
    }

    [Flags]
    private enum Mask : uint
    {
      Text = 1,
      Image = 2,
      Param = 4,
      State = 8,
      Handle = 16,
      SelectedImage = 32,
      Children = 64,
      Integral = 128,
    }

    /// <summary>
    /// Get a node's height. Will throw an error if the Node has not yet been added to a TreeView,
    /// as it's handle will not exist.
    /// </summary>
    /// <param name="tn">TreeNode to work with</param>
    /// <returns>Height in multiples of ItemHeight</returns>
    public static int GetHeight(this TreeNode tn)
    {
      TVITEMEX tvix = new TVITEMEX
      {
        mask = (uint)(Mask.Handle | Mask.Integral),
        hItem = tn.Handle,
        iIntegral = 0
      };
      SendMessage(tn.TreeView.Handle, TVM_GETITEM, IntPtr.Zero, ref tvix);
      return tvix.iIntegral;
    }

    /// <summary>
    /// Set a node's height. Will throw an error if the Node has not yet been added to a TreeView,
    /// as it's handle will not exist.
    /// </summary>
    /// <param name="tn">TreeNode to work with</param>
    /// <param name="height">Height in multiples of ItemHeight</param>
    public static void SetHeight(this TreeNode tn, int height)
    {
      TVITEMEX tvix = new TVITEMEX
      {
        mask = (uint)(Mask.Handle | Mask.Integral),
        hItem = tn.Handle,
        iIntegral = height
      };
      SendMessage(tn.TreeView.Handle, TVM_SETITEM, IntPtr.Zero, ref tvix);
    }
  }

  public class TreeViewTest : TreeView
  {
    public TreeViewTest()
    {
      // Do DoubleBuffered painting
      SetStyle(ControlStyles.AllPaintingInWmPaint, true);
      SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

      // Set value for owner drawing ...
      DrawMode = TreeViewDrawMode.OwnerDrawAll;
    }

    /// <summary>
    /// For TreeNodes to support variable heights, we need to apply the
    /// TVS_NONEVENHEIGHT style to the control.
    /// </summary>
    protected override CreateParams CreateParams
    {
      get
      {
        var cp = base.CreateParams;
        cp.Style |= NativeExtensions.TVS_NONEVENHEIGHT;
        return cp;
      }
    }

    /// <summary>
    /// Do not tempt anyone to change the DrawMode property, be it via code or via
    /// Property grid. It's still possible via code though ...
    /// </summary>
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
      EditorBrowsable(EditorBrowsableState.Never)]
    public new TreeViewDrawMode DrawMode
    {
      get { return base.DrawMode; }
      set { base.DrawMode = value; }
    }

    /// <summary>
    /// OwnerDraw code. Still needs a lot of work, no tree lines, symbols, checkboxes etc. are drawn
    /// yet, just the plain item text and background ...
    /// </summary>
    /// <param name="e"></param>
    protected override void OnDrawNode(DrawTreeNodeEventArgs e)
    {
      e.DrawDefault = false;

      // Draw window colour background
      e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds);

      // Draw selected item background
      if (e.Node.IsSelected)
        e.Graphics.FillRectangle(SystemBrushes.Highlight, e.Node.Bounds);

      // Draw item text
      TextRenderer.DrawText(e.Graphics, e.Node.Text, Font, e.Node.Bounds,
        e.Node.IsSelected ? SystemColors.HighlightText : SystemColors.WindowText,
        Color.Transparent, TextFormatFlags.Top | TextFormatFlags.NoClipping);

      // Draw focus rectangle
      if (Focused && e.Node.IsSelected)
        ControlPaint.DrawFocusRectangle(e.Graphics, e.Node.Bounds);

      base.OnDrawNode(e);
    }

    /// <summary>
    /// Without this piece of code, for some reason, drawing of items that get selected/unselected
    /// is deferred until MouseUp is received.
    /// </summary>
    /// <param name="e"></param>
    protected override void OnMouseDown(MouseEventArgs e)
    {
      base.OnMouseDown(e);
      TreeNode clickedNode = GetNodeAt(e.X, e.Y);
      if (clickedNode.Bounds.Contains(e.X, e.Y))
      {
        SelectedNode = clickedNode;
      }
    }
  }

  public class TreeForm : Form
  {
    public TreeForm() { InitializeComponent(); }

    private System.ComponentModel.IContainer components = null;

    protected override void Dispose(bool disposing)
    {
      if (disposing && (components != null))
      {
        components.Dispose();
      }
      base.Dispose(disposing);
    }

    private void InitializeComponent()
    {
      this.treeViewTest1 = new TreeTest.TreeViewTest();
      this.SuspendLayout();
      // 
      // treeViewTest1
      // 
      this.treeViewTest1.Dock = System.Windows.Forms.DockStyle.Fill;
      this.treeViewTest1.Location = new System.Drawing.Point(0, 0);
      this.treeViewTest1.Name = "treeViewTest1";
      this.treeViewTest1.Size = new System.Drawing.Size(284, 262);
      this.treeViewTest1.TabIndex = 0;
      // 
      // Form2
      // 
      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
      this.ClientSize = new System.Drawing.Size(284, 262);
      this.Controls.Add(this.treeViewTest1);
      this.Name = "Form2";
      this.Text = "Form2";
      this.ResumeLayout(false);
    }

    private TreeViewTest treeViewTest1;

    protected override void OnLoad(EventArgs e)
    {
      base.OnLoad(e);

      AddNodes(treeViewTest1.Nodes, 0, new Random());
    }

    private void AddNodes(TreeNodeCollection nodes, int depth, Random r)
    {
      if (depth > 2) return;

      for (int i = 0; i < 3; i++)
      {
        int height = r.Next(1, 4);
        TreeNode tn = new TreeNode { Text = $"Node {i + 1} at depth {depth} with height {height}" };
        nodes.Add(tn);
        tn.SetHeight(height);
        AddNodes(tn.Nodes, depth + 1, r);
      }
    }
  }
}
1 голос
/ 02 октября 2009

Это невозможно ни для System.Windows.Forms.TreeView, ни для какой-либо другой сторонней замены, о которой я знаю. Возможности стандартного древовидного представления соответствуют тем, что показаны в представлении папки Windows Explorer.

Может быть, вы хотите дать нам больше информации о вашей задаче или цели. Это позволило бы нам указать на некоторые другие альтернативы.

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

0 голосов
/ 28 февраля 2013

Если вы используете значок, самый простой способ - установить размер значка ... по крайней мере, это работало в компактной структуре, я установил высоту изображения равной высоте, которую хотел получить объект. Вы могли бы просто иметь значок, который соответствует вашему фону, если вы не хотите значок.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...