Reputation: 127
Sorry for creating another topic because my question wasn't specific and not accurate to resulting the thread to be closed and deleting.
I have a chat application, i want my application not to hang while the textblock for chat dialogue is refreshing every a second to retrieve the current conversation. How can i prevent my application not to stop and resume every second? I actually used the google but in return i found no specific answer. Here is my code:
public MainWindow()
{
InitializeComponent();
timerChatRefresh = new DispatcherTimer();
timerChatRefresh.Interval = new TimeSpan(0, 0, 1);
timerChatRefresh.IsEnabled = false;
timerChatRefresh.Tick += new EventHandler(timerChatRefresh_Tick);
timerChatRefresh.Start();
}
void timerChatRefresh_Tick(object sender, EventArgs e)
{
ChatRefresh();
}
private void ChatRefresh()
{
conn = new MySqlConnection("Server=...; Database=...; Uid=...; Password=...;");
ds.Clear();
textBlockChatArea.Text = "";
da.SelectCommand = conn.CreateCommand();
da.SelectCommand.CommandText = "select * from chatmessagetbl";
da.SelectCommand.CommandType = CommandType.Text;
da.Fill(ds, "chatmessagetbl");
foreach (DataRow item in ds.Tables["chatmessagetbl"].Rows)
{
textBlockChatArea.Text += item["username"].ToString() + ": " + item["message"].ToString() + "\n";
}
conn.Dispose();
}
Upvotes: 1
Views: 3175
Reputation: 6466
You will need to update the textbox on a thread that isn't the UI thread.
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();
}
The code I've provided isn't very clean, it's not meant to be a final solution that you can copy and paste (though it'll probably work), it's just to try to help you in the right direction and show you one way that it can be done with threading.
EDIT
To try and clear up a point I made in comments about using a list control instead of a textbox, I've added the following pseudo program to try and explain what I meant.
// 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);
}
}
}
To bind the listbox to the new Entries property you do something like this in the 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>
Upvotes: 2
Reputation: 3318
Try using parallel tasks as a background worker or a thread
Upvotes: 2