【问题标题】:Get/Set Data to UI-Objects from BackgroundWorker-Thread从 BackgroundWorker-Thread 获取/设置数据到 UI 对象
【发布时间】:2017-07-03 06:16:42
【问题描述】:

最近,我一直在努力从 UI 对象获取和设置数据。 我知道可以通过使用 Invoking 从 BackgroundWorker-Thread 执行此操作。可悲的是,我只找到了调用方法,它可以很好地设置标签和其他一些东西,但是当涉及到 DataGridViews、Combo-和 TextBoxes 时它失败了。这是我正在谈论的调用“代码”:

this.uiObject.Invoke((MethodInvoker)delegate
{
    this.uiObject.Text = "Hello World";//Setting Label Text from BackgroundWorker
});

正如我所说,我尝试在以下代码中使用它,但它不起作用。

private void loadPlaylists()
{
    this.playlistGrid.Rows.Clear();//Invoke on a DataGridView

    string filePath = this.genrePath + this.sortBox.SelectedItem.ToString() + ".txt";
    if (File.Exists(filePath) && File.ReadAllText(filePath) != "")
    {
        using (StreamReader sr = new StreamReader(filePath))
        {
            bool first = false;
            string line = "";
            while ((line = sr.ReadLine()) != null)
            {
                if (first && line != "")
                {
                    string[] split = line.Split(new string[] { " // " }, StringSplitOptions.None);

                    FullPlaylist playList = spotify.GetPlaylist(split[1], split[0]);

                    this.playlistGrid.Rows.Add(playList.Name, playList.Owner.Id);//Invoke on a DataGridView
                }
                if (line != "")
                    first = true;
            }
        }
    }
}

private void loadItems(bool newItem = false, bool deletedItem = false, string name = "")
{
    this.sortBox.Items.Clear();//Invoke on a ComboBox

    DirectoryInfo dir = new DirectoryInfo(this.genrePath);
    foreach (var file in dir.GetFiles("*.txt"))
    {
        string[] split = file.Name.Split(new string[] { ".txt" }, StringSplitOptions.None);
        this.sortBox.Items.Add(split[0]);//Invoke on a ComboBox
    }

    if (newItem)
    {
        this.sortBox.SelectedItem = name;//Invoke on a ComboBox
        this.mode = 5;
    }

    if (deletedItem)
    {
        if (this.sortBox.Items.Count > 0)//Invoke on a ComboBox
        {
            this.sortBox.SelectedIndex = 0;//Invoke on a ComboBox
            this.mode = 5;
        }
        else
            this.playlistGrid.Rows.Clear();//Invoke on a DataGirdView
    }
}

private void addPlaylists()
{
    string[] split;
    string filePath = "";
    if (this.sortBox.SelectedIndex != -1)//Invoke on a ComboBox
    {
        filePath = this.genrePath + this.sortBox.SelectedItem.ToString() + ".txt";//Invoke on a ComboBox
    }
    else
    {
        MetroFramework.MetroMessageBox.Show(this, "Select a valid Category first!",
            "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
    }
    if (this.playlistLink.Text != "" && this.playlistLink.Text.Contains("/"))//Invoke on a TextBox
    {
        if (this.playlistLink.Text.Contains("|"))//Invoke on a TextBox
        {
            split = this.playlistLink.Text.Split('|');//Invoke on a TextBox
        }
        else
        {
            split = new string[1];
            split[0] = this.playlistLink.Text;//Invoke on a TextBox
        }

        for (int j = 0; j < split.Length; j++)
        {
            string[] split2 = split[j].Split('/');
            string id = split2[split2.Length - 1], owner = split2[split2.Length - 3];
            FullPlaylist playlist = this.spotify.GetPlaylist(owner, id);
            string write = id + " // " + owner + " // " + playlist.Name;
            this.changeOrAddLine(filePath, write, write);
        }

        this.playlistLink.Text = "";//Invoke on a TextBox

        this.loadPlaylists();//Call to a Methode, where Invoke is needed
    }
    else if (!this.playlistLink.Text.Contains("/"))//Invoke on a TextBox
    {
        //Error
        this.playlistLink.Text = "";//Invoke on a TextBox
    }
    else
    {
        //Error
    }
}

这是我从 BackgrounddWorker 调用的三个方法。代码很好,可以在 BackgroundWorker 之外工作。我标记了应该使用调用(如果甚至需要)的所有行,因为它正在使用 UI 项目做一些事情。我希望你们中的某个人能够告诉我如何做到这一点。我不希望您使用我的代码并添加修复程序,只是一个关于如何执行此操作的总体示例也应该可以工作。提前致谢!

【问题讨论】:

    标签: c# user-interface backgroundworker


    【解决方案1】:

    在您影响 UI 控件的任何地方,将该逻辑包装在调用调用中。例如,而不是:

    this.playlistGrid.Rows.Clear(); // No
    

    这样做:

    uiObject.Invoke(() => this.playlistGrid.Rows.Clear()); // Yes
    

    这有效地向 UI 线程发送消息以在 UI 的消息循环中的正确时间执行该操作。如果您将与 UI 控件的每次交互都包装在这样的调用中,错误应该会停止,但请注意,您在等待调用结果时阻塞了后台线程。确保 UI 线程不会依次等待后台线程,否则您将陷入死锁。您可以通过使用 BeginInvoke 来避免这种情况,但您确实需要注意该操作可能不会立即发生。例如,如果您想从控件中读取内容,不要这样做:

    string localValue = null;
    uiObject.BeginInvoke(() => { localValue = this.sortBox.SelectedItem.ToString(); });
    // should NOT assume you can use localValue here
    

    在调用异步操作并将其传递给后台进程之前,您通常最好从 UI 层中的控件中收集所有数据。例如:

    private void loadPlaylists(string selectedItem)
    

    【讨论】:

    • 但是“this.playlistGrid.Invoke(() => this.playlistGrid.Rows.Clear());”给出一个错误。
    • “Lambda 表达式”不能转换为“Delegate”类型,因为它不是委托类型。(大致从德语翻译成英语)
    • 如果这不起作用,您可以尝试:.Invoke((Action)(() =&gt; ...))。 Invoke 是在 .NET 的过去时代创建的,当时不存在 lambda,因此他们试图让调用不同的委托签名变得“容易”。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多