我是一个初学者,正在做一些C#练习。我了解了森林火灾模型,并尝试使用WPF进行此操作,并且为了进行绘图,我通过为每个像素创建一个矩形来使用画布。 我得到的问题是程序冻结,并且画布不绘制任何内容(使用while(true)循环)。另外,我将在迭代后删除所有子项,但程序仍在收集GB的RAM。
测试的简化代码:
public partial class TestDrawing : Window
{
public TestDrawing()
{
InitializeComponent();
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
DrawForestFire();
}
private void DrawForestFire()
{
Random rand = new Random();
while (true)
{
for (int y = 0; y < 100; y++)
{
for (int x = 0; x < 100; x++)
{
Rectangle rectangle = new Rectangle();
Color color = Color.FromRgb((byte)rand.Next(200),
(byte)rand.Next(200), (byte)rand.Next(200));
rectangle.Fill = new SolidColorBrush(color);
rectangle.Width = 4;
rectangle.Height = 4;
Canvas.SetTop(rectangle, y * 4);
Canvas.SetLeft(rectangle, x * 4);
canvas.Children.Add(rectangle);
}
}
canvas.Children.Clear();
}
}
}
我还尝试在线程中绘制“ DrawForestFire()”,并将画布对象放入“ this.Dispatcher.Invoke(()=> {...});”中。但这对我没有任何影响。怎么了?
而且,对于这种操作,还有什么比Canvas更好的东西吗?
答案 0 :(得分:4)
与其在画布上添加10000个Rectangle元素,不如将其绘制到单个WriteableBitmap
中。
在XAML中声明一个Image
元素
<Image x:Name="image"/>
并将一个WriteableBitmap分配给它的Source属性。然后使用DispatcherTimer更新位图像素:
public partial class MainWindow : Window
{
private const int width = 100;
private const int height = 100;
private readonly Random random = new Random();
private readonly byte[] buffer = new byte[3 * width * height];
private readonly WriteableBitmap bitmap =
new WriteableBitmap(width, height, 96, 96, PixelFormats.Bgr24, null);
public MainWindow()
{
InitializeComponent();
image.Source = bitmap;
var timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(50) };
timer.Tick += OnTimerTick;
timer.Start();
}
private void UpdateBuffer()
{
for (var y = 0; y < height; y++)
{
for (var x = 0; x < width; x++)
{
var i = 3 * (width * y + x);
buffer[i++] = (byte)random.Next(200);
buffer[i++] = (byte)random.Next(200);
buffer[i++] = (byte)random.Next(200);
}
}
}
private async void OnTimerTick(object sender, EventArgs e)
{
await Task.Run(() => UpdateBuffer());
bitmap.WritePixels(new Int32Rect(0, 0, width, height), buffer, 3 * width, 0);
}
}
答案 1 :(得分:1)
只是为了好玩,这是一个有效的森林火灾实施方案。我喜欢玩自燃和新树概率。
public partial class MainWindow : Window
{
private enum CellState
{
Empty, Tree, Burning
}
private const int width = 400;
private const int height = 400;
private readonly WriteableBitmap bitmap =
new WriteableBitmap(width, height, 96, 96, PixelFormats.Bgr24, null);
private readonly byte[] buffer = new byte[3 * width * height];
private readonly Random random = new Random();
private readonly Dictionary<CellState, Color> stateColors =
new Dictionary<CellState, Color>
{
{ CellState.Empty, Colors.Black },
{ CellState.Tree, Colors.Green },
{ CellState.Burning, Colors.Yellow }
};
private CellState[,] cells = new CellState[height, width];
private double ignitionProbability = 0.0001;
private double newTreeProbability = 0.01;
public MainWindow()
{
InitializeComponent();
image.Source = bitmap;
var timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(50) };
timer.Tick += OnTimerTick;
timer.Start();
}
private async void OnTimerTick(object sender, EventArgs e)
{
await Task.Run(() => UpdateCells());
bitmap.WritePixels(new Int32Rect(0, 0, width, height), buffer, 3 * width, 0);
}
private bool IsBurning(int y, int x)
{
return x >= 0 && x < width && y >= 0 && y < height
&& cells[y, x] == CellState.Burning;
}
private bool StartsBurning(int y, int x)
{
return IsBurning(y - 1, x - 1)
|| IsBurning(y - 1, x)
|| IsBurning(y - 1, x + 1)
|| IsBurning(y, x - 1)
|| IsBurning(y, x + 1)
|| IsBurning(y + 1, x - 1)
|| IsBurning(y + 1, x)
|| IsBurning(y + 1, x + 1)
|| random.NextDouble() <= ignitionProbability;
}
private CellState GetNewState(int y, int x)
{
var state = cells[y, x];
switch (state)
{
case CellState.Burning:
state = CellState.Empty;
break;
case CellState.Empty:
if (random.NextDouble() <= newTreeProbability)
{
state = CellState.Tree;
}
break;
case CellState.Tree:
if (StartsBurning(y, x))
{
state = CellState.Burning;
}
break;
}
return state;
}
private void UpdateCells()
{
var newCells = new CellState[height, width];
for (var y = 0; y < height; y++)
{
for (var x = 0; x < width; x++)
{
newCells[y, x] = GetNewState(y, x);
var color = stateColors[newCells[y, x]];
var i = 3 * (width * y + x);
buffer[i++] = color.B;
buffer[i++] = color.G;
buffer[i++] = color.R;
}
}
cells = newCells;
}
}