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