防止wpf窗口挂起

时间:2012-06-11 12:24:06

标签: c# wpf hang

很抱歉创建了另一个主题,因为我的问题并不具体,也不准确导致该帖子被关闭和删除。

我有一个聊天应用程序,我希望我的应用程序不会挂起,而聊天对话的文本块每秒刷新一次以检索当前对话。如何防止我的应用程序不停止并每秒恢复? 我实际上使用谷歌但作为回报我没有找到具体的答案。 这是我的代码:

    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();
    }

2 个答案:

答案 0 :(得分:2)

尝试将并行任务用作后台工作程序或线程

答案 1 :(得分:2)

您需要更新非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>