我正在开发一个影院预订软件。我使用的是Windows Forms,座位由一个二维数组表示。我按下按钮如下:
public void DrawSeats()
{
// pnl_seats is a Panel
pnl_seats.Controls.Clear();
// Here I store all Buttons instance, to later add all buttons in one call (AddRange) to the Panel
var btns = new List<Control>();
// Suspend layout to avoid undesired Redraw/Refresh
this.SuspendLayout();
for (int y = 0; y < _seatZone.VerticalSize; y++)
{
for (int x = 0; x < _seatZone.HorizontalSize; x++)
{
// Check if this seat exists
if (IsException(x, y))
continue;
// Construct the button with desired properties. SeatSize is a common value for every button
var btn = new Button
{
Width = SeatSize,
Height = SeatSize,
Left = (x * SeatSize),
Top = (y * SeatSize),
Text = y + "" + x,
Tag = y + ";" + x, // When the button clicks, the purpose of this is to remember which seat this button is.
Font = new Font(new FontFamily("Microsoft Sans Serif"), 6.5f)
};
// Check if it is already reserved
if (ExistsReservation(x, y))
btn.Enabled = false;
else
btn.Click += btn_seat_Click; // Add click event
btns.Add(btn);
}
}
// As said before, add all buttons in one call
pnl_seats.Controls.AddRange(btns.ToArray());
// Resume the layout
this.ResumeLayout();
}
但是已经有一个20乘20(400个按钮)的座位区,它花了差不多1分钟来绘制它,并且在调试中我检查了缺乏性能,是按钮的实例化。
有一种方法可以让它更快吗?也许在instatiation期间禁用所有事件或者另一个具有Click事件的轻量级Control?
更新:
lbl
是一项测试,正确的是btn
,抱歉。
更新2:
以下是IsException
和ExistsReservations
方法:
private bool IsException(int x, int y)
{
for (var k = 0; k < _seatsExceptions.GetLength(0); k++)
if (_seatsExceptions[k, 0] == x && _seatsExceptions[k, 1] == y)
return true;
return false;
}
private bool ExistsReservation(int x, int y)
{
for (var k = 0; k < _seatsReservations.GetLength(0); k++)
if (_seatsReservations[k, 0] == x && _seatsReservations[k, 1] == y)
return true;
return false;
}
答案 0 :(得分:3)
假设您将预订和排除的数组更改为
public List<string> _seatsExceptions = new List<string>();
public List<string> _seatsReservations = new List<string>();
您可以在列表中添加排除和预订,例如
_seatsExceptions.Add("1;10");
_seatsExceptions.Add("4;19");
_seatsReservations.Add("2;5");
_seatsReservations.Add("5;5");
您的排除和预订检查可以更改为
bool IsException(int x, int y)
{
string key = x.ToString() + ";" + y.ToString();
return _seatsExceptions.Contains(key);
}
bool ExistsReservation(int x, int y)
{
string key = x.ToString() + ";" + y.ToString();
return _seatsReservations.Contains(key);
}
当然,我不知道您是否能够在您的计划中进行此更改。但请考虑迟早更改阵列上的搜索。
编辑我做了一些测试,虽然20x20按钮的虚拟网格工作得很好(平均31毫秒0.775毫秒),但更大的按钮明显减慢。在200x50时,时间跳跃到10秒(平均为1,0675)。所以也许需要一种不同的方法。绑定的DataGridView可能是一个更简单的解决方案,并且相对容易处理。
答案 1 :(得分:3)
我也不会使用这么多无数的控件来实现这样的功能。相反,您应该创建自己的UserControl,它将所有座位绘制为图像并对点击事件做出反应。
为了让你更容易一点,我创建了一个简单的UserControl,它将绘制所有座位并在鼠标单击时做出反应以更改状态。这是:
public enum SeatState
{
Empty,
Selected,
Full
}
public partial class Seats : UserControl
{
private int _Columns;
private int _Rows;
private List<List<SeatState>> _SeatStates;
public Seats()
{
InitializeComponent();
this.DoubleBuffered = true;
_SeatStates = new List<List<SeatState>>();
_Rows = 40;
_Columns = 40;
ReDimSeatStates();
MouseUp += OnMouseUp;
Paint += OnPaint;
Resize += OnResize;
}
public int Columns
{
get { return _Columns; }
set
{
_Columns = Math.Min(1, value);
ReDimSeatStates();
}
}
public int Rows
{
get { return _Rows; }
set
{
_Rows = Math.Min(1, value);
ReDimSeatStates();
}
}
private Image GetPictureForSeat(int row, int column)
{
var seatState = _SeatStates[row][column];
switch (seatState)
{
case SeatState.Empty:
return Properties.Resources.emptySeat;
case SeatState.Selected:
return Properties.Resources.choosenSeat;
default:
case SeatState.Full:
return Properties.Resources.fullSeat;
}
}
private void OnMouseUp(object sender, MouseEventArgs e)
{
var heightPerSeat = Height / (float)Rows;
var widthPerSeat = Width / (float)Columns;
var row = (int)(e.X / widthPerSeat);
var column = (int)(e.Y / heightPerSeat);
var seatState = _SeatStates[row][column];
switch (seatState)
{
case SeatState.Empty:
_SeatStates[row][column] = SeatState.Selected;
break;
case SeatState.Selected:
_SeatStates[row][column] = SeatState.Empty;
break;
}
Invalidate();
}
private void OnPaint(object sender, PaintEventArgs e)
{
var heightPerSeat = Height / (float)Rows;
var widthPerSeat = Width / (float)Columns;
e.Graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
for (int row = 0; row < Rows; row++)
{
for (int column = 0; column < Columns; column++)
{
var seatImage = GetPictureForSeat(row, column);
e.Graphics.DrawImage(seatImage, row * widthPerSeat, column * heightPerSeat, widthPerSeat, heightPerSeat);
}
}
}
private void OnResize(object sender, System.EventArgs e)
{
Invalidate();
}
private void ReDimSeatStates()
{
while (_SeatStates.Count < Rows)
_SeatStates.Add(new List<SeatState>());
if (_SeatStates.First().Count < Columns)
foreach (var columnList in _SeatStates)
while (columnList.Count < Columns)
columnList.Add(SeatState.Empty);
while (_SeatStates.Count > Rows)
_SeatStates.RemoveAt(_SeatStates.Count - 1);
if (_SeatStates.First().Count > Columns)
foreach (var columnList in _SeatStates)
while (columnList.Count > Columns)
columnList.RemoveAt(columnList.Count - 1);
}
}
目前这将绘制40行和列(因此有800个座位),您可以点击每个座位来更改其状态。
以下是我使用的图片:
如果您锚定此控件并调整其大小,或者您单击一个座位以更改其状态,如果您进一步增加行数或列数,可能会有一些轻微的重新绘制,但这仍然远低于一秒。如果这仍然伤害你,你必须改进paint方法,也许检查paint事件的ClipRectangle属性,只绘制真正需要的部分,但这是另一个故事。
答案 2 :(得分:1)
不是使用实际的按钮控件,只需绘制座位的图像,然后当用户点击座位时,翻译鼠标X,Y坐标以确定单击了哪个座位。这样会更有效率。当然,缺点是您必须编写将x,y坐标转换为座位的方法,但这确实不是那么困难。
答案 3 :(得分:0)
修改;有人指出,这在Windows窗体中不起作用!
嗯,你正在顺序完成它。 如果一次迭代花费1秒,则整个过程将花费400 * 1的时间。
也许您应该尝试制作对象的集合,并对其进行并行处理。
尝试.Net框架(4及以上版本)&#39;并行foreach&#39;方法: http://msdn.microsoft.com/en-s/library/system.threading.tasks.parallel.foreach(v=vs.110).aspx
编辑:所以,如果你有一个列表按钮名,你可以说
buttonNames.ForEach(x=>CreateButton(x));
,而您的CreateButton()方法如下:
private Button CreateButton(string nameOfButton){Button b = new 按钮(); b.Text = nameOfButton; //做你想做的... 返回b; }