+ UILineChart:完成曲线图表初版
This commit is contained in:
parent
5b3d940da0
commit
552d683df3
Binary file not shown.
BIN
Bin/SunnyUI.dll
BIN
Bin/SunnyUI.dll
Binary file not shown.
BIN
Bin/SunnyUI.pdb
BIN
Bin/SunnyUI.pdb
Binary file not shown.
154
SunnyUI.Demo/Charts/FLineChart.Designer.cs
generated
Normal file
154
SunnyUI.Demo/Charts/FLineChart.Designer.cs
generated
Normal file
@ -0,0 +1,154 @@
|
||||
namespace Sunny.UI.Demo.Charts
|
||||
{
|
||||
partial class FLineChart
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.uiSymbolButton1 = new Sunny.UI.UISymbolButton();
|
||||
this.uiImageButton3 = new Sunny.UI.UIImageButton();
|
||||
this.uiImageButton2 = new Sunny.UI.UIImageButton();
|
||||
this.uiImageButton1 = new Sunny.UI.UIImageButton();
|
||||
this.uiLine1 = new Sunny.UI.UILine();
|
||||
this.uiLineChart1 = new Sunny.UI.UILineChart();
|
||||
this.PagePanel.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.uiImageButton3)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.uiImageButton2)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.uiImageButton1)).BeginInit();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// PagePanel
|
||||
//
|
||||
this.PagePanel.Controls.Add(this.uiSymbolButton1);
|
||||
this.PagePanel.Controls.Add(this.uiImageButton3);
|
||||
this.PagePanel.Controls.Add(this.uiImageButton2);
|
||||
this.PagePanel.Controls.Add(this.uiImageButton1);
|
||||
this.PagePanel.Controls.Add(this.uiLine1);
|
||||
this.PagePanel.Controls.Add(this.uiLineChart1);
|
||||
this.PagePanel.Size = new System.Drawing.Size(800, 539);
|
||||
//
|
||||
// uiSymbolButton1
|
||||
//
|
||||
this.uiSymbolButton1.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||
this.uiSymbolButton1.Font = new System.Drawing.Font("微软雅黑", 12F);
|
||||
this.uiSymbolButton1.Location = new System.Drawing.Point(348, 466);
|
||||
this.uiSymbolButton1.MinimumSize = new System.Drawing.Size(1, 1);
|
||||
this.uiSymbolButton1.Name = "uiSymbolButton1";
|
||||
this.uiSymbolButton1.Padding = new System.Windows.Forms.Padding(28, 0, 0, 0);
|
||||
this.uiSymbolButton1.Size = new System.Drawing.Size(100, 27);
|
||||
this.uiSymbolButton1.Symbol = 61952;
|
||||
this.uiSymbolButton1.TabIndex = 34;
|
||||
this.uiSymbolButton1.Text = "数据";
|
||||
//
|
||||
// uiImageButton3
|
||||
//
|
||||
this.uiImageButton3.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||
this.uiImageButton3.Font = new System.Drawing.Font("微软雅黑", 12F);
|
||||
this.uiImageButton3.Image = global::Sunny.UI.Demo.Properties.Resources.ChartDarkStyle;
|
||||
this.uiImageButton3.Location = new System.Drawing.Point(242, 466);
|
||||
this.uiImageButton3.Name = "uiImageButton3";
|
||||
this.uiImageButton3.Size = new System.Drawing.Size(100, 27);
|
||||
this.uiImageButton3.TabIndex = 33;
|
||||
this.uiImageButton3.TabStop = false;
|
||||
this.uiImageButton3.Text = " Dark";
|
||||
this.uiImageButton3.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// uiImageButton2
|
||||
//
|
||||
this.uiImageButton2.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||
this.uiImageButton2.Font = new System.Drawing.Font("微软雅黑", 12F);
|
||||
this.uiImageButton2.Image = global::Sunny.UI.Demo.Properties.Resources.ChartPlainStyle;
|
||||
this.uiImageButton2.Location = new System.Drawing.Point(136, 466);
|
||||
this.uiImageButton2.Name = "uiImageButton2";
|
||||
this.uiImageButton2.Size = new System.Drawing.Size(100, 27);
|
||||
this.uiImageButton2.TabIndex = 32;
|
||||
this.uiImageButton2.TabStop = false;
|
||||
this.uiImageButton2.Text = " Plain";
|
||||
this.uiImageButton2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// uiImageButton1
|
||||
//
|
||||
this.uiImageButton1.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||
this.uiImageButton1.Font = new System.Drawing.Font("微软雅黑", 12F);
|
||||
this.uiImageButton1.Image = global::Sunny.UI.Demo.Properties.Resources.ChartDefaultStyle;
|
||||
this.uiImageButton1.Location = new System.Drawing.Point(30, 466);
|
||||
this.uiImageButton1.Name = "uiImageButton1";
|
||||
this.uiImageButton1.Size = new System.Drawing.Size(100, 27);
|
||||
this.uiImageButton1.TabIndex = 31;
|
||||
this.uiImageButton1.TabStop = false;
|
||||
this.uiImageButton1.Text = " Default";
|
||||
this.uiImageButton1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// uiLine1
|
||||
//
|
||||
this.uiLine1.Font = new System.Drawing.Font("微软雅黑", 12F);
|
||||
this.uiLine1.Location = new System.Drawing.Point(30, 20);
|
||||
this.uiLine1.MinimumSize = new System.Drawing.Size(16, 16);
|
||||
this.uiLine1.Name = "uiLine1";
|
||||
this.uiLine1.Size = new System.Drawing.Size(670, 20);
|
||||
this.uiLine1.TabIndex = 30;
|
||||
this.uiLine1.Text = "UIBarChart";
|
||||
this.uiLine1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// uiLineChart1
|
||||
//
|
||||
this.uiLineChart1.FillColor = System.Drawing.Color.FromArgb(((int)(((byte)(244)))), ((int)(((byte)(244)))), ((int)(((byte)(244)))));
|
||||
this.uiLineChart1.Font = new System.Drawing.Font("微软雅黑", 12F);
|
||||
this.uiLineChart1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(54)))), ((int)(((byte)(54)))), ((int)(((byte)(54)))));
|
||||
this.uiLineChart1.Location = new System.Drawing.Point(30, 48);
|
||||
this.uiLineChart1.MinimumSize = new System.Drawing.Size(1, 1);
|
||||
this.uiLineChart1.Name = "uiLineChart1";
|
||||
this.uiLineChart1.Option = null;
|
||||
this.uiLineChart1.Size = new System.Drawing.Size(670, 400);
|
||||
this.uiLineChart1.TabIndex = 35;
|
||||
this.uiLineChart1.Text = "uiLineChart1";
|
||||
//
|
||||
// FLineChart
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(10F, 21F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(800, 574);
|
||||
this.Name = "FLineChart";
|
||||
this.Symbol = 61953;
|
||||
this.Text = "LineChart";
|
||||
this.PagePanel.ResumeLayout(false);
|
||||
((System.ComponentModel.ISupportInitialize)(this.uiImageButton3)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.uiImageButton2)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.uiImageButton1)).EndInit();
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private UISymbolButton uiSymbolButton1;
|
||||
private UIImageButton uiImageButton3;
|
||||
private UIImageButton uiImageButton2;
|
||||
private UIImageButton uiImageButton1;
|
||||
private UILine uiLine1;
|
||||
private UILineChart uiLineChart1;
|
||||
}
|
||||
}
|
10
SunnyUI.Demo/Charts/FLineChart.cs
Normal file
10
SunnyUI.Demo/Charts/FLineChart.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace Sunny.UI.Demo.Charts
|
||||
{
|
||||
public partial class FLineChart : UITitlePage
|
||||
{
|
||||
public FLineChart()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
10
SunnyUI.Demo/Controls/FOther.Designer.cs
generated
10
SunnyUI.Demo/Controls/FOther.Designer.cs
generated
@ -93,6 +93,7 @@
|
||||
this.uiLight1.CenterColor = System.Drawing.Color.FromArgb(((int)(((byte)(239)))), ((int)(((byte)(248)))), ((int)(((byte)(232)))));
|
||||
this.uiLight1.Font = new System.Drawing.Font("微软雅黑", 12F);
|
||||
this.uiLight1.Location = new System.Drawing.Point(30, 52);
|
||||
this.uiLight1.MinimumSize = new System.Drawing.Size(1, 1);
|
||||
this.uiLight1.Name = "uiLight1";
|
||||
this.uiLight1.Radius = 35;
|
||||
this.uiLight1.Size = new System.Drawing.Size(35, 35);
|
||||
@ -115,6 +116,7 @@
|
||||
this.uiLight2.CenterColor = System.Drawing.Color.FromArgb(((int)(((byte)(239)))), ((int)(((byte)(248)))), ((int)(((byte)(232)))));
|
||||
this.uiLight2.Font = new System.Drawing.Font("微软雅黑", 12F);
|
||||
this.uiLight2.Location = new System.Drawing.Point(114, 52);
|
||||
this.uiLight2.MinimumSize = new System.Drawing.Size(1, 1);
|
||||
this.uiLight2.Name = "uiLight2";
|
||||
this.uiLight2.Radius = 35;
|
||||
this.uiLight2.Size = new System.Drawing.Size(35, 35);
|
||||
@ -127,6 +129,7 @@
|
||||
this.uiLight3.CenterColor = System.Drawing.Color.FromArgb(((int)(((byte)(239)))), ((int)(((byte)(248)))), ((int)(((byte)(232)))));
|
||||
this.uiLight3.Font = new System.Drawing.Font("微软雅黑", 12F);
|
||||
this.uiLight3.Location = new System.Drawing.Point(156, 52);
|
||||
this.uiLight3.MinimumSize = new System.Drawing.Size(1, 1);
|
||||
this.uiLight3.Name = "uiLight3";
|
||||
this.uiLight3.Radius = 35;
|
||||
this.uiLight3.Size = new System.Drawing.Size(35, 35);
|
||||
@ -159,6 +162,7 @@
|
||||
//
|
||||
this.uiProgressIndicator1.Font = new System.Drawing.Font("微软雅黑", 12F);
|
||||
this.uiProgressIndicator1.Location = new System.Drawing.Point(30, 298);
|
||||
this.uiProgressIndicator1.MinimumSize = new System.Drawing.Size(1, 1);
|
||||
this.uiProgressIndicator1.Name = "uiProgressIndicator1";
|
||||
this.uiProgressIndicator1.Size = new System.Drawing.Size(119, 132);
|
||||
this.uiProgressIndicator1.TabIndex = 26;
|
||||
@ -180,6 +184,7 @@
|
||||
this.uiTrackBar1.DisableColor = System.Drawing.Color.Silver;
|
||||
this.uiTrackBar1.Font = new System.Drawing.Font("微软雅黑", 12F);
|
||||
this.uiTrackBar1.Location = new System.Drawing.Point(381, 136);
|
||||
this.uiTrackBar1.MinimumSize = new System.Drawing.Size(1, 1);
|
||||
this.uiTrackBar1.Name = "uiTrackBar1";
|
||||
this.uiTrackBar1.Size = new System.Drawing.Size(158, 29);
|
||||
this.uiTrackBar1.TabIndex = 30;
|
||||
@ -205,6 +210,7 @@
|
||||
this.uiLight4.CenterColor = System.Drawing.Color.FromArgb(((int)(((byte)(239)))), ((int)(((byte)(248)))), ((int)(((byte)(232)))));
|
||||
this.uiLight4.Font = new System.Drawing.Font("微软雅黑", 12F);
|
||||
this.uiLight4.Location = new System.Drawing.Point(72, 52);
|
||||
this.uiLight4.MinimumSize = new System.Drawing.Size(1, 1);
|
||||
this.uiLight4.Name = "uiLight4";
|
||||
this.uiLight4.OnColor = System.Drawing.Color.FromArgb(((int)(((byte)(230)))), ((int)(((byte)(80)))), ((int)(((byte)(80)))));
|
||||
this.uiLight4.Radius = 35;
|
||||
@ -256,7 +262,6 @@
|
||||
this.uiLedBulb4.BlinkInterval = 500;
|
||||
this.uiLedBulb4.Location = new System.Drawing.Point(507, 52);
|
||||
this.uiLedBulb4.Name = "uiLedBulb4";
|
||||
this.uiLedBulb4.On = false;
|
||||
this.uiLedBulb4.Size = new System.Drawing.Size(32, 32);
|
||||
this.uiLedBulb4.TabIndex = 41;
|
||||
this.uiLedBulb4.Text = "uiLedBulb4";
|
||||
@ -278,6 +283,7 @@
|
||||
this.uiScrollingText1.Font = new System.Drawing.Font("微软雅黑", 12F);
|
||||
this.uiScrollingText1.ForeColor = System.Drawing.Color.Red;
|
||||
this.uiScrollingText1.Location = new System.Drawing.Point(381, 215);
|
||||
this.uiScrollingText1.MinimumSize = new System.Drawing.Size(1, 1);
|
||||
this.uiScrollingText1.Name = "uiScrollingText1";
|
||||
this.uiScrollingText1.ScrollingType = Sunny.UI.UIScrollingText.UIScrollingType.LeftToRight;
|
||||
this.uiScrollingText1.Size = new System.Drawing.Size(319, 35);
|
||||
@ -292,6 +298,7 @@
|
||||
this.uiScrollingText2.Font = new System.Drawing.Font("微软雅黑", 12F);
|
||||
this.uiScrollingText2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(80)))), ((int)(((byte)(160)))), ((int)(((byte)(255)))));
|
||||
this.uiScrollingText2.Location = new System.Drawing.Point(381, 256);
|
||||
this.uiScrollingText2.MinimumSize = new System.Drawing.Size(1, 1);
|
||||
this.uiScrollingText2.Name = "uiScrollingText2";
|
||||
this.uiScrollingText2.Size = new System.Drawing.Size(319, 35);
|
||||
this.uiScrollingText2.TabIndex = 49;
|
||||
@ -369,7 +376,6 @@
|
||||
this.uiToolTip1.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.uiToolTip1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(239)))), ((int)(((byte)(239)))), ((int)(((byte)(239)))));
|
||||
this.uiToolTip1.OwnerDraw = true;
|
||||
this.uiToolTip1.ToolTipTitle = "ToolTip title";
|
||||
//
|
||||
// FOther
|
||||
//
|
||||
|
@ -123,9 +123,6 @@
|
||||
<metadata name="timer1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<metadata name="uiToolTip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>107, 17</value>
|
||||
</metadata>
|
||||
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>35</value>
|
||||
</metadata>
|
||||
|
@ -57,6 +57,7 @@ namespace Sunny.UI.Demo
|
||||
Aside.CreateChildNode(parent, AddPage(new FDoughnutChart()));
|
||||
Aside.CreateChildNode(parent, AddPage(new FBarChart()));
|
||||
Aside.CreateChildNode(parent, AddPage(new FBarChartEx()));
|
||||
Aside.CreateChildNode(parent, AddPage(new FLineChart()));
|
||||
|
||||
Header.SetNodeSymbol(Header.Nodes[3], 61502);
|
||||
var styles = UIStyles.PopularStyles();
|
||||
|
@ -65,6 +65,12 @@
|
||||
<Compile Include="Charts\FDoughnutChart.Designer.cs">
|
||||
<DependentUpon>FDoughnutChart.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Charts\FLineChart.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Charts\FLineChart.Designer.cs">
|
||||
<DependentUpon>FLineChart.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Controls\FAvatar.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
@ -310,6 +316,9 @@
|
||||
<EmbeddedResource Include="Charts\FBarChartEx.resx">
|
||||
<DependentUpon>FBarChartEx.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Charts\FLineChart.resx">
|
||||
<DependentUpon>FLineChart.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Controls\FAvatar.resx">
|
||||
<DependentUpon>FAvatar.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
|
@ -39,46 +39,37 @@ namespace Sunny.UI
|
||||
protected override void OnSizeChanged(EventArgs e)
|
||||
{
|
||||
base.OnSizeChanged(e);
|
||||
CalcData(BarOption);
|
||||
CalcData();
|
||||
}
|
||||
|
||||
protected override void CalcData(UIOption option)
|
||||
protected override void CalcData()
|
||||
{
|
||||
Bars.Clear();
|
||||
NeedDraw = false;
|
||||
UIBarOption o = (UIBarOption)option;
|
||||
if (o == null || o.Series == null || o.SeriesCount == 0) return;
|
||||
if (BarOption == null || BarOption.Series == null || BarOption.SeriesCount == 0) return;
|
||||
|
||||
DrawOrigin = new Point(BarOption.Grid.Left, Height - BarOption.Grid.Bottom);
|
||||
DrawSize = new Size(Width - BarOption.Grid.Left - BarOption.Grid.Right,
|
||||
Height - BarOption.Grid.Top - BarOption.Grid.Bottom);
|
||||
|
||||
if (DrawSize.Width <= 0 || DrawSize.Height <= 0) return;
|
||||
if (o.XAxis.Data.Count == 0) return;
|
||||
if (BarOption.XAxis.Data.Count == 0) return;
|
||||
|
||||
NeedDraw = true;
|
||||
DrawBarWidth = DrawSize.Width * 1.0f / o.XAxis.Data.Count;
|
||||
DrawBarWidth = DrawSize.Width * 1.0f / BarOption.XAxis.Data.Count;
|
||||
|
||||
double min = double.MaxValue;
|
||||
double max = double.MinValue;
|
||||
foreach (var series in o.Series)
|
||||
foreach (var series in BarOption.Series)
|
||||
{
|
||||
min = Math.Min(min, series.Data.Min());
|
||||
max = Math.Max(max, series.Data.Max());
|
||||
}
|
||||
|
||||
if (min > 0 && max > 0 && !o.YAxis.Scale)
|
||||
{
|
||||
min = 0;
|
||||
}
|
||||
|
||||
if (min < 0 && max < 0 && !o.YAxis.Scale)
|
||||
{
|
||||
max = 0;
|
||||
}
|
||||
|
||||
if (!o.YAxis.MaxAuto) max = o.YAxis.Max;
|
||||
if (!o.YAxis.MinAuto) min = o.YAxis.Min;
|
||||
if (min > 0 && max > 0 && !BarOption.YAxis.Scale) min = 0;
|
||||
if (min < 0 && max < 0 && !BarOption.YAxis.Scale) max = 0;
|
||||
if (!BarOption.YAxis.MaxAuto) max = BarOption.YAxis.Max;
|
||||
if (!BarOption.YAxis.MinAuto) min = BarOption.YAxis.Min;
|
||||
|
||||
if ((max - min).IsZero())
|
||||
{
|
||||
@ -86,20 +77,20 @@ namespace Sunny.UI
|
||||
min = 0;
|
||||
}
|
||||
|
||||
UIChartHelper.CalcDegreeScale(min, max, o.YAxis.SplitNumber,
|
||||
UIChartHelper.CalcDegreeScale(min, max, BarOption.YAxis.SplitNumber,
|
||||
out int start, out int end, out double interval);
|
||||
|
||||
YAxisStart = start;
|
||||
YAxisEnd = end;
|
||||
YAxisInterval = interval;
|
||||
|
||||
float x1 = DrawBarWidth / ((o.SeriesCount * 2) + o.SeriesCount + 1);
|
||||
float x1 = DrawBarWidth / (BarOption.SeriesCount * 2 + BarOption.SeriesCount + 1);
|
||||
float x2 = x1 * 2;
|
||||
|
||||
for (int i = 0; i < o.SeriesCount; i++)
|
||||
for (int i = 0; i < BarOption.SeriesCount; i++)
|
||||
{
|
||||
float barX = DrawOrigin.X;
|
||||
var series = o.Series[i];
|
||||
var series = BarOption.Series[i];
|
||||
Bars.TryAdd(i, new List<BarInfo>());
|
||||
for (int j = 0; j < series.Data.Count; j++)
|
||||
{
|
||||
|
@ -28,45 +28,44 @@ using System.Windows.Forms;
|
||||
|
||||
namespace Sunny.UI
|
||||
{
|
||||
public class UIBarChartEx : UIBarChart
|
||||
public sealed class UIBarChartEx : UIBarChart
|
||||
{
|
||||
protected override void CalcData(UIOption option)
|
||||
protected override void CalcData()
|
||||
{
|
||||
Bars.Clear();
|
||||
NeedDraw = false;
|
||||
UIBarOption o = (UIBarOption)option;
|
||||
if (o == null || o.Series == null || o.SeriesCount == 0) return;
|
||||
if (BarOption == null || BarOption.Series == null || BarOption.SeriesCount == 0) return;
|
||||
|
||||
DrawOrigin = new Point(BarOption.Grid.Left, Height - BarOption.Grid.Bottom);
|
||||
DrawSize = new Size(Width - BarOption.Grid.Left - BarOption.Grid.Right,
|
||||
Height - BarOption.Grid.Top - BarOption.Grid.Bottom);
|
||||
|
||||
if (DrawSize.Width <= 0 || DrawSize.Height <= 0) return;
|
||||
if (o.Series.Count == 0) return;
|
||||
if (BarOption.Series.Count == 0) return;
|
||||
|
||||
NeedDraw = true;
|
||||
DrawBarWidth = DrawSize.Width * 1.0f / o.Series.Count;
|
||||
DrawBarWidth = DrawSize.Width * 1.0f / BarOption.Series.Count;
|
||||
|
||||
double min = double.MaxValue;
|
||||
double max = double.MinValue;
|
||||
foreach (var series in o.Series)
|
||||
foreach (var series in BarOption.Series)
|
||||
{
|
||||
min = Math.Min(min, series.Data.Min());
|
||||
max = Math.Max(max, series.Data.Max());
|
||||
}
|
||||
|
||||
if (min > 0 && max > 0 && !o.YAxis.Scale)
|
||||
if (min > 0 && max > 0 && !BarOption.YAxis.Scale)
|
||||
{
|
||||
min = 0;
|
||||
}
|
||||
|
||||
if (min < 0 && max < 0 && !o.YAxis.Scale)
|
||||
if (min < 0 && max < 0 && !BarOption.YAxis.Scale)
|
||||
{
|
||||
max = 0;
|
||||
}
|
||||
|
||||
if (!o.YAxis.MaxAuto) max = o.YAxis.Max;
|
||||
if (!o.YAxis.MinAuto) min = o.YAxis.Min;
|
||||
if (!BarOption.YAxis.MaxAuto) max = BarOption.YAxis.Max;
|
||||
if (!BarOption.YAxis.MinAuto) min = BarOption.YAxis.Min;
|
||||
|
||||
if ((max - min).IsZero())
|
||||
{
|
||||
@ -74,7 +73,7 @@ namespace Sunny.UI
|
||||
min = 0;
|
||||
}
|
||||
|
||||
UIChartHelper.CalcDegreeScale(min, max, o.YAxis.SplitNumber,
|
||||
UIChartHelper.CalcDegreeScale(min, max, BarOption.YAxis.SplitNumber,
|
||||
out int start, out int end, out double interval);
|
||||
|
||||
YAxisStart = start;
|
||||
@ -87,9 +86,9 @@ namespace Sunny.UI
|
||||
float x1 = DrawSize.Width * 1.0f / DataCount / 4;
|
||||
float x2 = x1 * 2;
|
||||
|
||||
for (int i = 0; i < o.SeriesCount; i++)
|
||||
for (int i = 0; i < BarOption.SeriesCount; i++)
|
||||
{
|
||||
var series = o.Series[i];
|
||||
var series = BarOption.Series[i];
|
||||
Bars.TryAdd(i, new List<BarInfo>());
|
||||
|
||||
for (int j = 0; j < series.Data.Count; j++)
|
||||
@ -162,9 +161,9 @@ namespace Sunny.UI
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < o.SeriesCount; i++)
|
||||
for (int i = 0; i < BarOption.SeriesCount; i++)
|
||||
{
|
||||
var series = o.Series[i];
|
||||
var series = BarOption.Series[i];
|
||||
float x1;
|
||||
if (BarOption.FixedSeriesCount > 0)
|
||||
x1 = DrawBarWidth / (BarOption.FixedSeriesCount * 2 + BarOption.FixedSeriesCount + 1);
|
||||
|
@ -25,13 +25,13 @@ using System.Drawing;
|
||||
|
||||
namespace Sunny.UI
|
||||
{
|
||||
public class UIBarOption : UIOption, IDisposable
|
||||
public sealed class UIBarOption : UIOption, IDisposable
|
||||
{
|
||||
public UICategoryAxis XAxis { get; set; } = new UICategoryAxis();
|
||||
public UIAxis XAxis { get; set; } = new UIAxis(UIAxisType.Category);
|
||||
|
||||
public UIBarToolTip ToolTip { get; set; }
|
||||
|
||||
public UIValueAxis YAxis { get; set; } = new UIValueAxis();
|
||||
public UIAxis YAxis { get; set; } = new UIAxis(UIAxisType.Value);
|
||||
|
||||
public List<UIBarSeries> Series = new List<UIBarSeries>();
|
||||
|
||||
@ -90,6 +90,16 @@ namespace Sunny.UI
|
||||
|
||||
public class UIAxis
|
||||
{
|
||||
public UIAxis()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public UIAxis(UIAxisType axisType)
|
||||
{
|
||||
Type = axisType;
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public UIAxisType Type { get; set; }
|
||||
@ -122,15 +132,6 @@ namespace Sunny.UI
|
||||
public double Max { get; set; } = 100;
|
||||
public double Min { get; set; } = 0;
|
||||
|
||||
}
|
||||
|
||||
public class UICategoryAxis : UIAxis
|
||||
{
|
||||
public UICategoryAxis()
|
||||
{
|
||||
Type = UIAxisType.Category;
|
||||
}
|
||||
|
||||
public List<string> Data = new List<string>();
|
||||
|
||||
public void Clear()
|
||||
@ -197,14 +198,6 @@ namespace Sunny.UI
|
||||
public int Distance { get; set; } = 0;
|
||||
}
|
||||
|
||||
public class UIValueAxis : UIAxis
|
||||
{
|
||||
public UIValueAxis()
|
||||
{
|
||||
Type = UIAxisType.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public class UIBarSeries : IDisposable
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
@ -86,7 +86,7 @@ namespace Sunny.UI
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 字体颜色
|
||||
/// 字体颜色
|
||||
/// </summary>
|
||||
[Description("字体颜色")]
|
||||
[Category("SunnyUI")]
|
||||
@ -109,7 +109,7 @@ namespace Sunny.UI
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 填充颜色,当值为背景色或透明色或空值则不填充
|
||||
/// 填充颜色,当值为背景色或透明色或空值则不填充
|
||||
/// </summary>
|
||||
[Description("填充颜色")]
|
||||
[Category("SunnyUI")]
|
||||
@ -141,10 +141,10 @@ namespace Sunny.UI
|
||||
public void SetOption(UIOption option)
|
||||
{
|
||||
Option = option;
|
||||
CalcData(option);
|
||||
CalcData();
|
||||
}
|
||||
|
||||
protected virtual void CalcData(UIOption o)
|
||||
protected virtual void CalcData()
|
||||
{
|
||||
}
|
||||
|
||||
@ -158,7 +158,7 @@ namespace Sunny.UI
|
||||
if (emptyOption == null)
|
||||
{
|
||||
CreateEmptyOption();
|
||||
CalcData(emptyOption);
|
||||
CalcData();
|
||||
}
|
||||
|
||||
return emptyOption;
|
||||
@ -307,8 +307,8 @@ namespace Sunny.UI
|
||||
if (legend.Top == UITopAlignment.Bottom) top = Height - totalHeight - TextInterval;
|
||||
}
|
||||
|
||||
float startleft = left;
|
||||
float starttop = top;
|
||||
float startLeft = left;
|
||||
float startTop = top;
|
||||
for (int i = 0; i < legend.DataCount; i++)
|
||||
{
|
||||
var data = legend.Data[i];
|
||||
@ -320,17 +320,17 @@ namespace Sunny.UI
|
||||
|
||||
if (legend.Orient == UIOrient.Horizontal)
|
||||
{
|
||||
g.FillRoundRectangle(color, (int)startleft, (int)top + 1, 18, (int)oneHeight - 2, 5);
|
||||
g.DrawString(data, LegendFont, color, startleft + 20, top);
|
||||
startleft += 22;
|
||||
startleft += sf.Width;
|
||||
g.FillRoundRectangle(color, (int)startLeft, (int)top + 1, 18, (int)oneHeight - 2, 5);
|
||||
g.DrawString(data, LegendFont, color, startLeft + 20, top);
|
||||
startLeft += 22;
|
||||
startLeft += sf.Width;
|
||||
}
|
||||
|
||||
if (legend.Orient == UIOrient.Vertical)
|
||||
{
|
||||
g.FillRoundRectangle(color, (int)left, (int)starttop + 1, 18, (int)oneHeight - 2, 5);
|
||||
g.DrawString(data, LegendFont, color, left + 20, starttop);
|
||||
starttop += oneHeight;
|
||||
g.FillRoundRectangle(color, (int)left, (int)startTop + 1, 18, (int)oneHeight - 2, 5);
|
||||
g.DrawString(data, LegendFont, color, left + 20, startTop);
|
||||
startTop += oneHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ using System.Windows.Forms;
|
||||
namespace Sunny.UI
|
||||
{
|
||||
[ToolboxItem(true), Description("甜甜圈图")]
|
||||
public class UIDoughnutChart : UIChart
|
||||
public sealed class UIDoughnutChart : UIChart
|
||||
{
|
||||
protected override void CreateEmptyOption()
|
||||
{
|
||||
@ -58,7 +58,7 @@ namespace Sunny.UI
|
||||
protected override void OnSizeChanged(EventArgs e)
|
||||
{
|
||||
base.OnSizeChanged(e);
|
||||
CalcData(DoughnutOption);
|
||||
CalcData();
|
||||
}
|
||||
|
||||
protected override void DrawOption(Graphics g)
|
||||
@ -69,15 +69,14 @@ namespace Sunny.UI
|
||||
DrawLegend(g, DoughnutOption.Legend);
|
||||
}
|
||||
|
||||
protected override void CalcData(UIOption option)
|
||||
protected override void CalcData()
|
||||
{
|
||||
Angles.Clear();
|
||||
UIDoughnutOption o = (UIDoughnutOption)option;
|
||||
if (o == null || o.Series == null || o.Series.Count == 0) return;
|
||||
if (DoughnutOption == null || DoughnutOption.Series == null || DoughnutOption.Series.Count == 0) return;
|
||||
|
||||
for (int pieIndex = 0; pieIndex < o.Series.Count; pieIndex++)
|
||||
for (int pieIndex = 0; pieIndex < DoughnutOption.Series.Count; pieIndex++)
|
||||
{
|
||||
var pie = o.Series[pieIndex];
|
||||
var pie = DoughnutOption.Series[pieIndex];
|
||||
Angles.TryAdd(pieIndex, new ConcurrentDictionary<int, Angle>());
|
||||
|
||||
double all = 0;
|
||||
@ -93,14 +92,14 @@ namespace Sunny.UI
|
||||
float angle = (float)(pie.Data[i].Value * 360.0f / all);
|
||||
float percent = (float)(pie.Data[i].Value * 100.0f / all);
|
||||
string text = "";
|
||||
if (o.ToolTip != null)
|
||||
if (DoughnutOption.ToolTip != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
UITemplate template = new UITemplate(o.ToolTip.Formatter);
|
||||
UITemplate template = new UITemplate(DoughnutOption.ToolTip.Formatter);
|
||||
template.Set("a", pie.Name);
|
||||
template.Set("b", pie.Data[i].Name);
|
||||
template.Set("c", pie.Data[i].Value.ToString(o.ToolTip.ValueFormat));
|
||||
template.Set("c", pie.Data[i].Value.ToString(DoughnutOption.ToolTip.ValueFormat));
|
||||
template.Set("d", percent.ToString("F2"));
|
||||
text = template.Render();
|
||||
}
|
||||
@ -113,7 +112,7 @@ namespace Sunny.UI
|
||||
|
||||
Angle pieAngle = new Angle(start, angle, text);
|
||||
GetSeriesRect(pie, ref pieAngle);
|
||||
Angles[pieIndex].AddOrUpdate(i, pieAngle);
|
||||
Angles[pieIndex].TryAddOrUpdate(i, pieAngle);
|
||||
start += angle;
|
||||
}
|
||||
}
|
||||
|
313
SunnyUI/Charts/UILineChart.cs
Normal file
313
SunnyUI/Charts/UILineChart.cs
Normal file
@ -0,0 +1,313 @@
|
||||
using Sunny.UI.Charts;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Linq;
|
||||
|
||||
namespace Sunny.UI
|
||||
{
|
||||
[ToolboxItem(true)]
|
||||
public sealed class UILineChart : UIChart
|
||||
{
|
||||
private bool NeedDraw;
|
||||
|
||||
protected override void OnSizeChanged(EventArgs e)
|
||||
{
|
||||
base.OnSizeChanged(e);
|
||||
CalcData();
|
||||
}
|
||||
|
||||
private Point DrawOrigin;
|
||||
private Size DrawSize;
|
||||
|
||||
private int YAxisStart;
|
||||
private int YAxisEnd;
|
||||
private double YAxisInterval;
|
||||
private int XAxisStart;
|
||||
private int XAxisEnd;
|
||||
private double XAxisInterval;
|
||||
|
||||
protected override void CalcData()
|
||||
{
|
||||
NeedDraw = false;
|
||||
if (LineOption == null || LineOption.Series == null || LineOption.Series.Count == 0) return;
|
||||
|
||||
DrawOrigin = new Point(LineOption.Grid.Left, Height - LineOption.Grid.Bottom);
|
||||
DrawSize = new Size(Width - LineOption.Grid.Left - LineOption.Grid.Right,
|
||||
Height - LineOption.Grid.Top - LineOption.Grid.Bottom);
|
||||
|
||||
if (DrawSize.Width <= 0 || DrawSize.Height <= 0) return;
|
||||
CalcAxises();
|
||||
|
||||
foreach (var series in LineOption.Series.Values)
|
||||
{
|
||||
series.Points.Clear();
|
||||
|
||||
for (int i = 0; i < series.XData.Count; i++)
|
||||
{
|
||||
float x = (float)(series.XData[i] - XAxisStart) * 1.0f * DrawSize.Width / (XAxisEnd - XAxisStart);
|
||||
float y = (float)(series.YData[i] - YAxisStart) * 1.0f * DrawSize.Height / (YAxisEnd - YAxisStart);
|
||||
series.Points.Add(new PointF(DrawOrigin.X + x, DrawOrigin.Y - y));
|
||||
}
|
||||
}
|
||||
|
||||
NeedDraw = true;
|
||||
}
|
||||
|
||||
private void CalcAxises()
|
||||
{
|
||||
//Y轴
|
||||
double min = double.MaxValue;
|
||||
double max = double.MinValue;
|
||||
foreach (var series in LineOption.Series.Values)
|
||||
{
|
||||
min = Math.Min(min, series.YData.Min());
|
||||
max = Math.Max(max, series.YData.Max());
|
||||
}
|
||||
|
||||
if (min > 0 && max > 0 && !LineOption.YAxis.Scale) min = 0;
|
||||
if (min < 0 && max < 0 && !LineOption.YAxis.Scale) max = 0;
|
||||
if (!LineOption.YAxis.MaxAuto) max = LineOption.YAxis.Max;
|
||||
if (!LineOption.YAxis.MinAuto) min = LineOption.YAxis.Min;
|
||||
|
||||
if ((max - min).IsZero())
|
||||
{
|
||||
max = 100;
|
||||
min = 0;
|
||||
}
|
||||
|
||||
UIChartHelper.CalcDegreeScale(min, max, LineOption.YAxis.SplitNumber,
|
||||
out int startY, out int endY, out double intervalY);
|
||||
|
||||
YAxisStart = startY;
|
||||
YAxisEnd = endY;
|
||||
YAxisInterval = intervalY;
|
||||
|
||||
//X轴
|
||||
min = double.MaxValue;
|
||||
max = double.MinValue;
|
||||
foreach (var series in LineOption.Series.Values)
|
||||
{
|
||||
min = Math.Min(min, series.XData.Min());
|
||||
max = Math.Max(max, series.XData.Max());
|
||||
}
|
||||
|
||||
if (min > 0 && max > 0 && !LineOption.XAxis.Scale) min = 0;
|
||||
if (min < 0 && max < 0 && !LineOption.XAxis.Scale) max = 0;
|
||||
if (!LineOption.XAxis.MaxAuto) max = LineOption.XAxis.Max;
|
||||
if (!LineOption.XAxis.MinAuto) min = LineOption.XAxis.Min;
|
||||
|
||||
if ((max - min).IsZero())
|
||||
{
|
||||
max = 100;
|
||||
min = 0;
|
||||
}
|
||||
|
||||
UIChartHelper.CalcDegreeScale(min, max, LineOption.XAxis.SplitNumber,
|
||||
out int startX, out int endX, out double intervalX);
|
||||
|
||||
XAxisStart = startX;
|
||||
XAxisEnd = endX;
|
||||
XAxisInterval = intervalX;
|
||||
}
|
||||
|
||||
[Browsable(false)]
|
||||
private UILineOption LineOption
|
||||
{
|
||||
get
|
||||
{
|
||||
UIOption option = Option ?? EmptyOption;
|
||||
return (UILineOption)option;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void CreateEmptyOption()
|
||||
{
|
||||
if (emptyOption != null) return;
|
||||
|
||||
UILineOption option = new UILineOption();
|
||||
option.Title = new UITitle();
|
||||
option.Title.Text = "SunnyUI";
|
||||
option.Title.SubText = "LineChart";
|
||||
|
||||
option.AddSeries(new UILineSeries("Line"));
|
||||
option.AddData("Line", 0, 1);
|
||||
option.AddData("Line", 1, 2);
|
||||
option.AddData("Line", 2, 3);
|
||||
option.AddData("Line", 3, 4);
|
||||
option.AddData("Line", 4, 3);
|
||||
option.AddData("Line", 5, 2);
|
||||
|
||||
option.XAxis.Name = "日期";
|
||||
option.YAxis.Name = "数值";
|
||||
|
||||
emptyOption = option;
|
||||
}
|
||||
|
||||
protected override void DrawOption(Graphics g)
|
||||
{
|
||||
if (LineOption == null) return;
|
||||
if (!NeedDraw) return;
|
||||
|
||||
// if (BarOption.ToolTip != null && BarOption.ToolTip.AxisPointer.Type == UIAxisPointerType.Shadow) DrawToolTip(g);
|
||||
DrawAxis(g);
|
||||
DrawTitle(g, LineOption.Title);
|
||||
DrawSeries(g);
|
||||
// if (BarOption.ToolTip != null && BarOption.ToolTip.AxisPointer.Type == UIAxisPointerType.Line) DrawToolTip(g);
|
||||
DrawLegend(g, LineOption.Legend);
|
||||
DrawAxisScales(g);
|
||||
}
|
||||
|
||||
private void DrawAxis(Graphics g)
|
||||
{
|
||||
if (YAxisStart >= 0) g.DrawLine(ChartStyle.ForeColor, DrawOrigin,
|
||||
new Point(DrawOrigin.X + DrawSize.Width, DrawOrigin.Y));
|
||||
if (YAxisEnd <= 0) g.DrawLine(ChartStyle.ForeColor, new Point(DrawOrigin.X, LineOption.Grid.Top),
|
||||
new Point(DrawOrigin.X + DrawSize.Width, LineOption.Grid.Top));
|
||||
|
||||
g.DrawLine(ChartStyle.ForeColor, DrawOrigin, new Point(DrawOrigin.X, DrawOrigin.Y - DrawSize.Height));
|
||||
|
||||
//X Tick
|
||||
if (LineOption.XAxis.AxisTick.Show)
|
||||
{
|
||||
float start = DrawOrigin.X;
|
||||
float DrawBarWidth = DrawSize.Width * 1.0f / (XAxisEnd - XAxisStart);
|
||||
for (int i = XAxisStart; i <= XAxisEnd; i++)
|
||||
{
|
||||
g.DrawLine(ChartStyle.ForeColor, start, DrawOrigin.Y, start, DrawOrigin.Y + LineOption.XAxis.AxisTick.Length);
|
||||
|
||||
if (i != 0)
|
||||
{
|
||||
using (Pen pn = new Pen(ChartStyle.ForeColor))
|
||||
{
|
||||
pn.DashStyle = DashStyle.Dash;
|
||||
pn.DashPattern = new float[] { 3, 3 };
|
||||
g.DrawLine(pn, start, DrawOrigin.Y, start, LineOption.Grid.Top);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g.DrawLine(ChartStyle.ForeColor, start, DrawOrigin.Y, start, LineOption.Grid.Top);
|
||||
}
|
||||
|
||||
start += DrawBarWidth;
|
||||
}
|
||||
}
|
||||
|
||||
//X Label
|
||||
if (LineOption.XAxis.AxisLabel.Show)
|
||||
{
|
||||
float start = DrawOrigin.X;
|
||||
float DrawBarWidth = DrawSize.Width * 1.0f / (XAxisEnd - XAxisStart);
|
||||
int idx = 0;
|
||||
float wmax = 0;
|
||||
for (int i = XAxisStart; i <= XAxisEnd; i++)
|
||||
{
|
||||
string label = LineOption.XAxis.AxisLabel.GetLabel(i * XAxisInterval, idx);
|
||||
SizeF sf = g.MeasureString(label, SubFont);
|
||||
wmax = Math.Max(wmax, sf.Width);
|
||||
g.DrawString(label, SubFont, ChartStyle.ForeColor, start - sf.Width / 2.0f,
|
||||
DrawOrigin.Y + LineOption.XAxis.AxisTick.Length);
|
||||
start += DrawBarWidth;
|
||||
}
|
||||
|
||||
SizeF sfname = g.MeasureString(LineOption.XAxis.Name, SubFont);
|
||||
g.DrawString(LineOption.XAxis.Name, SubFont, ChartStyle.ForeColor,
|
||||
DrawOrigin.X + (DrawSize.Width - sfname.Width) / 2.0f,
|
||||
DrawOrigin.Y + LineOption.XAxis.AxisTick.Length + sfname.Height);
|
||||
}
|
||||
|
||||
//Y Tick
|
||||
if (LineOption.YAxis.AxisTick.Show)
|
||||
{
|
||||
float start = DrawOrigin.Y;
|
||||
float DrawBarHeight = DrawSize.Height * 1.0f / (YAxisEnd - YAxisStart);
|
||||
for (int i = YAxisStart; i <= YAxisEnd; i++)
|
||||
{
|
||||
g.DrawLine(ChartStyle.ForeColor, DrawOrigin.X, start, DrawOrigin.X - LineOption.YAxis.AxisTick.Length, start);
|
||||
|
||||
if (i != 0)
|
||||
{
|
||||
using (Pen pn = new Pen(ChartStyle.ForeColor))
|
||||
{
|
||||
pn.DashStyle = DashStyle.Dash;
|
||||
pn.DashPattern = new float[] { 3, 3 };
|
||||
g.DrawLine(pn, DrawOrigin.X, start, Width - LineOption.Grid.Right, start);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g.DrawLine(ChartStyle.ForeColor, DrawOrigin.X, start, Width - LineOption.Grid.Right, start);
|
||||
}
|
||||
|
||||
start -= DrawBarHeight;
|
||||
}
|
||||
}
|
||||
|
||||
//Y Label
|
||||
if (LineOption.YAxis.AxisLabel.Show)
|
||||
{
|
||||
float start = DrawOrigin.Y;
|
||||
float DrawBarHeight = DrawSize.Height * 1.0f / (YAxisEnd - YAxisStart);
|
||||
int idx = 0;
|
||||
float wmax = 0;
|
||||
for (int i = YAxisStart; i <= YAxisEnd; i++)
|
||||
{
|
||||
string label = LineOption.YAxis.AxisLabel.GetLabel(i * YAxisInterval, idx);
|
||||
SizeF sf = g.MeasureString(label, SubFont);
|
||||
wmax = Math.Max(wmax, sf.Width);
|
||||
g.DrawString(label, SubFont, ChartStyle.ForeColor, DrawOrigin.X - LineOption.YAxis.AxisTick.Length - sf.Width, start - sf.Height / 2.0f);
|
||||
start -= DrawBarHeight;
|
||||
}
|
||||
|
||||
SizeF sfname = g.MeasureString(LineOption.YAxis.Name, SubFont);
|
||||
int x = (int)(DrawOrigin.X - LineOption.YAxis.AxisTick.Length - wmax - sfname.Height);
|
||||
int y = (int)(LineOption.Grid.Top + (DrawSize.Height - sfname.Width) / 2);
|
||||
g.DrawString(LineOption.YAxis.Name, SubFont, ChartStyle.ForeColor, new Point(x, y),
|
||||
new StringFormat() { Alignment = StringAlignment.Center }, 270);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawSeries(Graphics g)
|
||||
{
|
||||
int idx = 0;
|
||||
foreach (var series in LineOption.Series.Values)
|
||||
{
|
||||
Color color = series.Color;
|
||||
if (!series.CustomColor) color = ChartStyle.GetColor(idx);
|
||||
|
||||
if (series.Smooth)
|
||||
g.DrawCurve(color, series.Points.ToArray(), true);
|
||||
else
|
||||
g.DrawLines(series.Color, series.Points.ToArray(), true);
|
||||
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawAxisScales(Graphics g)
|
||||
{
|
||||
foreach (var line in LineOption.YAxisScaleLines)
|
||||
{
|
||||
double ymin = YAxisStart * YAxisInterval;
|
||||
double ymax = YAxisEnd * YAxisInterval;
|
||||
float pos = (float)((line.Value - ymin) * (Height - LineOption.Grid.Top - LineOption.Grid.Bottom) / (ymax - ymin));
|
||||
pos = (Height - LineOption.Grid.Bottom - pos);
|
||||
using (Pen pn = new Pen(line.Color, line.Size))
|
||||
{
|
||||
g.DrawLine(pn, DrawOrigin.X, pos, Width - LineOption.Grid.Right, pos);
|
||||
}
|
||||
|
||||
SizeF sf = g.MeasureString(line.Name, SubFont);
|
||||
|
||||
if (line.Left == UILeftAlignment.Left)
|
||||
g.DrawString(line.Name, SubFont, line.Color, DrawOrigin.X + 4, pos - 2 - sf.Height);
|
||||
if (line.Left == UILeftAlignment.Center)
|
||||
g.DrawString(line.Name, SubFont, line.Color, DrawOrigin.X + (Width - LineOption.Grid.Left - LineOption.Grid.Right - sf.Width) / 2, pos - 2 - sf.Height);
|
||||
if (line.Left == UILeftAlignment.Right)
|
||||
g.DrawString(line.Name, SubFont, line.Color, Width - sf.Width - 4 - LineOption.Grid.Right, pos - 2 - sf.Height);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
224
SunnyUI/Charts/UILineChartOption.cs
Normal file
224
SunnyUI/Charts/UILineChartOption.cs
Normal file
@ -0,0 +1,224 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
|
||||
namespace Sunny.UI.Charts
|
||||
{
|
||||
public sealed class UILineOption : UIOption, IDisposable
|
||||
{
|
||||
public UIAxis XAxis { get; set; } = new UIAxis(UIAxisType.Value);
|
||||
|
||||
public UIAxis YAxis { get; set; } = new UIAxis(UIAxisType.Value);
|
||||
|
||||
public UIBarToolTip ToolTip { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
public UIChartGrid Grid = new UIChartGrid();
|
||||
|
||||
public UIAxisType XAxisType { get; set; } = UIAxisType.Value;
|
||||
|
||||
public UIAxisType YAxisType { get; set; } = UIAxisType.Value;
|
||||
|
||||
public ConcurrentDictionary<string, UILineSeries> Series = new ConcurrentDictionary<string, UILineSeries>();
|
||||
|
||||
public readonly List<UIScaleLine> XAxisScaleLines = new List<UIScaleLine>();
|
||||
|
||||
public readonly List<UIScaleLine> YAxisScaleLines = new List<UIScaleLine>();
|
||||
public void AddSeries(UILineSeries series)
|
||||
{
|
||||
if (series.Name.IsNullOrEmpty()) return;
|
||||
Series.TryAdd(series.Name, series);
|
||||
}
|
||||
|
||||
public void AddData(string name, double x, double y)
|
||||
{
|
||||
if (!Series.ContainsKey(name)) return;
|
||||
Series[name].Add(x, y);
|
||||
}
|
||||
|
||||
public void AddData(string name, DateTime x, double y)
|
||||
{
|
||||
if (!Series.ContainsKey(name)) return;
|
||||
Series[name].Add(x, y);
|
||||
}
|
||||
|
||||
public void AddData(string name, string x, double y)
|
||||
{
|
||||
if (!Series.ContainsKey(name)) return;
|
||||
Series[name].Add(x, y);
|
||||
}
|
||||
|
||||
public void AddData(string name, List<double> x, List<double> y)
|
||||
{
|
||||
if (x.Count != y.Count) return;
|
||||
for (int i = 0; i < x.Count; i++)
|
||||
{
|
||||
AddData(name, x[i], y[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddData(string name, List<DateTime> x, List<double> y)
|
||||
{
|
||||
if (x.Count != y.Count) return;
|
||||
for (int i = 0; i < x.Count; i++)
|
||||
{
|
||||
AddData(name, x[i], y[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddData(string name, List<string> x, List<double> y)
|
||||
{
|
||||
if (x.Count != y.Count) return;
|
||||
for (int i = 0; i < x.Count; i++)
|
||||
{
|
||||
AddData(name, x[i], y[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddData(string name, double[] x, double[] y)
|
||||
{
|
||||
if (x.Length != y.Length) return;
|
||||
for (int i = 0; i < x.Length; i++)
|
||||
{
|
||||
AddData(name, x[i], y[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddData(string name, DateTime[] x, double[] y)
|
||||
{
|
||||
if (x.Length != y.Length) return;
|
||||
for (int i = 0; i < x.Length; i++)
|
||||
{
|
||||
AddData(name, x[i], y[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddData(string name, string[] x, double[] y)
|
||||
{
|
||||
if (x.Length != y.Length) return;
|
||||
for (int i = 0; i < x.Length; i++)
|
||||
{
|
||||
AddData(name, x[i], y[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
foreach (var series in Series.Values)
|
||||
{
|
||||
series.Clear();
|
||||
}
|
||||
|
||||
Series.Clear();
|
||||
}
|
||||
|
||||
public void Clear(string name)
|
||||
{
|
||||
if (Series.ContainsKey(name))
|
||||
{
|
||||
Series[name].Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetLabels(string[] labels)
|
||||
{
|
||||
XAxis.Clear();
|
||||
if (XAxis.Type == UIAxisType.Category)
|
||||
{
|
||||
foreach (var label in labels)
|
||||
{
|
||||
AddLabel(label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddLabel(string label)
|
||||
{
|
||||
if (XAxis.Type == UIAxisType.Category)
|
||||
{
|
||||
XAxis.Data.Add(label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class UILineSeries
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
|
||||
public float Width { get; set; } = 1;
|
||||
public Color Color { get; set; }
|
||||
|
||||
public UILinePointSymbol Symbol { get; set; } = UILinePointSymbol.None;
|
||||
public int SymbolSize { get; set; } = 1;
|
||||
|
||||
public Color SymbolColor { get; set; }
|
||||
|
||||
public bool CustomColor { get; set; }
|
||||
|
||||
public bool Smooth { get; set; }
|
||||
|
||||
public UILineSeries(string name)
|
||||
{
|
||||
Name = name;
|
||||
Color = UIColor.Blue;
|
||||
}
|
||||
|
||||
public UILineSeries(string name, Color color)
|
||||
{
|
||||
Name = name;
|
||||
Color = color;
|
||||
CustomColor = true;
|
||||
}
|
||||
|
||||
public readonly List<double> XData = new List<double>();
|
||||
|
||||
public readonly List<double> YData = new List<double>();
|
||||
|
||||
public readonly List<PointF> Points = new List<PointF>();
|
||||
|
||||
public int DataCount => XData.Count;
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
XData.Clear();
|
||||
YData.Clear();
|
||||
Points.Clear();
|
||||
}
|
||||
|
||||
public void Add(double x, double y)
|
||||
{
|
||||
XData.Add(x);
|
||||
YData.Add(y);
|
||||
}
|
||||
|
||||
public void Add(DateTime x, double y)
|
||||
{
|
||||
DateTimeInt64 t = new DateTimeInt64(x);
|
||||
XData.Add(t);
|
||||
YData.Add(y);
|
||||
}
|
||||
|
||||
public void Add(string x, double y)
|
||||
{
|
||||
int cnt = XData.Count;
|
||||
XData.Add(cnt);
|
||||
YData.Add(y);
|
||||
}
|
||||
}
|
||||
|
||||
public enum UILinePointSymbol
|
||||
{
|
||||
None,
|
||||
Square,
|
||||
Diamond,
|
||||
Triangle,
|
||||
Circle,
|
||||
Plus,
|
||||
Star
|
||||
}
|
||||
}
|
@ -96,8 +96,7 @@ namespace Sunny.UI
|
||||
{
|
||||
Value,
|
||||
Category,
|
||||
Time,
|
||||
Log
|
||||
Time
|
||||
}
|
||||
|
||||
public class UITitle
|
||||
|
@ -57,7 +57,7 @@ namespace Sunny.UI
|
||||
protected override void OnSizeChanged(EventArgs e)
|
||||
{
|
||||
base.OnSizeChanged(e);
|
||||
CalcData(PieOption);
|
||||
CalcData();
|
||||
}
|
||||
|
||||
protected override void DrawOption(Graphics g)
|
||||
@ -70,15 +70,14 @@ namespace Sunny.UI
|
||||
|
||||
private bool AllIsZero;
|
||||
|
||||
protected override void CalcData(UIOption option)
|
||||
protected override void CalcData()
|
||||
{
|
||||
Angles.Clear();
|
||||
UIPieOption o = (UIPieOption)option;
|
||||
if (o == null || o.Series == null || o.Series.Count == 0) return;
|
||||
if (PieOption == null || PieOption.Series == null || PieOption.Series.Count == 0) return;
|
||||
|
||||
for (int pieIndex = 0; pieIndex < o.Series.Count; pieIndex++)
|
||||
for (int pieIndex = 0; pieIndex < PieOption.Series.Count; pieIndex++)
|
||||
{
|
||||
var pie = o.Series[pieIndex];
|
||||
var pie = PieOption.Series[pieIndex];
|
||||
Angles.TryAdd(pieIndex, new ConcurrentDictionary<int, Angle>());
|
||||
|
||||
double all = 0;
|
||||
@ -95,14 +94,14 @@ namespace Sunny.UI
|
||||
float angle = (float)(pie.Data[i].Value * 360.0f / all);
|
||||
float percent = (float)(pie.Data[i].Value * 100.0f / all);
|
||||
string text = "";
|
||||
if (o.ToolTip != null)
|
||||
if (PieOption.ToolTip != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
UITemplate template = new UITemplate(o.ToolTip.Formatter);
|
||||
UITemplate template = new UITemplate(PieOption.ToolTip.Formatter);
|
||||
template.Set("a", pie.Name);
|
||||
template.Set("b", pie.Data[i].Name);
|
||||
template.Set("c", pie.Data[i].Value.ToString(o.ToolTip.ValueFormat));
|
||||
template.Set("c", pie.Data[i].Value.ToString(PieOption.ToolTip.ValueFormat));
|
||||
template.Set("d", percent.ToString("F2"));
|
||||
text = template.Render();
|
||||
}
|
||||
@ -113,7 +112,7 @@ namespace Sunny.UI
|
||||
}
|
||||
}
|
||||
|
||||
Angles[pieIndex].AddOrUpdate(i, new Angle(start, angle, text));
|
||||
Angles[pieIndex].TryAddOrUpdate(i, new Angle(start, angle, text));
|
||||
start += angle;
|
||||
}
|
||||
}
|
||||
@ -288,10 +287,6 @@ namespace Sunny.UI
|
||||
|
||||
private float RadiusSize(UIPieSeries series)
|
||||
{
|
||||
int left = series.Center.Left;
|
||||
int top = series.Center.Top;
|
||||
left = Width * left / 100;
|
||||
top = Height * top / 100;
|
||||
return Math.Min(Width, Height) * series.Radius / 200.0f;
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ using System.Drawing;
|
||||
|
||||
namespace Sunny.UI
|
||||
{
|
||||
public class UIPieOption : UIOption, IDisposable
|
||||
public sealed class UIPieOption : UIOption, IDisposable
|
||||
{
|
||||
public List<UIPieSeries> Series = new List<UIPieSeries>();
|
||||
|
||||
|
@ -185,17 +185,6 @@ namespace Sunny.UI
|
||||
SetBarPosition();
|
||||
}
|
||||
|
||||
private int VisibleColumnCount()
|
||||
{
|
||||
int cnt = 0;
|
||||
foreach (DataGridViewColumn column in Columns)
|
||||
{
|
||||
if (column.Visible) cnt++;
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
protected override void OnPaint(PaintEventArgs e)
|
||||
{
|
||||
base.OnPaint(e);
|
||||
@ -544,10 +533,10 @@ namespace Sunny.UI
|
||||
ClearColumns();
|
||||
}
|
||||
|
||||
// public void AddRow(params object[] values)
|
||||
// {
|
||||
// Rows.Add(values);
|
||||
// }
|
||||
public int AddRow(params object[] values)
|
||||
{
|
||||
return Rows.Add(values);
|
||||
}
|
||||
}
|
||||
|
||||
public static class UIDataGridViewHelper
|
||||
|
@ -22,9 +22,9 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Sunny.UI
|
||||
@ -624,7 +624,7 @@ namespace Sunny.UI
|
||||
{
|
||||
if (MenuHelper.GetPageIndex(node) >= 0)
|
||||
{
|
||||
AllNodes.AddOrUpdate(MenuHelper.GetPageIndex(node), node);
|
||||
AllNodes.TryAddOrUpdate(MenuHelper.GetPageIndex(node), node);
|
||||
}
|
||||
|
||||
GetAllNodes(node.Nodes);
|
||||
|
@ -1,120 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
@ -1,120 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
@ -1,120 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
@ -1,120 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
@ -317,14 +317,14 @@ namespace Sunny.UI
|
||||
//SetStyle(UIStyles.Style);
|
||||
}
|
||||
|
||||
protected override CreateParams CreateParams
|
||||
{
|
||||
get
|
||||
{
|
||||
CreateParams cp = base.CreateParams;
|
||||
cp.ExStyle |= 0x02000000;
|
||||
return cp;
|
||||
}
|
||||
}
|
||||
// protected override CreateParams CreateParams
|
||||
// {
|
||||
// get
|
||||
// {
|
||||
// CreateParams cp = base.CreateParams;
|
||||
// cp.ExStyle |= 0x02000000;
|
||||
// return cp;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
@ -126,7 +126,7 @@ namespace Sunny.UI
|
||||
/// <param name="value">值</param>
|
||||
/// <typeparam name="Key">键类型</typeparam>
|
||||
/// <typeparam name="Value">值类型</typeparam>
|
||||
public static void AddOrUpdate<Key, Value>(this ConcurrentDictionary<Key, Value> dictionary, Key key, Value value)
|
||||
public static void TryAddOrUpdate<Key, Value>(this ConcurrentDictionary<Key, Value> dictionary, Key key, Value value)
|
||||
{
|
||||
if (dictionary.ContainsKey(key))
|
||||
{
|
||||
@ -146,7 +146,7 @@ namespace Sunny.UI
|
||||
/// <param name="value">值</param>
|
||||
/// <typeparam name="Key">键类型</typeparam>
|
||||
/// <typeparam name="Value">值类型</typeparam>
|
||||
public static void AddOrUpdate<Key, Value>(this Dictionary<Key, Value> dictionary, Key key, Value value)
|
||||
public static void TryAddOrUpdate<Key, Value>(this Dictionary<Key, Value> dictionary, Key key, Value value)
|
||||
{
|
||||
if (dictionary.ContainsKey(key))
|
||||
{
|
||||
|
@ -20,7 +20,6 @@
|
||||
******************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Windows.Forms;
|
||||
@ -35,7 +34,7 @@ namespace Sunny.UI
|
||||
alpha = Math.Min(255, alpha);
|
||||
return Color.FromArgb(alpha, color);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 九宫切图背景填充,#,http://st233.com/blog.php?id=24
|
||||
/// 例如按钮是图片分成九个区域 然后只需要将四角填充到目标区域 其余的拉伸就可以了
|
||||
@ -216,6 +215,26 @@ namespace Sunny.UI
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawCurve(this Graphics g, Color color, Point[] points, bool smooth = false)
|
||||
{
|
||||
using (Pen pen = new Pen(color))
|
||||
{
|
||||
g.Smooth(smooth);
|
||||
g.DrawCurve(pen, points);
|
||||
g.Smooth(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawCurve(this Graphics g, Color color, PointF[] points, bool smooth = false)
|
||||
{
|
||||
using (Pen pen = new Pen(color))
|
||||
{
|
||||
g.Smooth(smooth);
|
||||
g.DrawCurve(pen, points);
|
||||
g.Smooth(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawLine(this Graphics g, Color color, int x1, int y1, int x2, int y2, bool smooth = false)
|
||||
{
|
||||
using (Pen pen = new Pen(color))
|
||||
|
@ -304,7 +304,7 @@ namespace Sunny.UI
|
||||
{
|
||||
if (!Forms.ContainsKey(guid))
|
||||
{
|
||||
Forms.AddOrUpdate(guid, form);
|
||||
Forms.TryAddOrUpdate(guid, form);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -320,7 +320,7 @@ namespace Sunny.UI
|
||||
{
|
||||
if (!Pages.ContainsKey(guid))
|
||||
{
|
||||
Pages.AddOrUpdate(guid, page);
|
||||
Pages.TryAddOrUpdate(guid, page);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -335,7 +335,7 @@ namespace Sunny.UI
|
||||
{
|
||||
if (!Forms.ContainsKey(form.Guid))
|
||||
{
|
||||
Forms.AddOrUpdate(form.Guid, form);
|
||||
Forms.TryAddOrUpdate(form.Guid, form);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -350,7 +350,7 @@ namespace Sunny.UI
|
||||
{
|
||||
if (!Pages.ContainsKey(page.Guid))
|
||||
{
|
||||
Pages.AddOrUpdate(page.Guid, page);
|
||||
Pages.TryAddOrUpdate(page.Guid, page);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -65,6 +65,10 @@
|
||||
<Compile Include="Charts\UIDoughnutChart.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Charts\UILineChart.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Charts\UILineChartOption.cs" />
|
||||
<Compile Include="Charts\UIOption.cs" />
|
||||
<Compile Include="Charts\UIPieChartOption.cs" />
|
||||
<Compile Include="Controls\Color\UIColorBar.cs">
|
||||
@ -503,27 +507,12 @@
|
||||
<Compile Include="Units\UXmlConfig.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Controls\DropItem\UIDateTimeItem.resx">
|
||||
<DependentUpon>UIDateTimeItem.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Forms\UIEditForm.resx">
|
||||
<DependentUpon>UIEditForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Forms\UIForm.resx">
|
||||
<DependentUpon>UIForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Forms\UILoginForm.resx">
|
||||
<DependentUpon>UILoginForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Forms\UINotifier.resx">
|
||||
<DependentUpon>UINotifier.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Font\UIFontImages.resx">
|
||||
<DependentUpon>UIFontImages.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Forms\UIStatusForm.resx">
|
||||
<DependentUpon>UIStatusForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
|
@ -32,8 +32,8 @@ namespace Sunny.UI
|
||||
|
||||
public void TryAdd(TKey1 key1, TKey2 key2)
|
||||
{
|
||||
Key1.AddOrUpdate(key1, key2);
|
||||
Key2.AddOrUpdate(key2, key1);
|
||||
Key1.TryAddOrUpdate(key1, key2);
|
||||
Key2.TryAddOrUpdate(key2, key1);
|
||||
}
|
||||
|
||||
public TKey2 this[TKey1 key1]
|
||||
|
Loading…
x
Reference in New Issue
Block a user