2013-04-02 63 views
2

我想在wpf中對氣泡排序算法進行動畫製作。對於我寫的以下代碼。代碼正在編譯。問題是,當我單擊排序按鈕時它不更新UI元素。面臨問題swapData方法。更新wpf中的UI元素

編輯:

當我點擊freezes.but我想,當我在排序按鈕,點擊它顯示線路的交換排序按鈕UI。

enter image description here

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Data; 
using System.Windows.Documents; 
using System.Windows.Input; 
using System.Windows.Media; 
using System.Windows.Media.Imaging; 
using System.Windows.Navigation; 
using System.Windows.Shapes; 
using System.Collections; 
using System.Threading; 
using System.ComponentModel; 
using System.Windows.Threading; 

namespace Sorting 
{ 

    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window 
    { 
     public struct SwapIndex 
     { 
      public int i; public int j; 
     }; 
     delegate void UIswap(int i, int j); 
     const int scale = 4; 
     const int size = 50; 
     Int32[] data = new Int32[size]; 
     bool Working = false; 
     Line[] lines = new Line[size]; 
     public MainWindow() 
     { 
      InitializeComponent(); 
      this.Loaded += new RoutedEventHandler(MainWindow_Loaded); 
     } 

     void MainWindow_Loaded(object sender, RoutedEventArgs e) 
     { 
      Draw(); 
     } 
     private void Draw() 
     { 
      canvas1.Children.Clear(); 
      for (int i = 0; i < data.Length; i++) 
      { 
       data[i] = i; 
       lines[i] = new Line() 
       { 
        X1 = 0, 
        Y1 = i * scale, 
        X2 = i * scale, 
        Y2 = i * scale, 
        StrokeThickness = 2, 
        Stroke = new SolidColorBrush(Colors.Black) 
       }; 
       canvas1.Children.Add(lines[i]); 
      } 
     } 

     private void Sort_Click(object sender, RoutedEventArgs e) 
     { 
      if (Working) return; 
      Working = true; 
      Thread T1 = new Thread(new ThreadStart(BubbleSimple)); 
      T1.Start(); 

     } 
     void BubbleSimple() 
     { 
      bool flag = false; 
      do 
      { 
       flag = false; 
       for (int i = 0; i < data.Length - 1; i++) 
       { 
        if (data[i] > data[i + 1]) 
        { 
         flag = true; 
         swapData(i, i + 1); 
        } 
       } 
      } while (flag); 
      Working = false; 
     } 

     private void swapData(int i, int j) 
     { 

      UIswap swap = (i1, j1) => 
       { 
        double temp; 
        temp = lines[i1].X2; 
        lines[i1].X2 = lines[j1].X2; 
        lines[j1].X2 = temp; 
       }; 
      canvas1.Dispatcher.BeginInvoke(swap, new object[] { i, j }); 
     } 
     void Randomize(object sender, DoWorkEventArgs e) 
     { 
      BackgroundWorker bw = (BackgroundWorker)sender; 
      Random R = new Random(); 
      for (int i = 0; i < data.Length; i++) 
      { 
       int j = R.Next(data.Length); 
       bw.ReportProgress(1, new SwapIndex() { i = i, j = j }); 
      } 
     } 
     void SwapLine(object sender, ProgressChangedEventArgs e) 
     { 
      int i = ((SwapIndex)e.UserState).i; 
      int j = ((SwapIndex)e.UserState).j; 
      int t = data[i]; 
      data[i] = data[j]; 
      data[j] = t; 

      double temp; 
      temp = lines[i].X2; 
      lines[i].X2 = lines[j].X2; 
      lines[j].X2 = temp; 
     } 
     private void Suffle_Click(object sender, RoutedEventArgs e) 
     { 
      if (Working) return; 
      Working = true; 
      BackgroundWorker bw = new BackgroundWorker(); 
      bw.WorkerReportsProgress = true; 
      bw.WorkerSupportsCancellation = false; 
      bw.DoWork += new DoWorkEventHandler(Randomize); 
      bw.ProgressChanged += new ProgressChangedEventHandler(SwapLine); 
      bw.RunWorkerCompleted += delegate(object s1, RunWorkerCompletedEventArgs e1) 
      { 
       Working = false; 
      }; 
      bw.RunWorkerAsync(); 
     } 

    } 
} 
+0

您需要告訴我們*爲什麼*它不能正常工作。你有例外嗎?代碼是否運行?點擊「排序按鈕」時執行什麼方法?否則,每個人都必須複製代碼並運行它才能找出結果。 – Patrick

+0

@帕特里克:沒有例外它凍結了UI –

+1

老兄,我不知道'冒泡排序'是什麼。但是你的代碼是完全錯誤的。 UI不是數據。發佈你需要的截圖,我可以告訴你在WPF中完成它的正確方法。 –

回答

2

正如其他人所提到的數據綁定的效果會更好,但要告訴你,你有沒有完全重新編寫代碼,這是我想出了問題:

public partial class MainWindow : Window 
{ 
    public struct SwapIndex 
    { 
     public int i; public int j; 
    }; 
    delegate void UIswap(int i, int j); 
    const int scale = 4; 
    const int size = 50; 
    Int32[] data = new Int32[size]; 
    bool Working = false; 
    Line[] lines = new Line[size]; 
    public MainWindow() 
    { 
     InitializeComponent(); 
     this.Loaded += new RoutedEventHandler(MainWindow_Loaded); 
    } 

    void MainWindow_Loaded(object sender, RoutedEventArgs e) 
    { 
     Draw(); 
    } 
    private void Draw() 
    { 
     canvas1.Children.Clear(); 
     for (int i = 0; i < data.Length; i++) 
     { 
      data[i] = i; 
      lines[i] = new Line() 
      { 
       X1 = 0, 
       Y1 = i * scale, 
       X2 = i * scale, 
       Y2 = i * scale, 
       StrokeThickness = 2, 
       Stroke = new SolidColorBrush(Colors.Black) 
      }; 
      canvas1.Children.Add(lines[i]); 
     } 
    } 

    private void Sort_Click(object sender, RoutedEventArgs e) 
    { 
     if (Working) return; 
     Working = true; 
     Thread T1 = new Thread(new ThreadStart(BubbleSimple)); 
     T1.Start(); 

    } 
    void BubbleSimple() 
    { 
     bool flag = false; 
     do 
     { 
      flag = false; 
      for (int i = 0; i < data.Length - 1; i++) 
      { 
       if (data[i] > data[i + 1]) 
       { 
        flag = true; 
        swapData(i, i + 1); 
       } 

       Thread.Sleep(10); 
      } 
     } while (flag); 
     Working = false; 
    } 

    private void swapData(int i, int j) 
    { 
     var temp = data[i]; 
     data[i] = data[j]; 
     data[j] = temp; 

     UIswap swap = (i1, j1) => 
     { 
      var tempd = lines[i1].X2; 
      lines[i1].X2 = lines[j1].X2; 
      lines[j1].X2 = tempd; 
     }; 

     canvas1.Dispatcher.BeginInvoke(swap, new object[] { i, j }); 
    } 

    void Randomize(object sender, DoWorkEventArgs e) 
    { 
     BackgroundWorker bw = (BackgroundWorker)sender; 
     Random R = new Random(); 
     for (int i = 0; i < data.Length; i++) 
     { 
      int j = R.Next(data.Length); 
      bw.ReportProgress(1, new SwapIndex() { i = i, j = j }); 
     } 
    } 
    void SwapLine(object sender, ProgressChangedEventArgs e) 
    { 
     int i = ((SwapIndex)e.UserState).i; 
     int j = ((SwapIndex)e.UserState).j; 
     int t = data[i]; 
     data[i] = data[j]; 
     data[j] = t; 

     double temp; 
     temp = lines[i].X2; 
     lines[i].X2 = lines[j].X2; 
     lines[j].X2 = temp; 
    } 
    private void Suffle_Click(object sender, RoutedEventArgs e) 
    { 
     if (Working) return; 
     Working = true; 
     BackgroundWorker bw = new BackgroundWorker(); 
     bw.WorkerReportsProgress = true; 
     bw.WorkerSupportsCancellation = false; 
     bw.DoWork += new DoWorkEventHandler(Randomize); 
     bw.ProgressChanged += new ProgressChangedEventHandler(SwapLine); 
     bw.RunWorkerCompleted += delegate(object s1, RunWorkerCompletedEventArgs e1) 
     { 
      Working = false; 
     }; 
     bw.RunWorkerAsync(); 
    } 
} 

你最大的問題是算法是交換行,但不交換數據,因此它處於無限循環。

1

您需要了解的UI在WPF的處理方式,這是一種叫做MVVM:

<Window x:Class="MiscSamples.WrongCode" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="WrongCode" Height="300" Width="300"> 
    <DockPanel> 
     <Button Click="Sort" Content="Sort" DockPanel.Dock="Top"/> 
     <Button Click="Shuffle" Content="Shuffle" DockPanel.Dock="Top"/> 
     <ItemsControl ItemsSource="{Binding}"> 
      <ItemsControl.ItemTemplate> 
       <DataTemplate> 
        <Line X1="0" Y1="0" X2="{Binding Length}" Y2="0" 
          Stroke="Black" StrokeThickness="2" Margin="0,2,0,2"/> 
       </DataTemplate> 
      </ItemsControl.ItemTemplate> 
     </ItemsControl> 
    </DockPanel> 
</Window> 

代碼背後:

public partial class WrongCode : Window 
    { 
     public ObservableCollection<LineModel> Lines { get; set; } 

     public WrongCode() 
     { 
      InitializeComponent(); 
      Lines = new ObservableCollection<LineModel>(); 
      DataContext = Lines; 
     } 

     private void Sort(object sender, RoutedEventArgs e) 
     { 
      SortTimer = new Timer(x => SortItem(), null, 0, 100); 
     } 

     private void SortItem() 
     { 
      //Implement your sort algorithm here by 
      //Modifying the ObservableCollection in this way: 
      //Lines.Move(index1, index2); 

      //This example is just moving the lines randomly without any sort order 
      var index1 = rnd.Next(0, Lines.Count - 1); 
      var index2 = rnd.Next(0, Lines.Count - 1); 

      Dispatcher.BeginInvoke((Action) (() => Lines.Move(index1, index2))); 
     } 

     public static System.Threading.Timer SortTimer; 
     public static Random rnd = new Random(); 

     private void Shuffle(object sender, RoutedEventArgs e) 
     { 
      if (SortTimer != null) 
       SortTimer.Dispose(); 

      Lines.Clear(); 

      Enumerable.Range(0, rnd.Next(50, 60)) 
         .Select(x => new LineModel() 
          { 
           Length = rnd.Next(1, 100) 
          }) 
         .ToList() 
         .ForEach(Lines.Add); 

     } 
    } 

    public class LineModel 
    { 
     public int Length { get; set; } 
    } 

結果:

enter image description here

要點這裏要注意:

  • 我使用的是ItemsControl「畫」在屏幕上的項目。當您需要在WPF中顯示多個Items時,這是正確的方法,無論這些項目是什麼。實際上,在WPF中,能夠顯示多個項目的所有UI元素(例如ListBox,ComboBox,Menu等)都源自ItemsControl
  • 我在NO WAY操作代碼中的UI元素。 WPF中的大部分時間完全沒有必要。再次,UI is Not Data. Data is Data. UI is UI.,因此您不能將UI元素視爲數據,因爲它們不是。
  • 請注意,我正在使用ObservableCollection<T>來存儲這些項目。這是一種特殊類型的集合,可以在物品自身添加/移除/移動時通知。 WPF綁定框架監聽這些事件並自動更新UI。
+0

MVVM是一個可以在WPF中實現的模型,它不再是WPF,而是ObjectFactory是C#。 –

+0

@LouLouviere是的,我傾向於誇大其詞,因爲當這些人嘗試在WPF中「winforms」時,我經常會看到這樣的可怕事情。 –

4

要回答這個問題,我們必須要回去WPF 101

你想做數據綁定你的「行」,而不是所有的工作,你在這裏做。這不是winforms。你綁定在WPF中,綁定確實爲你工作。

首先,您需要使用數據對象而不是行。

public class DataLine 
{ 
    private const double _Scale = 4.0; 
    public double Length { get; set; } 
    public double DisplayLength { get { return Length * _Scale; } } 
} 

然後一個ObservableCollection添加到您的窗口類

public ObservableCollection<DataLine> _Data = new ObservableCollection<DataLine>(); 
public ObservableCollection<DataLine> Data 
{ 
    get { return _Data; } 
} 

然後你就會綁定的ItemsSource在你的窗口的XAML控制

<ItemsControl Grid.Row="2" ItemsSource="{Binding Data}"/> 

然後添加一個DataTemplate

<ItemsControl Grid.Row="2" ItemsSource="{Binding Data}"> 
    <ItemsControl.Resources> 
     <DataTemplate DataType="{x:Type local:DataLine}"> 
      <Line X1="0" X2="{Binding DisplayLength}" Y1="{Binding DisplayLength}" Y2="{Binding Length}"/> 
     </DataTemplate> 
    </ItemsControl.Resources> 
</ItemsControl> 

那管理用戶界面。現在,您只需重新排列班級中的Data數組就可以重新排列線路。

private void swapData(int i, int j) 
{ 
    int nMax = Math.Max(i, j); 
    int nMin = Math.Min(i, j); 

    DataLine tempMax = Data[nMax]; 
    DataLine tempMin = Data[nMin]; 


    Action swap =() => 
    { 
     Data.RemoveAt(nMax); 
     Data.RemoveAt(nMin); 
     Data.Insert(nMin, tempMax); 
     Data.Insert(nMax, tempMin); 
    }; 

    Dispatcher.Invoke(swap, null); 
} 

現在只放了等待每個交換之間,做你的排序在一個單獨的線程。

+0

感謝您的新想法:) –