2

wpf でバブルソートアルゴリズムをアニメーション化しようとしています。同じために、次のコードを書きました。コードはコンパイル中です。問題は、ソートボタンをクリックしたときに UI 要素が更新されないことです。swapDataメソッドで問題に直面しています。

編集:

並べ替えボタンをクリックするとUIがフリーズしますが、並べ替えボタンをクリックすると、行の交換が表示されます。

ここに画像の説明を入力

   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();
        }

    }
}
4

3 に答える 3

4

他の人が述べているように、データ バインディングの方が優れていますが、コードを完全に書き直さずにどこが間違っているかを示すために、これが私が思いついたものです。

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();
    }
}

あなたの最大の問題は、アルゴリズムが行を交換していたが、データを交換していなかったため、無限ループに陥っていたことです。

于 2013-04-02T20:48:54.780 に答える
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; }
}

次に、ウィンドウの xaml のコントロールに ItemsSource をバインドします。

<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>

それがUIを支配します。行を再配置するために必要なことは、クラス内の 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);
}

次に、各スワップの間に待機を置き、別のスレッドで並べ替えを行います。

于 2013-04-02T20:42:23.713 に答える
1

MVVM と呼ばれる WPF での UI の処理方法を理解する必要があります。

<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; }
    }

結果:

ここに画像の説明を入力

ここで注意すべき重要な点:

  • ItemsControl画面上にアイテムを「描画」するためにを使用しています。Itemsこれらの項目が何であっても、WPF で複数を表示する必要がある場合は、これが正しいアプローチです。実際、WPF では、複数のアイテム ( 、 、 など) を表示できるすべての UI 要素ListBoxComboBoxからMenu派生していItemsControlます。
  • コード内の UI 要素を操作するつもりはまったくありません。ほとんどの場合、これは WPF では完全に不要です。繰り返しますが、UI はデータではありません。データはデータです。UIはUIです。したがって、UI 要素をデータであるかのように扱ってはなりません。
  • ObservableCollection<T>これらのアイテムを格納するために を使用していることに注意してください。これは、アイテムが追加/削除/移動されるたびに通知する特別なタイプのコレクションです。WPF バインディング フレームワークはこれらのイベントをリッスンし、UI を自動的に更新します。
于 2013-04-02T20:39:00.217 に答える