Prepare
本文将使用一個NuGet公開的元件來實作曲線的顯示,包含了多種顯示的模式和配置來滿足各種不同的應用場景,友善大家進行快速的開發系統。
聯系作者及加群方式(激活碼在群裡發放):http://www.hslcommunication.cn/Cooperation
在Visual Studio 中的NuGet管理器中可以下載下傳安裝,也可以直接在NuGet控制台輸入下面的指令安裝:
Install-Package HslCommunication
NuGet安裝教程 http://www.cnblogs.com/dathlin/p/7705014.html
更強大的曆史曲線控件參考:https://www.cnblogs.com/dathlin/p/10291327.html
Summary
曲線控件屬于元件裡諸多控件中的一種,為什麼單獨拿出來寫一篇部落格呢,就是因為曲線控件相對于其他控件都要複雜很多,并不是幾個屬性那麼簡單,下面列舉了本曲線控件的特性:
- 提供便捷的API調用即可顯示曲線資訊内容,不需要複雜的配置
- 曲線界面的一些資訊可以自由定制,比如坐标軸的顔色,是否顯示虛線等等
- 高度的大小自适應,無論你怎麼調整控件的大小,始終都能友好顯示
- 支援資料拉伸填充和像素點填充兩種模式,具體差別參照下面的代碼
- 多曲線支援,支援同時顯示多個曲線資訊,每個曲線可獨立的指定顔色,線寬等等。
- 支援左右兩個參考系,就是說一個控件中允許顯示2種資料跨度不一緻的曲線,每種曲線可以顯示多條不同的曲線
其他控件的說明位址:http://www.cnblogs.com/dathlin/p/8150516.html
元件的完整API說明:http://www.cnblogs.com/dathlin/p/7703805.html
要想使用元件的控件,除了使用NuGet來安裝元件外,還需要将元件的dll檔案(在你的項目的packages裡面可以找到,如果你本來就是引用本地的,就直接拖拽本地的即可)拖拽到工具欄:

拖拽完成後效果如下:
先定義一個方法,擷取指定範圍的,指定個數的随機數數組,供下方的代碼調用
private float[] GetRandomValueByCount( int count, float min, float max )
{
float[] data = new float[count];
for (int i = 0; i < data.Length; i++)
{
data[i] = (float)random.NextDouble( ) * (max - min) + min;
}
return data;
}
1.單曲線使用
把控件拖拽到視窗界面上去後,現在界面如下,你可以随意的拉伸大小,調整到一個虛線看着比較清晰的時刻停止:
我們看到左右縱軸的資料跨度都是0-100,現在我們有個需求,手裡有300個0-200的資料需要顯示,那麼就要先設定左右縱軸的資料跨度
接下來就可以編寫顯示的代碼了,一下資料随機:
private void userButton1_Click( object sender, EventArgs e )
{
userCurve1.SetLeftCurve( "A", GetRandomValueByCount( 300, 0, 200 ), Color.DodgerBlue );
}
顯示結果如下:
看吧,相當簡單友善,如果你覺得目前的資料太密了,想要寬松一點,希望資料拉伸滿整個X軸,沒問題,因為目前預設的模式是像素點模式,是以切換為拉伸模式即可。
然後在運作看看效果:
接下來我們要對曲線“A”進行資料更新,我們假設你的data數組的資料已經更新了,有可能隻更新了一個資料,有可能全部更新了,在資料更新的時候就不需要在指定顔色了,因為指定了顔色也沒有用了。
private void userButton3_Click( object sender, EventArgs e )
{
// 假設你的data數組已經更新了
// 之前已經給A指定過顔色了,以後後續的資料更新不需要重新指定,指定了也無效
// 如果需要重新設定顔色,或是線寬,需要先RemoveCurve,然後重新建立曲線資訊
userCurve1.SetLeftCurve( "A", GetRandomValueByCount( 300, 100, 200 ) );
}
從上面的資料更新我們發現,隻要更新了資料,就不停的調用資料顯示,那麼我們就可以顯示實時資料了,而唯一的麻煩之處在于我們需要維護自己的data數組。是以目前的這種方式隻适合靜态資料顯示
2.舉個經典的例子
當我們需要顯示一些統計資料的時候,比如說我要顯示十二個月的銷售金額,那麼我們應該怎麼寫
我們先選擇拉伸模式,然後設定拉伸模式下最大的資料量為12;
private void userButton9_Click( object sender, EventArgs e )
{
// 模拟的資料
string[] text = new string[]
{
"一月",
"二月",
"三月",
"四月",
"五月",
"六月",
"七月",
"八月",
"九月",
"十月",
"十一月",
"十二月"
};
userCurve1.SetCurveText( text );
userCurve1.SetLeftCurve( "A", GetRandomValueByCount( 12, 0, 200 ), Color.Tomato ); // 每個月使用者1的銷售金額
}
效果圖如下:
如果我有兩條曲線需要顯示,以友善對比的話:
private void userButton9_Click( object sender, EventArgs e )
{
// 模拟的資料
string[] text = new string[]
{
"一月",
"二月",
"三月",
"四月",
"五月",
"六月",
"七月",
"八月",
"九月",
"十月",
"十一月",
"十二月"
};
userCurve1.SetCurveText( text );
userCurve1.SetLeftCurve( "A", GetRandomValueByCount( 12, 0, 200 ), Color.Tomato ); // 每個月使用者1的銷售金額
userCurve1.SetLeftCurve( "B", GetRandomValueByCount( 12, 0, 200 ), Color.DodgerBlue ); // 每個月使用者2的銷售金額
}
圖形效果如下:
即時3條曲線或者是更多的曲線,以此類推。重複設定資料即可,隻是每條曲線的關鍵字需要區分開來。
控件支援移除曲線,主要包含了下面的兩個方法,移除單個的曲線,或者是移除所有的曲線。
userCurve1.RemoveCurve( "A" ); // 移除指定的曲線
userCurve1.RemoveAllCurve( ); // 移除所有的曲線
在上面設定曲線資料的時候發現,是通過調用 SetLeftCurve 方法來設定曲線資訊的,這個方法有個left單詞,很明顯是設定左曲線的,控件裡還有設定右曲線的,SetLeftCurve 方法就是設定右曲線,那麼這裡的左右曲線都是指什麼呢?
我們在上面的曲線控件上看到,縱軸的刻度線分左右兩邊,那麼設定左曲線就是以左邊的刻度線為标準繪制的曲線,而設定右曲線則以右刻度線為标準,在上圖中,左右刻度的資訊是一緻的,是以無所謂左曲線還是右曲線,但是如果我們設定不一緻後,那麼我們就可以實作顯示2種不同範圍的資料資訊,例如我們右曲線設定為0-10,再随便顯示2條曲線
private void userButton9_Click( object sender, EventArgs e )
{
// 模拟的資料
string[] text = new string[]
{
"一月",
"二月",
"三月",
"四月",
"五月",
"六月",
"七月",
"八月",
"九月",
"十月",
"十一月",
"十二月"
};
userCurve1.SetCurveText( text );
userCurve1.SetLeftCurve( "A", GetRandomValueByCount( 12, 0, 200 ), Color.Tomato ); // 每個月使用者1的銷售金額
userCurve1.SetLeftCurve( "B", GetRandomValueByCount( 12, 0, 200 ), Color.DodgerBlue ); // 每個月使用者2的銷售金額
userCurve1.SetRightCurve( "C", GetRandomValueByCount( 12, 3, 6 ), Color.LimeGreen );
userCurve1.SetRightCurve( "D", GetRandomValueByCount( 12, 3, 6 ), Color.Orchid );
}
進階使用舉例,動态坐标軸,
根據上面的情況,我們看到如果我們擷取到的一組資料,範圍不确定的,需要來動态調整的,比如我們有一個12個資料的float數組,我們設定左坐标軸為資料的上下限
private void userButton10_Click( object sender, EventArgs e )
{
// 模拟的資料
string[] text = new string[]
{
"一月",
"二月",
"三月",
"四月",
"五月",
"六月",
"七月",
"八月",
"九月",
"十月",
"十一月",
"十二月"
};
float[] data = GetRandomValueByCount( 12, 40, 150 );
userCurve1.ValueMaxLeft = (float)Math.Ceiling( data.Max( ) );// 向上取整
userCurve1.ValueMinLeft = (float)Math.Floor( data.Min( ) );// 向下取整
userCurve1.SetCurveText( text );
userCurve1.SetLeftCurve( "A", data, Color.Tomato ); // 每個月使用者1的銷售金額
}
可以看到,不停的重新整理資料後,左坐标軸的資料一直在更新中。
總結下:如果每次都是強行更新所有的資料,也能達到實時重新整理的效果,但是如果是一個資料一個資料的采集顯示,将按照下面的實時資料模式使用更加合理
3.實時資料顯示使用
當我們需要顯示一些實時資料時,也就是說,每隔1秒(随便舉個栗子)就有新的資料采集上來,然後追加到曲線中去,曲線進行挪動,通常就是這種情況。
3.1 像素點模式(請确認 IsAbscissaStrech 為False):
我們先講解預設的模式,所謂像素點模式是指在橫軸上,一個像素點顯示一個資料,如果你的橫軸像素長度為1000,那麼你就可以顯示1000個資料了,當然在實時顯示的情況下,不需要你管那麼多,你隻需要負責定期往裡面塞資料即可。
第一步先進行初始化:先增加指定名字的曲線資訊,曲線顔色,曲線寬度等等
private void userButton4_Click( object sender, EventArgs e )
{
// 這裡傳入了數組長度為空的資料,不能傳NULL
userCurve1.SetLeftCurve( "B", new float[] { }, Color.Tomato );
}
我們再寫一個按鈕,啟動定時器,去新增資料,來模拟我們從其他裝置讀取到的資料資訊:
private void userButton5_Click( object sender, EventArgs e )
{
Timer timer = new Timer( );
timer.Interval = 100;
timer.Tick += ( sender1, e1 ) =>
{
userCurve1.AddCurveData( "B", random.Next( 50, 201 ) );
};
timer.Start( );
}
如上面的兩個按鈕資訊,必須先點選按鈕4進行曲線初始化,按鈕5的點選才有效果。最終你會看到曲線每隔100ms重新整理一次,不停的有新的資料遞增。
當曲線數量超過目前可顯示的點數時,曲線會自動的往左挪動,即時你拉伸的整個控件,它依然可以正常的工作,可顯示的資料點數會自動更新,記憶體中會緩存2048個資料點來支援拉伸的效果轉換。
當然,它也支援一次更新多個資料,雖然這種情況很少,隻是需要注意的是,一次更新的資料必須少于2048。
userCurve1.AddCurveData( "B", new float[] { random.Next( 50, 201 ), random.Next( 50, 201 ), random.Next( 50, 201 ) } );
2.2 拉伸模式(請确認 IsAbscissaStrech 為True):
拉伸模式的意思是無論你的data數組有多少個點,都強行按照最大資料點拉伸完整個橫軸界面
先設定 StrechDataCountMax 屬性為 300 ,意思是強行顯示300個點,最大300個點,僅僅在拉伸模式下有效果
private void userButton4_Click( object sender, EventArgs e )
{
// 這裡傳入了數組長度為空的資料,不能傳NULL
userCurve1.SetLeftCurve( "B", new float[] { }, Color.Tomato); // 指定上限500個資料,該上限隻對拉伸模式有效
}
private void userButton5_Click( object sender, EventArgs e )
{
Timer timer = new Timer( );
timer.Interval = 100;
timer.Tick += ( sender1, e1 ) =>
{
userCurve1.AddCurveData( "B", random.Next( 50, 201 ) );
};
timer.Start( );
}
我們再看拉伸模式的曲線:
你再拉伸控件試試看?拉伸模式的意思是無論你的控件多少大小,你規定了300個點,它就是300個點,即使你的控件拉伸了,它還是300個點,隻是沒有那麼密罷了
至于一次增加多個資料是和上面的像素點模式是一緻的。
2.3 模式差別及選擇
像素點模式下,随便看着資料比較密,但是好處在于分辨率高的顯示器,可以顯示的資料更多。
拉伸模式雖然在控件拉伸的情況下顯示的資料量不會增長,但是可以控制疏密程度。
各有優劣,建議先使用像素點模式,看看效果怎麼樣,一般資料變化都是慢慢來的,是以曲線不會像測試資料那樣亂串。如果資料亂串比較厲害,再使用拉伸模式。
3.多曲線,雙坐标使用
多曲線和單曲線模式很相似,無非是多幾條曲線而已,每條曲線的操作,新增資料都是一模一樣的,隻是多曲線的模式都是統一的,要麼全部是像素點模式,要麼全部是拉伸模式,所有的特性和上兩節是相似的。
為了說明使用,舉個例子,你有多個裝置(2個及以上),每個裝置都有一個溫度資訊,現在要進行實時資料的直接對比,當然最好将三條曲線放到一起顯示。
我們命名三個曲線為“A”,“B”,“C” 然後假設所有的資料都是100-200之間,資料A是160-180随機,資料B是150-170随機,資料C是155-165随機
此處測試友善,使用了 像素點模式。在 拉伸模式 下代碼也是一緻的
我們接下來看一種相當複雜的使用場景,假設我們有一台裝置,需要監控4條曲線,2條溫度,2條壓力,溫度的範圍是0-200,壓力的範圍為0-5 mpa,那麼想要在一個控件裡顯示,也是可以實作的。先調整左右的坐标範圍。
此處仍然使用像素點模式,我們接下來寫初始化代碼和新增資料的代碼:
private void userButton4_Click( object sender, EventArgs e )
{
userCurve1.SetLeftCurve( "A", new float[] { }, Color.Tomato ); // 溫度1
userCurve1.SetLeftCurve( "B", new float[] { }, Color.DodgerBlue ); // 溫度2
userCurve1.SetRightCurve( "C", new float[] { }, Color.LimeGreen ); // 壓力1
userCurve1.SetRightCurve( "D", new float[] { }, Color.Purple ); // 壓力2
}
private void userButton5_Click( object sender, EventArgs e )
{
Timer timer = new Timer( );
timer.Interval = 100;
timer.Tick += ( sender1, e1 ) =>
{
userCurve1.AddCurveData(
new string[] { "A", "B", "C", "D" },
new float[] { random.Next( 160, 181 ), random.Next( 150, 171 ), (float)random.NextDouble( ) * 2.5f + 1, (float)random.NextDouble( ) * 1f } );
};
timer.Start( );
}
效果如下圖:
4.橫坐标文本格式調整
上述的實時曲線在顯示的時候,我們看到橫坐标的文本是小時加分鐘的模式,如果我們改成隻顯示分鐘和秒鐘怎麼辦
這個屬性就是DateTime的格式化字元串,理論上你可以擷取到任務時間相關的文本資訊,按照目前的需求,調整成 mm:ss 即可
5.輔助線添加
我想在實時資料顯示中設定一條曲線資料的報警上限的輔助線,用來提醒以及更加至關的檢視資訊,是否異常,可以調用控件的方法來完成
新增的輔助線是左右兩個參考坐标系區分出來了,比如我新增左輔助線,192的一條線,顔色為紅色
userCurve1.AddLeftAuxiliary( 192, Color.Red);
移除的代碼為
userCurve1.RemoveAuxiliary( 192 );
輔助線效果如下:
如果你新增的輔助線和原有的虛線重疊時,原有的虛線會自動屏蔽掉。
6.背景顔色調整
我修改下背景為暗黑色,瞬間就有黑科技的效果了。當然,線條的顔色可以調整的更加好一點
4.結束語
感謝閱讀。