簡體   English   中英

更新wpf中的UI元素

[英]Update UI element in wpf

我試圖在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();
        }

    }
}

要回答這個問題,我們必須回到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綁定到Window的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);
}

現在,只需在每個交換之間進行等待,然后在單獨的線程中進行排序即可。

正如其他人提到的那樣,數據綁定會更好,但是為了向您展示您出了問題的地方而又沒有完全重新編寫代碼,這就是我想到的:

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

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

您需要了解在WPF中處理UI的方式,這稱為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; }
    }

結果:

在此輸入圖像描述

這里要注意的重要事項:

  • 我正在使用ItemsControl來“繪制”屏幕上的項目。 當你需要在WPF中顯示多個Items時,這是正確的方法,無論這些項目是什么。 實際上,在WPF中,所有能夠顯示多個項目的UI元素(例如ListBoxComboBoxMenu等)都是從ItemsControl派生的。
  • 我沒有在代碼中操縱UI元素 在WPF中,大多數時候這完全是不必要的。 同樣, UI不是數據。 數據是數據。 UI是UI。 因此,您不能將UI元素視為數據,因為它們不是。
  • 請注意,我正在使用ObservableCollection<T>來存儲這些項目。 這是一種特殊類型的集合,可以在項目本身添加/刪除/移動時通知。 WPF綁定框架偵聽這些事件並自動更新UI。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM