2015-10-13 1 views
1

У меня есть приложение WPF, написанное на C#. На этом у меня есть элемент холста, который я хотел бы создать сетку кнопок, каждая кнопка будет иметь одинаковую ширину и высоту и располагаться бок о бок без интервала.Самый быстрый способ генерировать неизвестное количество кнопок во время выполнения без замораживания приложения?

Я пробовал несколько вещей, первое, что я сделал, было вложено для циклов в методе конструктора. Это заперло окно до тех пор, пока оно не было сделано с помощью кнопок, но было довольно быстро для сетки 100x100. Затем я переместил его на событие Loaded, что позволило открыть окно сразу, но все же заблокировало его до тех пор, пока не будут созданы все кнопки.

Затем я попытался установить таймер и Dispatcher на элементы пользовательского интерфейса, которые я хотел изменить, но это оказалось очень slow.

Вот моя текущая попытка на этом, она работает, и она не запирает окно пользовательского интерфейса, но оно все еще отвратительно медленное.

public partial class MainWindow : Window 
{ 
    private DispatcherTimer timer; 
    private int width = 100; 
    private int height = 100; 
    private int toDo = 0; 
    private int done = 0; 
    private int x = 0; 
    private int y = 0; 

    public MainWindow() 
    { 
     toDo = width * height; 
     InitializeComponent(); 
     Loaded += MainWindow_Loaded; 
    } 

    private void MainWindow_Loaded(object sender, RoutedEventArgs e) 
    { 

     //TimerCallback callback = generateButtons; 
     //timer = new Timer(callback, null, 1, 1); 
     DispatcherTimer timer = new DispatcherTimer(); 
     timer.Interval = new TimeSpan(0,0,0,0,1); 
     timer.Tick += generateButtons; 
     timer.IsEnabled = true; 
    } 

    public void generateButtons(object sender, EventArgs e) 
    { 
     if(x < width){ 
      if(y < height) 
      { 
       Button tileButton = new Button(); 
       tileButton.Content = "Blank"; 
       tileButton.Name = "Tile_" + x.ToString() + "_" + y.ToString(); 
       tileButton.Width = 15; 
       tileButton.Height = 15; 
       Canvas.SetLeft(tileButton, x*15); 
       Canvas.SetTop(tileButton, y*15); 
       this.world.Children.Add(tileButton); 
       done++; 
      } 
      x++; 
     } 
     else 
     { 
      x = 0; 
      y++; 
     } 
     this.progress.Value = ((float)done/(float)toDo)*100; 
    } 
} 

Есть ли более быстрый способ делать то, что я делаю? Думая об этом, напишите это, было бы проще создать элемент Canvas в коде, заполнить его кнопками, а затем добавить все это в основную сетку окон? Я понятия не имею, с чего начать это делать, или, если это будет эффективно, улучшите производительность.

Помните, что это для доказательства концепции (поэтому минимально жизнеспособного продукта), оно не обязательно должно быть изящным решением, оно просто должно быть быстрым.

+0

Сделайте несколько заставки, которые ждут, пока все «кнопки» не будут загружены в фоновом режиме? – kevintjuh93

+0

Асинхронный подход позволил бы сохранить восприимчивость холста, поддерживая скорость. – Mathias

+0

@ Mathias - это не то, что использует 'DispatcherTimer'? Или есть какой-то другой метод для этого? –

ответ

0

В приведенном ниже коде я проиллюстрировал, как это можно сделать с помощью асинхронного подхода, который позволяет окну оставаться отзывчивым, в то время как скорость не резко уменьшается, как с результатами DispatchTimer.

Это было проверено и работает, как ожидалось:

public partial class MainWindow : Window 
{ 
    private int width; 
    private int height; 
    private int toDo = 0; 
    private int done = 0; 
    private int x = 0; 
    private int y = 0; 

    public MainWindow() 
    { 
     InitializeComponent(); 
    } 

    // Assigned to the canvas' "Loaded" attribute. 
    private async void World_OnLoaded(object sender, RoutedEventArgs e) 
    { 
     width = (int)this.world.ActualWidth; 
     height = (int)this.world.ActualHeight; 
     toDo = width * height; 
     // awaits a newly started Thread with the method StartLoopAsync 
     await Task.Factory.StartNew(() => StartLoopAsync()); 
    } 

    // Iterates the canvas width and height based on the hardcoded "15" values. 
    private async Task StartLoopAsync() 
    { 
     for (int i = y; i < height; i += 15) 
     { 
      // Use Dispatcher BeginInvoke, as we are updating the UI elements from a different thread 
      // other than the main thread. 
      for (int j = x; j < width; j += 15) 
      { 
       await Dispatcher.BeginInvoke(new Action(() => GenerateButton(i, j))); 
      } 
     } 
    } 

    // Generates the button. 
    private void GenerateButton(int i, int j) 
    { 
     Button tileButton = new Button(); 
     tileButton.Content = "Blank"; 
     tileButton.Name = "Tile_" + x.ToString() + "_" + y.ToString(); 
     tileButton.Width = 15; 
     tileButton.Height = 15; 
     Canvas.SetLeft(tileButton, j); 
     Canvas.SetTop(tileButton, i); 
     this.world.Children.Add(tileButton); 
     //this.progress.Value = ((float)done/(float)toDo) * 100; 
    } 
} 

Изменения от собственного проекта:

  • код, который был выполнен в Loaded, была перенесена на холст Loaded методом.
  • определения веса и роста создаются в методе нагруженного холста
  • новый поток назначаются на петле, который перебирает все buttoncreations
  • Dispatcher.BeginInvoke() используется для изменения элементов пользовательского интерфейса в Главная тема.
+0

Ширина и высота, которые должны быть определены в коде, id фактически уже сделал мой «OnLoad» противоположным тому, что делает ваш, под которым я подразумеваю, что он устанавливает ширину 'width' /' height' на основе 'width * 15 '/' height * 15', кроме этого второстепенного значения, я буду реализовывать это и видеть, как это происходит. Выглядит намного лучше, я никогда раньше не видел такой реализации.* (sidenote: волшебная константа '15', плавающая по всему коду, будет изменена на параметр в более поздней точке, а также будут определяться количество' width' и 'height',' width' и 'height' кнопок в строке/столбце) * –

+0

Как вы сами сказали «Доказательство концепции» :-). Концепция асинхронного подхода и запуск нового потока - вот что важно в этом примере. – Mathias

+0

Действительно, спасибо. –

Смежные вопросы