【问题标题】:C# .NET Chart - Adding a legend scrollbar and checkboxesC# .NET 图表 - 添加图例滚动条和复选框
【发布时间】:2014-07-30 19:44:06
【问题描述】:

我正在使用 Visual Studio 2013 中的 Chart 类来可视化我的一些数据。但是,我的数据很快产生了许多系列,将它们全部放在一个图表中非常重要。我将图例区域限制为整个图表区域的 20%,因此当我将图表拉伸到最大尺寸时,我几乎不能显示超过 7-8 个图例项。控件在图例项的空间用完后才放 ...。

不只是写...,是否有可能在图例中添加滚动条并能够查看所有项目?我知道我可以以某种方式实现我自己的图例,但我想充分利用 Chart 类所提供的功能。我还想在每个图例项旁边添加复选框,以指示该系列是否应隐藏在图表上。如果没有我自己的图例实现,这可能吗?

此外,我还希望右键单击带有几个选项的图例项展开菜单,但这完全是可选的。滚动条和复选框现在是我的主要问题。

谢谢。

【问题讨论】:

  • 我怀疑您的任何愿望都会在开箱后实现。要扩展图例,请考虑切换您设置的 20% 限制。但是,如果你无论如何都要写一个复选框,它可以完全取代图例,也可以完成菜单的任务..

标签: c# .net visual-studio charts


【解决方案1】:

总体思路:您必须创建两个图表。一个是主要的,第二个仅用于传说。如果系列顺序相同,您将拥有相同的系列样式。

用于在图例项上右键单击显示弹出窗口:

将 ContextMenu(工具箱中的 ContextMenuStrip 类)连接到图例的图表。

用于显示隐藏系列:

您必须实现MouseClick 事件处理程序并使用数学检查鼠标光标下的对象(GetChildAtPoint() 方法不适用于图例项)。方程式:是series_index = control_relative_mouse_y / c_legendItemHeight,其中c_legendItemHeight 是您提供的用于计算控件高度(单个图例项的高度)的值。 您必须将图例图表配置为包含 LegendStyleRowMaximumAutoSize100DockingLeftIsTextAutoFitfalseIsEquallySpacedItems 到 @987654335。 您在图例中定义了 3 列(一列用于系列样式,第二列用于复选框,第三列用于系列名称)。使用系列CustomProperties 保持可见性状态。在检查列中使用此自定义属性 (Text = "#CUSTOMPROPERTY(...)") 来显示检查状态。图表不支持自动调整大小。您可以手动完成。在系列加载期间,将图表高度设置为计算值。该值等于_stock.Shares.Count * c_legendItemHeight + 9。其中:_stock.Shares.Count 是图例中的项目数,c_legendItemHeight 项目的恒定高度(整数值,数字大于 18 似乎对我有用),9(似乎是恒定的)。我知道这不好,但我找不到更好的解决方案。我在示例中添加了 502 系列,效果很好。确保图表中没有任何边距,否则您将无法正确计算序列号。

对于“传说中的许多系列”问题:

将您的图例图表放入打开 AutoScroll 属性的面板中。使用上述描述中的表达式设置面板和图例高度。

源代码:

    public partial class Form1 : Form
    {
        private const int c_legendItemHeight = 20;
        private const string c_checkCustomPropertyName = "CHECK";
        private const string c_checkedString = "✔"; // see http://www.edlazorvfx.com/ysu/html/ascii.html for more
        private const string c_uncheckedString = "✘";
        private Stock _stock;
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            _stock = Stock.Load();

            // mainChart
            mainChart.Legends.Clear();
            foreach (Share share in _stock.Shares)
            {
                Series series = mainChart.Series.Add(share.Name);
                series.ChartType = SeriesChartType.Line;
                foreach (ShareQuotation shareQuotation in share.Quotations)
                {
                    series.Points.AddXY(shareQuotation.Date.ToString(), shareQuotation.Close);
                }
            }

            // LegendChart
            Legend legend = legendChart.Legends[0];
            legendChart.Series.Clear();
            legend.IsTextAutoFit = false;
            legend.IsEquallySpacedItems = true;
            legend.MaximumAutoSize = 100;
            legend.Docking = Docking.Left;
            legend.LegendStyle = LegendStyle.Column;
            legend.Position.Auto = true;
            legend.Position.Width = 100;
            legend.Position.Height = 100;
            legend.CellColumns[1].Text = "#CUSTOMPROPERTY(" +c_checkCustomPropertyName+ ")";

            foreach (Share share in _stock.Shares)
            {
                Series series = legendChart.Series.Add(share.Name);
                series.SetCustomProperty(c_checkCustomPropertyName,c_checkedString);
            }
            legendChart.Height = _stock.Shares.Count * c_legendItemHeight + 9; // 9 - seems to be constant value
            legendPanel.Height = legendChart.Height;

        }



        private void legendChart_MouseClick(object sender, MouseEventArgs e)
        {
            Point mousePosition = legendChart.PointToClient(Control.MousePosition);
            int seriesNo = mousePosition.Y / c_legendItemHeight;
            Series series = legendChart.Series[seriesNo]; // TODO - check if not out of range 

            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                // check uncheck series
                if (series.GetCustomProperty(c_checkCustomPropertyName) == c_checkedString)
                {
                    // if checked
                    // uncheck
                    series.SetCustomProperty(c_checkCustomPropertyName, c_uncheckedString);
                    series.CustomProperties = series.CustomProperties; // workaround - trigger change - is this a bug?
                    // hide in mainChart
                    mainChart.Series[seriesNo].Enabled = false;
                }
                else
                {
                    // if unchecked
                    legendChart.Series[seriesNo].SetCustomProperty(c_checkCustomPropertyName, c_checkedString);
                    series.CustomProperties = series.CustomProperties; // workaround - trigger change - is this a bug?
                    // show in mainChart
                    mainChart.Series[seriesNo].Enabled = true;
                }
            }
        }

        private void contextMenu_Opening(object sender, CancelEventArgs e)
        {
            Point mousePosition = legendChart.PointToClient(Control.MousePosition);
            int seriesNo = mousePosition.Y / c_legendItemHeight;
            Series series = legendChart.Series[seriesNo]; // TODO - check if not out of range 

            contextMenu.Items.Clear();
            string state = series.GetCustomProperty(c_checkCustomPropertyName) == c_checkedString ? "visible" : "hidden";
            contextMenu.Items.Add("&Some strange action for " + state + " item named " + series.Name);
            contextMenu.Items.Add("&Another action ...");
        }
    }

结果应如下所示:

【讨论】:

  • 最后,我只是隐藏了图例,否则保持它处于活动状态,并使用它的项目来生成我的自定义图例。我认为这是在我的情况下最轻松的解决方案(除非有人知道开箱即用的解决方案)。
  • 也许您可以只创建带有图例的第二个图表并将滚动条附加到它并在第一个图表中禁用图例。我认为您应该在两个图表控件中获得相同的系列样式。
  • 我们可能正在做点什么!通常如何将滚动条附加到图表?
  • 其实听起来挺不错的,但问题是我觉得没有办法让图表自动垂直展开。换句话说,图表将遵循您指定的维度。我也想过加个滚动面板,但是不知道怎么让图表垂直展开。
  • 图表不支持自动调整大小。您可以手动完成。将图表的 Height 属性更改为计算值。请务必关闭 Legend 的 IsTextAutoFit 属性,这样您的图例项目大小将保持不变(如果您不在系列名称中使用文字扭曲)。
猜你喜欢
  • 2019-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-17
  • 1970-01-01
相关资源
最近更新 更多