[英]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.