[英]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元素(例如ListBox
, ComboBox
, Menu
等)都是从ItemsControl
派生的。 ObservableCollection<T>
来存储这些项目。 这是一种特殊类型的集合,可以在项目本身添加/删除/移动时通知。 WPF绑定框架侦听这些事件并自动更新UI。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.