您将需要在不是 UI 线程的线程上更新文本框。
Thread chatRefreshTimer;
void StartChat()
{
chatRefreshTimer = new Thread(new ThreadStart(ChatRefresh));
chatRefreshTimer.Start();
}
void ChatRefresh()
{
conn = new MySqlConnection("Server=...; Database=...; Uid=...; Password=...;");
ds.Clear();
da.SelectCommand = conn.CreateCommand();
da.SelectCommand.CommandText = "select * from chatmessagetbl";
da.SelectCommand.CommandType = CommandType.Text;
while (true)
{
da.Fill(ds, "chatmessagetbl");
textBlockChatArea.Text = "";
foreach (DataRow item in ds.Tables["chatmessagetbl"].Rows)
{
//This has to be done on the thread that owns the textbox.
textBlockChatArea.Dispatcher.BeginInvoke(new Action(() =>
{
textBlockChatArea.Text += item["username"].ToString() + ": " + item["message"].ToString() + "\n";
}));
}
Thread.Sleep(TimeSpan.FromSeconds(1));
}
conn.Dispose();
}
我提供的代码不是很干净,它并不是您可以复制和粘贴的最终解决方案(尽管它可能会起作用),它只是为了帮助您朝着正确的方向前进并展示给您一个可以通过线程完成的方式。
编辑
为了尝试澄清我在评论中关于使用列表控件而不是文本框的观点,我添加了以下伪程序来尝试解释我的意思。
// Each row returned from the database will be converted in to one of these.
public class ChatEntry
{
public string UserName { get; set; }
public string Message { get; set; }
public int MessageID { get; set; }
}
// You will need to introduce a MessageID field to your database to make this method work.
public partial class MainWindow : Window
{
public ObservableCollection<ChatEntry> Entries
{
get { return (ObservableCollection<ChatEntry>)GetValue(EntriesProperty); }
set { SetValue(EntriesProperty, value); }
}
public static readonly DependencyProperty EntriesProperty = DependencyProperty.Register("Entries", typeof(ObservableCollection<ChatEntry>), typeof(MainWindow), new UIPropertyMetadata(null));
// This will be used to make sure that only new entries are added to the chat log.
int lastMessageID;
// This will be used to call UpdateEntries every second.
Thread updateThread;
public MainWindow()
{
Entries = new ObservableCollection<ChatEntry>();
InitializeComponent();
updateThread = new Thread(new ThreadStart(UpdateEntries));
updateThread.Start();
}
void UpdateEntries()
{
while (true)
{
// Prepare your query to gather messages from the message table
// with MessageID > lastMessageID.
// This bit needs to be done on the UI dispatcher or it'll cause an exception.
this.Dispatcher.BeginInvoke(new Action(() =>
{
// Each row that came back is a new message and can be added to the collection.
foreach (var row in rows)
{
Entries.Add(new ChatEntry()
{
UserName = (string)row["UserName"],
Message = (string)row["Message"],
});
}
}));
// at this point, the UI will have been upadted with JUST the new entries, no flicker
// no scrolling to the top each second.
// just one more thing, we need to set lastMessageID to be the latest messageID
// so next time UpdateEntries is called it'll only get the new ones that we don't
// have yet.
lastMessageID = Entries.Max(x => x.MessageID);
// Sleep for a second to ease the update speed.
Thread.Sleep(1000);
}
}
}
要将列表框绑定到新的 Entries 属性,您可以在 XAML 中执行类似的操作。
<Window x:Class="WpfApplication5.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
>
<ListView ItemsSource="{Binding Entries}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" Margin="0,0,5,0" FontWeight="Bold" />
<TextBlock Text="{Binding Message}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Window>