퇴근5분전

# 4) .Net ( Vs 2010 )/C# +41

ChartFX Version : 7.0.3754.18555


ChartFX로 챠트가 지정되었고 이를 좀더 쉽게 데이타를 맵핑하기 위해 확장 클래스를 만들었음.

Vs2010으로 작업하니 편한점이 많다.

우선 옛날 VB를 번역하면서 보던 파라미터에 기본값을 설정하는 부분이 맘에 든다.

자바스크립트 하는 맛?

CalcEngine을 만들때 사용했던 dynamic객체와 또다른 매력이랄까? 

사용 순서는 DataTable객체를 넘겨주면서 시리즈의 데이타를 구해서
ChartEX.SeriesData 로 변환하여 갯수만큼 List화 한다.
이때 Y2 로 사용될 시리즈가 존재 시
IsAxisY2 값을 true로 주면 된다.
MinMax값을 계산해서 넣어줘야 하니 적절한 LINQ문을 사용한다.

2010 10 15 수정
 내용 : SeriesDataList  추가
        시리즈 관리 객체를 만들어서 Y축관련 스텝, 최소값 최대값을 자동 계산해주도록 추가해주었음.

//챠트 사용 소스

  ChartEX.SeriesDataList datas = new ChartEX.SeriesDataList();
            EnumerableRowCollection<double> BM_DURATION = dt.AsEnumerable().Select(c => Convert.ToDouble(c.Field<object>("BM_DURATION")) * cboDurationValue ); // Secondary AxisY
            datas.Add(new ChartEX.SeriesData()
            {
                Name = "벤치마크",
                Values = BM_DURATION.ToArray(),
                Color = ChartEX.DefaultColors[datas.Count],
                Gallery = Gallery.Lines,
                IsAxisY2 = false
            });

            EnumerableRowCollection<double> PF_DURATION = dt.AsEnumerable().Select(c => Convert.ToDouble(c.Field<object>("PF_DURATION")) * cboDurationValue); // Secondary AxisY
            datas.Add(new ChartEX.SeriesData
            {
                Name = "채권",
                Values = PF_DURATION.ToArray(),
                Color = ChartEX.DefaultColors[datas.Count],
                Gallery = Gallery.Lines,
                IsAxisY2 = false
            });

            EnumerableRowCollection<double> RF_RATE = dt.AsEnumerable().Select(c => Convert.ToDouble(c.Field<object>("RF_RATE")) ); // Secondary AxisY
            datas.Add(new ChartEX.SeriesData
            {
                Name = "시장이자율",
                Values = RF_RATE.ToArray(),
                Color = ChartEX.DefaultColors[datas.Count],
                Gallery = Gallery.Lines,
                IsAxisY2 = true
            });

            datas.ClacMaxMin();

            //챠트 셋팅
            chart.ChartSetting(
                    dt.AsEnumerable().Select(c => c.Field<string>("WORKDAY")).ToArray(),
                    datas.ToArray(),
                //null,
                    Legend =>
                    {
                        Legend.Dock = DockArea.Top;
                    },
                //null,
                    (X, Y) =>
                    {
                        X.AutoScale = true;
                        X.Step = 3;
                        X.FirstLabel = 0;
                        X.LabelsFormat.Format = AxisFormat.Date;
                        X.LabelsFormat.CustomFormat = "yyyy-MM-dd";
                        X.Step = 0;

                        Y.AutoScale = false;
                        Y.ForceZero = false;
                        Y.LabelsFormat.Format = AxisFormat.Number;
                        Y.LabelsFormat.CustomFormat = "N2";

                        Y.Max = datas.YMax;
                        Y.Min = datas.YMin;
                        Y.Step = datas.YStep; //0=Auto
                        Y.Title.Text = "벤치마크/채권";
                    },
                // null
                    AxisY2 =>
                    {
                        AxisY2.AutoScale = false;
                        AxisY2.ForceZero = false;
                        AxisY2.Max = datas.Y2Max ;
                        AxisY2.Min = datas.Y2Min;
                        AxisY2.Step = datas.Y2Step;
                        AxisY2.LabelsFormat.CustomFormat = "N2";
                        AxisY2.Title.Text = "시장이자율";
                    }
            );
            datas.Clear();
            datas = null;
            #endregion


// 추가된 챠트 소스
        public class SeriesDataList : IList<SeriesData>
        {
            /// <summary>
            /// Y좌표축 표시 갯수 ( 10d )
            /// </summary>
            public readonly double STEP_INCREEMENT = 10d;
            /// <summary>
            /// Min, Max값 계산시 여백 계산 값. ( 0.3d )
            /// </summary>
            public readonly double STEP_SEED = 0.2d;

            /// <summary>
            /// 시리즈 데이타 리스트
            /// </summary>
            private List<SeriesData> lst = null;
            public SeriesDataList()
            {
                lst = new List<SeriesData>();
                YMax1 = double.MinValue;
                YMin1 = double.MaxValue;

                YMax2 = double.MinValue;
                YMin2 = double.MaxValue;
            }

            /// <summary>
            /// Y축 Min, Max를 계산해줌. 셋팅전에 반드시 호출해주어야 함.
            /// </summary>
            public void ClacMaxMin()
            {
                YMax1 = double.MinValue;
                YMin1 = double.MaxValue;

                YMax2 = double.MinValue;
                YMin2 = double.MaxValue;

                foreach (SeriesData item in lst)
                {
                    if (item.IsAxisY2 == true)
                    {
                        if (YMax2 < item.Values.Max())
                            YMax2 = item.Values.Max();

                        if (YMin2 > item.Values.Min())
                            YMin2 = item.Values.Min();
                    }
                    else
                    {
                        if (YMax1 < item.Values.Max())
                            YMax1 = item.Values.Max();

                        if (YMin1 > item.Values.Min())
                            YMin1 = item.Values.Min();
                    }
                }
            }

            /// <summary>
            /// Y 축 Max
            /// </summary>
            private double YMax1 {get; set;}
            /// <summary>
            /// Y 축 Min
            /// </summary>
            private double YMin1 {get; set;}
            /// <summary>
            /// Y2 축 Max
            /// </summary>
            private double YMax2 { get; set; }
            /// <summary>
            /// Y2 축 Min
            /// </summary>
            private double YMin2 { get; set; }
            /// <summary>
            /// Y축 Max값
            /// </summary>
            public double YMax { get { return YMax1 + Math.Abs(YMax1) * STEP_SEED; } }
            /// <summary>
            /// Y축 Min값
            /// </summary>
            public double YMin { get { return YMin1 - Math.Abs(YMin1) * STEP_SEED; } }

            /// <summary>
            /// Y2 축 Max값
            /// </summary>
            public double Y2Max { get { return YMax2 + Math.Abs(YMax2) * STEP_SEED; } }
            /// <summary>
            /// Y2 축 Min값
            /// </summary>
            public double Y2Min { get { return YMin2 - Math.Abs(YMin2) * STEP_SEED; } }

            /// <summary>
            ///  Y축 스탭 계산해서 나옴.
            /// </summary>
            public double YStep
            {
                get { return Math.Abs((YMax1 - Math.Abs(YMin1)) / STEP_INCREEMENT); }
            }
            /// <summary>
            /// Y2축 스탭 계산해서 나옴.
            /// </summary>
            public double Y2Step {
                get { return Math.Abs((YMax2 - Math.Abs(YMin2)) / STEP_INCREEMENT); ; }
            }

            #region   IList 구현
            public int IndexOf(SeriesData item)
            {
                return lst.IndexOf(item);
            }

            void IList<SeriesData>.Insert(int index, SeriesData item)
            {
                lst.Insert(index, item);
            }

            void IList<SeriesData>.RemoveAt(int index)
            {
                lst.RemoveAt(index);
            }

            SeriesData IList<SeriesData>.this[int index]
            {
                get
                {
                    return lst[index];
                }
                set
                {
                    lst[index] = value;
                }
            }

            public void Clear()
            {
                lst.Clear();
            }

            public bool Contains(SeriesData item)
            {
                return lst.Contains(item);
            }

            public void CopyTo(SeriesData[] array, int arrayIndex)
            {
                lst.CopyTo(array, arrayIndex);
            }

            public int Count
            {
                get { return lst.Count; }
            }

            public bool IsReadOnly
            {
                get { return true; }
            }

            public bool Remove(SeriesData item)
            {
                return lst.Remove(item);
            }

            public IEnumerator<SeriesData> GetEnumerator()
            {
                return lst.GetEnumerator();
            }

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return lst.GetEnumerator();
            }

            public void Add(SeriesData item)
            {
                lst.Add(item);
            }
            #endregion
        }

        /// <summary>
        /// 챠트 데이타 셋팅&lt;T&gt;
        /// </summary>
        /// <typeparam name="T">double, int 등 바인딩 데이타 타입</typeparam>
        /// <param name="chart"> 챠트 </param>
        /// <param name="SeriesAxisX_Datas"> X축 Header 문자열[]</param>
        /// <param name="ToDatas">데이타 클래스&lt;T&gt;[]</param>
        public static void AddSeries<T>(this Chart chart, SeriesData<T> ToDatas)
        {
            try
            {
                chart.Data.Series++;
                chart.Series[chart.Series.Count - 1].Color = ToDatas.Color;
                chart.Series[chart.Series.Count - 1].Gallery = ToDatas.Gallery;
                chart.Series[chart.Series.Count - 1].MarkerShape = MarkerShape.None;
                chart.Series[chart.Series.Count - 1].Text = ToDatas.Name;
                chart.Series[chart.Series.Count - 1].AxisX.Labels.AddRange(ToDatas.Labels);
                if (ToDatas.IsAxisY2) chart.Series[chart.Series.Count - 1].AxisY = chart.AxisY2;
                // Y축 2를 사용하는 필드로 SeriesData 내에 IsAxisY2가 True인것은 이 좌표계를 사용함.

                int cnt = 0;
                foreach (T _data in ToDatas.Values)
                {
                    chart.Data.Y[chart.Series.Count - 1, cnt++] = Convert.ToDouble(_data);
                }
                Debug.Write(string.Format("chart.SeriesCount : {0}", chart.Series.Count - 1));

            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 챠트 데이타 셋팅&lt;T&gt;
        /// </summary>
        /// <typeparam name="T">double, int 등 바인딩 데이타 타입</typeparam>
        /// <param name="chart"> 챠트 </param>
        /// <param name="SeriesAxisX_Datas"> X축 Header 문자열[]</param>
        /// <param name="ToDatas">데이타 클래스&lt;T&gt;[]</param>
        public static void AddSeries(this Chart chart, SeriesData ToDatas)
        {
            try
            {
                chart.Data.Series++;
                chart.Series[chart.Series.Count - 1].Color = ToDatas.Color;
                chart.Series[chart.Series.Count - 1].Gallery = ToDatas.Gallery;
                chart.Series[chart.Series.Count - 1].MarkerShape = MarkerShape.None;
                chart.Series[chart.Series.Count - 1].Text = ToDatas.Name;
                chart.Series[chart.Series.Count - 1].AxisX.Labels.AddRange(ToDatas.Labels);
                if (ToDatas.IsAxisY2) chart.Series[chart.Series.Count - 1].AxisY = chart.AxisY2;
                // Y축 2를 사용하는 필드로 SeriesData 내에 IsAxisY2가 True인것은 이 좌표계를 사용함.

                int cnt = 0;
                foreach (double _data in ToDatas.Values)
                {
                    chart.Data.Y[chart.Series.Count - 1, cnt++] = Convert.ToDouble(_data);
                }
                Debug.Write(string.Format("chart.SeriesCount : {0}", chart.Series.Count - 1));

            }
            catch (Exception ex)
            {
                throw ex;
            }
        }


##############################################  이하 기존 소스 .......................

// 챠트 생성 

 ## 우선 DataTable에서 챠트에 Series로 등록될 데이타를 구한다.

   #region   챠트
           // Series 1개가 됨.
            List<ChartEX.SeriesData> datas = new List<ChartEX.SeriesData>();
            EnumerableRowCollection<double> BM_DURATION = dt.AsEnumerable().Select(c => Convert.ToDouble(c.Field<object>("BM_DURATION")) ); 

            datas.Add(new ChartEX.SeriesData()
            {
                Name = "벤치마크",
                Values = BM_DURATION.ToArray(),
                Color = ChartEX.DefaultColors[datas.Count],
                Gallery = Gallery.Lines,
                IsAxisY2 = false
            });  

            EnumerableRowCollection<double> PF_DURATION = dt.AsEnumerable().Select(c => Convert.ToDouble(c.Field<object>("PF_DURATION")) ); 

            datas.Add(new ChartEX.SeriesData
            {
                Name = "채권",
                Values = PF_DURATION.ToArray(),
                Color = ChartEX.DefaultColors[datas.Count],
                Gallery = Gallery.Lines,
                IsAxisY2 = false
            });

            EnumerableRowCollection<double> RF_RATE = dt.AsEnumerable().Select(c => Convert.ToDouble(c.Field<object>("RF_RATE")) ); // Secondary AxisY

            datas.Add(new ChartEX.SeriesData
            {
                Name = "시장이자율",
                Values = RF_RATE.ToArray(),
                Color = ChartEX.DefaultColors[datas.Count],
                Gallery = Gallery.Lines,
                IsAxisY2 = true
            });

            ## 챠트 Y 좌표계 Max Min 구하기. 이때 챠트에 사용되는 Y2 좌표계 데이타를 제외한다.
         RF_RATE를 제외했음.
            double AxisYMax = BM_DURATION.Max();
            if (AxisYMax < PF_DURATION.Max())
                AxisYMax = PF_DURATION.Max();

            double AxisYMin = BM_DURATION.Min();
            if (AxisYMin > PF_DURATION.Min())
                AxisYMin = PF_DURATION.Min();

            ##챠트 초기화
            //chart.ChartInit();
            ##챠트 셋팅
            chart.ChartSetting(
                    dt.AsEnumerable().Select(c => c.Field<string>("WORKDAY")).ToArray(), //##X축 Heaer 문자열[]
                    datas.ToArray(),   //##시리즈 데이타클래스[] 를 넘겨준다.
                //null,
                    Legend => //##LegendBox 셋팅
                    {
                        Legend.Dock = DockArea.Top;
                    },
                //null,
                    (X, Y) => //##X,Y좌표계 셋팅
                    {
                        X.AutoScale = true;
                        X.Step = 3;
                        X.FirstLabel = 0;
                        X.LabelsFormat.Format = AxisFormat.Date;
                        X.LabelsFormat.CustomFormat = "yyyy-MM-dd";
                        X.Step = 0;

                        Y.AutoScale = false;
                        Y.ForceZero = false;
                        Y.LabelsFormat.Format = AxisFormat.Number;
                        Y.LabelsFormat.CustomFormat = "N2";
                        Y.Max = AxisYMax;
                        Y.Min = AxisYMin;
                        Y.Step = (AxisYMax - AxisYMin) / 10d; //0=Auto
                    },
                // null
                    AxisY2 => //## Y2 좌표계에 대한 셋팅
                    {
                        AxisY2.AutoScale = true;
                        AxisY2.ForceZero = false;
                        AxisY2.Min = RF_RATE.Min();
                        AxisY2.Max = RF_RATE.Max();
                        AxisY2.Step = (AxisY2.Max - AxisY2.Min) / 10d;
                        AxisY2.LabelsFormat.CustomFormat = "N2";
                        AxisY2.Title.Text = "RF_RATE";
                    }
            );
            datas.Clear();
            datas = null;
            #endregion

여기까지 하면 챠트가 그려진다. ###############################################################



// 챠트 FX 확장 클래스 전체 소스
    /// <summary>
    /// 챠트 확장 클래스
    /// </summary>
    public static class ChartEX
    {
        /// <summary>
        /// 데이타 클래스
        /// </summary>
        public class SeriesData : SeriesData<Double>
        {
        }
        /// <summary>
        /// 데이타 클래스
        /// </summary>
        public class SeriesData<T>
        {
            public string Name { get; set; }
            public T[] Values { get; set; }
            public System.Drawing.Color Color { get; set; }
            public Gallery Gallery { get; set; }
            public bool IsAxisY2 { get; set; }
        }
        /// <summary>
        /// 기본 컬러값 빨주노초파남보?
        /// </summary>
        public static System.Drawing.Color[] DefaultColors = new System.Drawing.Color[] {
                 System.Drawing.Color.Red
                ,System.Drawing.Color.Orange
                ,System.Drawing.Color.Yellow
                ,System.Drawing.Color.Green
                ,System.Drawing.Color.Blue
                ,System.Drawing.Color.DarkBlue // 남색이 맞으려나?
                ,System.Drawing.Color.Violet
        };
        /// <summary>
        /// 좌표계 셋팅 대리자
        /// </summary>
        /// <param name="x">X축</param>
        /// <param name="Y">Y축</param>
        public delegate void __Settings_Axis(Axis x, Axis Y);
        /// <summary>
        /// 레젼드 셋팅 대리자
        /// </summary>
        /// <param name="legendBox">레젼드박스</param>
        public delegate void __Settings_LegendBox(LegendBox legendBox);
        /// <summary>
        /// 좌표계 Y축2  셋팅 대리자
        /// </summary>
        /// <param name="ay2">Y2축</param>
        public delegate void __SetAxisY2(AxisY ay2);

        /// <summary>
        /// 레젼드 박스 셋팅
        /// <para>람다식사용</para>
        /// <para> ( L ) => {</para>
        /// <para>   L 셋팅;</para>
        /// <para> }</para>
        /// </summary>
        /// <param name="chart">챠트</param>
        /// <param name="DoSetting"> 셋팅 대리자로 레젼드 노출 </param>
        private static void ChartLegendBoxSetting(this Chart chart, __Settings_LegendBox DoSetting = null)
        {
            chart.LegendBox.Visible = true;
            chart.LegendBox.Dock = DockArea.Bottom;
            chart.LegendBox.AutoSize = true;

            if (DoSetting != null)
                DoSetting(chart.LegendBox);
        }

        /// <summary>
        /// 챠트 좌표계 셋팅
        /// <para>람다식사용</para>
        /// <para> ( X, Y ) => {</para>
        /// <para>   X 셋팅;</para>
        /// <para>   Y 셋팅;</para>
        /// <para> }</para>
        /// </summary>
        /// <param name="chart">챠트</param>
        /// <param name="DoSetting"> 좌표계 셋팅 대리자로 X축 Y축 노출</param>
        private static void ChartAxisSetting(this Chart chart, __Settings_Axis DoSetting = null)
        {
            chart.AxisX.AutoScale = true;
            chart.AxisX.Step = 3;
            chart.AxisX.FirstLabel = 0;
            chart.AxisX.LabelsFormat.Format = AxisFormat.Date;
            chart.AxisX.LabelsFormat.CustomFormat = "yyyy-MM-dd";
            chart.AxisX.Step = 0;

            chart.AxisY.AutoScale = true;
            chart.AxisY.ForceZero = false;
            chart.AxisY.LabelsFormat.Format = AxisFormat.Number;
            chart.AxisY.LabelsFormat.CustomFormat = "N2";
            chart.AxisY.Step = 0.5d; //0=Auto

            if (DoSetting != null)
                DoSetting(chart.AxisX, chart.AxisY);
        }

        /// <summary>
        /// 챠트 데이타 셋팅&lt;T&gt;
        /// </summary>
        /// <typeparam name="T">double, int 등 바인딩 데이타 타입</typeparam>
        /// <param name="chart"> 챠트 </param>
        /// <param name="SeriesAxisX_Datas"> X축 Header 문자열[]</param>
        /// <param name="ToDatas">데이타 클래스&lt;T&gt;[]</param>
        private static void ChartDataSetting<T>(this Chart chart, string[] SeriesAxisX_Datas, params SeriesData<T>[] ToDatas)
        {
            try
            {
                /* 각 시리즈 별로 데이타를 넣음
                 * 데이타 클래스에 담겨진 데이타를(SeriesData)
                 * chart.Data.Y[SeriesCnt, cnt++] = Convert.ToDouble(_data); 를 통해 각 씨리즈의 Y축에 뿌려줌.
                 *  chart.AxisX.Labels.AddRange(SeriesAxisX_Datas); X축에 헤더를 뿌림.
                 */
                int SeriesCnt = 0;
                int cnt = 0;
                foreach (var Series in chart.Series)
                {
                    chart.Series[SeriesCnt].Color = ToDatas[SeriesCnt].Color;
                    chart.Series[SeriesCnt].Gallery = ToDatas[SeriesCnt].Gallery;
                    chart.Series[SeriesCnt].MarkerShape = MarkerShape.None;
                    chart.Series[SeriesCnt].Text = ToDatas[SeriesCnt].Name;

                    if (ToDatas[SeriesCnt].IsAxisY2) chart.Series[SeriesCnt].AxisY = chart.AxisY2;
                    // Y축 2를 사용하는 필드로 SeriesData 내에 IsAxisY2가 True인것은 이 좌표계를 사용함.

                    cnt = 0;
                    foreach (T _data in ToDatas[SeriesCnt].Values)
                    {
                        chart.Data.Y[SeriesCnt, cnt++] = Convert.ToDouble(_data);
                    }

                    Debug.Write(string.Format("chart.SeriesCount : {0}", SeriesCnt));
                    SeriesCnt++;
                }
                chart.AxisX.Labels.AddRange(SeriesAxisX_Datas);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 챠트 셋팅
        /// </summary>
        /// <typeparam name="T">double, int 등 바인딩 데이타 타입</typeparam>
        /// <param name="chart">챠트</param>
        /// <param name="SeriesAxisX_ColumnName">X축 Header 문자열[]</param>
        /// <param name="ToSeriesDatas">데이타 클래스&lt;T&gt;[]</param>
        /// <param name="legendBox">레젼드 셋팅 대리자</param>
        /// <param name="AxisXY">좌표계x,y 셋팅 대리자</param>
        /// <param name="setay2">좌표계Y2 셋팅 대리자</param>
        public static void ChartSetting<T>(this Chart chart, string[] SeriesAxisX_ColumnName, SeriesData<T>[] ToSeriesDatas, __Settings_LegendBox legendBox = null, __Settings_Axis AxisXY = null, __SetAxisY2 setay2 = null)
        {
            chart.Reset();                                                      // 리셋
            chart.Data.Series = ToSeriesDatas.Length;                           // 시리즈 갯수지정
            chart.AxesStyle = AxesStyle.FlatFrame;                              // 좌표계프레임 설정인데... FlatFrame이 가장 보기 좋음.
            chart.ChartLegendBoxSetting(legendBox);                             // 디폴트 설정후 -> 대리자 처리로 덮어쓰기..
            chart.ChartAxisSetting(AxisXY);                                     // 디폴트 설정후 -> 대리자 처리로 덮어쓰기..
            if (setay2 != null) setay2(chart.AxisY2);                           // 좌표계Y2 셋팅 대리자로 노출시킴[ AxisY2는 추가된 좌표계가 적을시 새로 생성해서 리턴하므로 따로 추가할 필요없음.
            chart.ChartDataSetting<T>(SeriesAxisX_ColumnName, ToSeriesDatas);   // 데이타 셋팅
            chart.RecalculateScale();                                           // 챠트 다시 계산함.
        }

        /// <summary>
        /// 챠트 초기화
        /// </summary>
        /// <param name="chart">챠트</param>
        public static void ChartInit(this Chart chart)
        {
            chart.Reset();
            chart.AxesX.Clear();
            chart.AxesY.Clear();
            chart.LegendBox.Visible = false;
            for (int i = chart.Series.Count - 1; i >= 0; i--)
            {
                chart.Series.Remove(chart.Series[i]);
            }
            chart.Data.Clear();    // 실제 데이타를 초기화 하여 데모에 따라오는 챠트를 숨김.
            chart.Data.Series = 0;
            chart.Titles.Clear();
        }
    }

'# 4) .Net ( Vs 2010 ) > C#' 카테고리의 다른 글

[IPC] Event 추가 ~~  (0) 2011.05.14
LINQ] 로또 구하기?  (1) 2011.04.25
[C#]Box 그리기...  (0) 2010.11.29
[C#]TabControl에서 특정 TabPage를 안보이게 감추기..  (0) 2010.11.26
[LINQ] 콤마 구분자 넣기?  (0) 2010.11.24