LegendBox에 CheckBox, Series모양, Series 이름을 넣은 화면입니다.

 

 

[이미지 1] LegendBox에 CheckBox를 추가한 모습

 

 

Xaml 부분에 아래와 같이 추가합니다.

 

xmlns:cfxConverters="http://schemas.softwarefx.com/chartfx/wpf/80/converters"

xmlns:cfxControls="http://schemas.softwarefx.com/chartfx/wpf/80/controls"

 

.............

 

 

<cfx:Chart.Resources>

     <DataTemplate x:Key="CheckLegend">

         <DataTemplate.Resources>

             <cfxConverters:VisibilityToBooleanConverter x:Key="VisibilityToBool" FalseVisibility="Hidden"/>

         </DataTemplate.Resources>

         <Grid>

             <Grid.ColumnDefinitions>

                 <ColumnDefinition Width="Auto" />

                 <ColumnDefinition Width="Auto" />

                 <ColumnDefinition Width="*" />

             </Grid.ColumnDefinitions>

             <CheckBox Margin="2,0" VerticalAlignment="Center" IsChecked="{Binding Path=Visibility, Converter={StaticResource VisibilityToBool}}" />

             <cfxControls:MarkerLegendControl Content="{Binding Path=Self}"   Grid.Column="1" Margin="2,0" />

             <TextBlock FontFamily="{Binding Path=FontFamily}" FontSize="{Binding Path=FontSize}" Text="{Binding Path=Text}" Grid.Column="2" VerticalAlignment="Center" Margin="2,0" />

         </Grid>

     </DataTemplate>

 </cfx:Chart.Resources>

 <cfx:Chart.AxisX>

 

 

 

현재 Xaml코드에서 DataTamplate를 변경할 방법이 없어 cs 코드상에서 구현해 주어아한다고 하네요.

 

ChartFX.WPF.LegendItemAttributes itemAttr = this.chart.LegendBox.ItemAttributes[chart.Series];

itemAttr.Template = (DataTemplate)this.chart.FindResource("CheckLegend"); 

 

 

 

 

 

참조 : SoftWare FX Community - WPF Blog

내가 지정한 컬럼 순서대로 스키마 정할때 옵션

 

Options->Column->Physical Order 체크해제

 

 

RadialGuage 같은 경우에는 RadialGuageNeedle Class 속성에 AllowDrag 라는 속성이 있어서 사용자가 마우스를 이용해서 변경 할 수 있습니다.

(인프라지스틱스 홈페이지 참조 : http://help.infragistics.com/NetAdvantage/winforms/CLR4.0/?page=WinGauge_Working_with_Linear_Gauges.html)

 

그치만 Linear Gauge의 LinearGaugeNeedle에는 AllowDrag를 제공하지 않아 직접 구현해야합니다.

 

 

 

 

일단 클릭한 지점의 Marker를 저장하기 위한 필드 하나를 작성합니다.

 

 

private LinearGaugeNeedle draggingNeedle; 

 

 

 

ultraGauge 개체의 아래의 이벤트 등록합니다.

 

 

 this.ultraGauge.PrimitiveMouseDown += ultraGauge1_PrimitiveMouseDown;

            this.ultraGauge.MouseMove += ultraGauge1_MouseMove;

            this.ultraGauge.MouseLeave += ultraGauge1_MouseLeave;

            this.ultraGauge.MouseUp += ultraGauge1_MouseUp;

 

 

 

아래와 같이 구현합니다.

MouseDown 되었을때에는 needle 개체를 얻고,

MouseMove될때는 얻은 needle개체가 있다면 Value를 설정합니다.

아래의 예제같은경우 min, max를 지정을해서 Range 밖으로의 이동을 막았습니다.

 

       

        private void ultraGauge1_PrimitiveMouseDown(object sender, GaugePrimitiveMouseEventArgs e)

        {

            LinearGaugeNeedle needle = e.Primitive.Element as LinearGaugeNeedle;

            if (needle != null)

            {

                this.draggingNeedle = needle;

            }

        }

 

        private void ultraGauge1_MouseMove(object sender, MouseEventArgs e)

        {

            if (this.draggingNeedle != null)

            {

               

                Rectangle scaleBounds = this.draggingNeedle.Scale.GetScaleBounds(this.ultraGauge.Bounds);

                double value = (double)this.draggingNeedle.Scale.Axis.MapInverse(e.X, scaleBounds.Left, scaleBounds.Right);

                if (value < min || value > max)

                    return;

                this.draggingNeedle.Value = value;

                System.Diagnostics.Debug.WriteLine(this.draggingNeedle.Value);

            }

        }

        private void ultraGauge1_MouseLeave(object sender, EventArgs e)

        {

            this.draggingNeedle = null;

        }

        private void ultraGauge1_MouseUp(object sender, MouseEventArgs e)

        {

            this.draggingNeedle = null;

        }

 

 

 

 

 

부모 컨테이너에서 MouseUp 되는것도 감지해줘야하기 때문에

해당 컨테이너의 Mouse Up 또는 Mouse Leave 되는 이벤트도 override해줍니다.

 

 

        protected override void OnMouseUp(MouseEventArgs e)

        {

            base.OnMouseUp(e);

            this.draggingNeedle = null;

        }

 

        protected override void OnMouseLeave(EventArgs e)

        {

            base.OnMouseLeave(e);

            this.draggingNeedle = null;

        }

 

 

 

결과 이미지입니다.

 

 

 

 

 

아래 압축파일은 예제 Form Class파일입니다.

 

Gauge 마커 이동 Form 예제.zip

 

 

 

 

Minimum, Maximum, Q1, Q2, Q3로 이루어진 Chart로써 데이터 분포를 설명하는데 사용됩니다.

<이미지 1> BoxChart(Whisker Chart)의 예제

 

 

 

 

 

 

UltraChart에 Binding 할 때 DataTable의 구조는 Minimum, Q1, Q2, Q3, Maximum, Label 순서입니다.

각 Row당 한 개의 Box를 나타내며 Box의 구조는 아래 <이미지 2>와 동일합니다.

박스의 크기는 BoxChart 개체의 BoxWidthFactor 속성으로 변경가능합니다.

 

<이미지2> 구성된 구조 (출처:Infragistics)

 

 

Infragistics의 UltraChart는 아래의 이미지와 같이 구성되어야합니다. (Column명은 무관)

 

<이미지 3> DataTable 구조

 

 

 

아래는 SampleCode 입니다.

 

 

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 

namespace AboutUltraChart 
{ 
    public partial class AboutWhiskerChart : Form 
    { 
        public AboutWhiskerChart() 
        { 
            InitializeComponent(); 
        } 

        private void AboutWhiskerChart_Load(object sender, EventArgs e) 
        { 
            Infragistics.Win.UltraWinChart.UltraChart chart = this.CreateSampleBoxChart(); 

            DataTable dtResult = this.CreateBoxChartSampleData(); 
           
            chart.DataSource = dtResult; 
            chart.DataBind(); 
        } 

        /// <summary> 
        /// Sample DataTable 생성 
        /// </summary> 
        /// <returns></returns> 
        private DataTable CreateBoxChartSampleData() 
        { 
            DataTable dtResult = new DataTable(); 
            dtResult.Columns.Add("Min", typeof(float)); 
            dtResult.Columns.Add("Q1", typeof(float)); 
            dtResult.Columns.Add("Q2", typeof(float)); 
            dtResult.Columns.Add("Q3", typeof(float)); 
            dtResult.Columns.Add("Max", typeof(float)); 
            dtResult.Columns.Add("Label", typeof(string)); 

            DataRow dr = dtResult.NewRow(); 
            dr[0] = 9; 
            dr[1] = 10; 
            dr[2] = 15; 
            dr[3] = 21; 
            dr[4] = 28; 
            dr[5] = "Junction1"; 

            dtResult.Rows.Add(dr); 

            dr = dtResult.NewRow(); 
            dr[0] = 4; 
            dr[1] = 10; 
            dr[2] = 20; 
            dr[3] = 30; 
            dr[4] = 38; 
            dr[5] = "Junction2"; 
            dtResult.Rows.Add(dr); 

            dr = dtResult.NewRow(); 
            dr[0] = 20; 
            dr[1] = 23; 
            dr[2] = 32; 
            dr[3] = 35; 
            dr[4] = 37; 
            dr[5] = "Junction3"; 
            dtResult.Rows.Add(dr); 

            dr = dtResult.NewRow(); 
            dr[0] = 30; 
            dr[1] = 35; 
            dr[2] = 36; 
            dr[3] = 42; 
            dr[4] = 44; 
            dr[5] = "Junction4"; 
            dtResult.Rows.Add(dr); 

            dr = dtResult.NewRow(); 
            dr[0] = 30; 
            dr[1] = 30; 
            dr[2] = 30; 
            dr[3] = 32; 
            dr[4] = 35; 
            dr[5] = "Junction5"; 
            dtResult.Rows.Add(dr); 

            dr = dtResult.NewRow(); 
            dr[0] = 10; 
            dr[1] = 14; 
            dr[2] = 16; 
            dr[3] = 20; 
            dr[4] = 24; 
            dr[5] = "Junction6"; 
            dtResult.Rows.Add(dr); 

            dr = dtResult.NewRow(); 
            dr[0] = 11; 
            dr[1] = 13; 
            dr[2] = 14; 
            dr[3] = 15; 
            dr[4] = 16; 
            dr[5] = "Junction7"; 
            dtResult.Rows.Add(dr); 

            dr = dtResult.NewRow(); 
            dr[0] = 12; 
            dr[1] = 16; 
            dr[2] = 21; 
            dr[3] = 23; 
            dr[4] = 25; 
            dr[5] = "Junction8"; 
            dtResult.Rows.Add(dr); 

            return dtResult; 
        } 

        /// <summary> 
        /// BoxChart 생성 
        /// </summary> 
        /// <returns></returns> 
        private Infragistics.Win.UltraWinChart.UltraChart CreateSampleBoxChart() 
        { 
            this.Controls.Clear(); 
            Infragistics.Win.UltraWinChart.UltraChart chart = new Infragistics.Win.UltraWinChart.UltraChart(); 
            chart.ChartType = Infragistics.UltraChart.Shared.Styles.ChartType.BoxChart; 
            chart.Dock = DockStyle.Fill; 
            this.Controls.Add(chart); 
            return chart; 
        } 
    } 
} 
        /// <summary>
        /// 모든 컬럼 Header 글자에 맞게 컬럼 Size를 조정한다.
        /// </summary>
        public void SetAutoAllColumnHeaderSize()
        {
            try
            {
                foreach (SheetView sv in this.Sheets)
                {
                    foreach (Column column in sv.Columns)
                    {
                        column.Width = column.GetPreferredWidth() + 5;
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

 

How to C# Component - tip

FpSpreadSheet4.0 Sheet의 데이터 및 스키마를 Save 및 Open 하기

 

1. 개요

[그림 1.Sheet의 데이터]

프로젝트를 진행 하다보면 Sheet의 데이터를 저장하였다가 프로그램 재 구동시 불러와야 할 경우가 있습니다.

제가 생각하는 방법은 2가지 입니다.

1. Sheet에 바인딩 되어있는 DataTable을 Serialize하여 txt나 xml로 저장 하였다가 Deserialize하여 가져온다.

2. FpSpreadSheet의 Save 메소드와 Open 메소드를 이용하여 xml로 관리한다.

두 번째 방법을 이용하면 Sheet의 스키마도 Save 및 Open 할 수 있으니 두 번째 방법으로 설명하겠습니다.

 

 

2. 얻을 수 있는 기술

    - FpSpreadSheet의 Save, Open

 

3. 사용할 Method 소개하기

    Save : Sheet의 데이터를 인코딩하여 파일로 저장합니다. (Sheet의 스키마도 저장가능)

    Open : Sheet의 데이터를 디코딩하여 Sheet에 불러옵니다. (Sheet의 스키마도 로드가능)

 

 

4. 프로그래밍 UI 디자인

단계1. 프로젝트 생성

[그림 2. 프로젝트 생성]

    

    단계2. Form UI 디자인하기

 

 

            [그림 3. Form UI 디자인하기-FormMain.cs]

 

 

 

 

 

        [그림 4. Form UI 디자인하기-FormTest1.cs]

 

 

 

 

5. 프로그래밍하기

     5.1 프로그래밍에 앞서 고민하기

         프로그램 규모가 작으면 당장엔 상관 없겠지만 점점 커지고 방대해 지다 보면 Sheet를 보여주는 Form이 1000개가 될 수 있습니다.

         메인 폼에서 다른 자식들의 폼을 1000개를 가지고 있는 List<Form> foms 필드가 있습니다.

        1000개의 폼에 존재하는 Sheet를 저장 및 로드를 해야 합니다. 1000개의 메소드를 일일이 호출 할 수는 없으니 Interface를 사용하였습니다.

 

 

     5.2 본격적으로 프로그래밍하기

          노하우를 효과적으로 전달하기 위하여 설계와 OOP 철학에 대한 부분은 생략하도록 하겠습니다.

         IExportAndImport Interface에 대해 먼저 설명하겠습니다. controlFileName은 Export, Import할 파일 이름입니다.

        Directory는 사용자에게 입력 받을 수 있기 때문에 파일 이름만 필드에 넣었습니다. Export는 Save, Import는 Open기능 입니다.

 

 

namespace AboutFarPointExportAndImport

{

/// <summary>

/// Export와 Import Interface

/// </summary>

interface IExportAndImport

{

/// <summary>

/// Export, Import 할 파일 이름

/// </summary>

string controlFileName { get; set; }

 

/// <summary>

/// Save

/// </summary>

/// <param name="filePath"></param>

void Export(string folderPath);

 

/// <summary>

/// Open

/// </summary>

/// <param name="filePath"></param>

void Import(string folderPath);

}

}

 

 

 

 

     각 폼은 IExportAndImport Interface를 상속 받아 구현합니다.

    Save 메소드의 2번째 인자는 데이터만 저장 할 것인지 Sheet의 스키마도 xml형식으로 저장 할 것인지 지정합니다.

    true일 경우 데이터만 false일경우 데이터+스키마 모두 저장합니다.

public partial class FormTest1 : Form, IExportAndImport

{

public FormTest1()

{

InitializeComponent();

InitTable();

}

 

#region IExportAndImport 멤버

 

public string controlFileName { get; set; }

 

public void Export(string filePath)

{

System.IO.FileStream fs = new System.IO.FileStream(filePath, System.IO.FileMode.Create, System.IO.FileAccess.Write);

this.sheet.Save(fs, true);

fs.Close();

}

 

public void Import(string filePath)

{

System.IO.FileStream fs = System.IO.File.Open(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read);

this.sheet.Open(fs);

fs.Close();

}

 

 

#endregion

 

}

 

.........

 

     FormMain의 동작을 설명하겠습니다.

     메인 폼은 List<Form> forms 라는 필드 하나를 가지고 있습니다. Load 할 때, Form 개체를 생성하고

        IExportAndImport의 필드인 controlFileName에 Save 및 Open할 파일 이름을 지정해줍니다.

 

public partial class FormMain : Form

{

/// <summary>

/// 생성한 Form

/// </summary>

List<Form> forms = null;

 

public FormMain()

{

InitializeComponent();

}

 

/// <summary>

/// Form Load할때

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

private void FormMain_Load(object sender, EventArgs e)

{

FormTest1 form1 = new FormTest1();

FormTest1 form2 = new FormTest1();

form1.controlFileName = "form1.xml";

form2.controlFileName = "form2.xml";

 

forms = new List<Form>();

forms.Add(form1);

forms.Add(form2);

}

......

 

      

 

 

     프로그램 구동 중에 Save를 하려고 Save 버튼을 눌렀을 시와 Load를 하려고 Load 버튼을 눌렀을 시입니다.

    자식 폼들이 또 자식 폼을 가지고 있다면, 해당 자식 Form의 Export 메소드를 구현 할 때,

    아래와 같은 구조로 작성하신다면 문제가 없을 것입니다.

 

/// <summary>

/// Export

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

private void btn_save_Click(object sender, EventArgs e)

{

FolderBrowserDialog fb = new FolderBrowserDialog();

if (fb.ShowDialog() == DialogResult.OK)

{

for (int i = 0; i < this.forms.Count; i++)

{

IExportAndImport exportAndImportMember = this.forms[i] as IExportAndImport;

if (exportAndImportMember != null)

{

exportAndImportMember.Export(string.Format("{0}\\{1}", fb.SelectedPath, exportAndImportMember.controlFileName));

}

}

}

}

 

/// <summary>

/// Import

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

private void btn_load_Click(object sender, EventArgs e)

{

FolderBrowserDialog fb = new FolderBrowserDialog();

if (fb.ShowDialog() == DialogResult.OK)

{

for (int i = 0; i < this.forms.Count; i++)

{

IExportAndImport exportAndImportMember = this.forms[i] as IExportAndImport;

if (exportAndImportMember != null)

{

exportAndImportMember.Export(string.Format("{0}\\{1}", fb.SelectedPath, exportAndImportMember.controlFileName));

}

}

}

}

 

 

 

 

How_to_-_FarPoint Sheet 저장 및 불러오기.docx

 

AboutFarPointExportAndImport.zip

 

디자인 창에서 FpSpread 작업 할 때, SheetView 추가하여 Column Header 조작하기를 설명하겠습니다.

FpSpread를 하나 생성 하신 후, 속성 중 Sheets를 클릭하여 SheetView를 하나 생성합니다.

이 때, Column의 개수 및 Row 개수를 조절 하실 수 있습니다. (물론 Spread Designer에서도 변경 가능합니다.)

 

 

 

 

 

 

 

 

 

 

 

 

 

오른쪽 마우스 버튼을 눌러 Spread Designer를 클릭합니다.

 

 

 

 

 

 1번 지점을 클릭하시면 전체 선택이됩니다. 2번의 ColumnHeaderRowCount 속성을 조절하시면 3번과 같이
Header의 Row 개수가 3개로 증가한걸 확인 하실 수 있습니다.

 

 

 

 

 

 

 

 

 

 

 

 

1번 처럼 드래그 하여 선택을 한 후 오른쪽 마우스 버튼을 눌러 맨아래의 "Headers.."를 클릭하면 Header Editor창이
뜨게 됩니다. 2번은 1번의 상태와 동일합니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

1번의 위치의 Cell을 클릭한 후, ColumnSpan을 2로 변경하면 Column 방향으로 2개가 합쳐지게 됩니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

그 상태에서 RowSpan을 3으로 주면 Column 2개와 Row 3개가 합쳐진 모습을 미리 보여줍니다. Ok를 눌러줍니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

1번과 같은 상태가 됩니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

이처럼 Column 헤더를 조작하면 위와 같은 복잡한 헤더도 간단히 구현 가능합니다.

 

+ Recent posts