尝试向WrapPanel添加按钮时出现WPF错误

时间:2018-09-16 12:44:02

标签: c# wpf

我当然有一个FileSystemWatcher,它检查文件夹中的更改。因此,当您将文件添加到文件夹时,我需要在包装面板中添加一个按钮。我尝试过:

    public void CheckDir()
{
    string[] args = System.Environment.GetCommandLineArgs();
    var folderName = $"{AppDomain.CurrentDomain.BaseDirectory}games";

    FileSystemWatcher watcher = new FileSystemWatcher
    {
        Path = folderName,

        NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
       | NotifyFilters.FileName | NotifyFilters.DirectoryName
    };

    // Add event handlers.
    watcher.Changed += new FileSystemEventHandler(OnChanged);
    watcher.Created += new FileSystemEventHandler(OnChanged);
    watcher.Deleted += new FileSystemEventHandler(OnChanged);
    watcher.Renamed += new RenamedEventHandler(OnRenamed);

    // Begin watching.
    watcher.EnableRaisingEvents = true;

}

//When button is changed, created, or deleted
private void OnChanged(object source, FileSystemEventArgs e)
{
    Console.WriteLine("File: " + e.Name + " " + e.ChangeType);

    var buttonName = "Button_" + Path.GetFileNameWithoutExtension(e.FullPath).Replace(" ", "");

    if (e.ChangeType == WatcherChangeTypes.Created)
    {
        var buttonContent = Path.GetFileNameWithoutExtension(e.FullPath);

        CreateButton(buttonContent, buttonName);
    }
    else if (e.ChangeType == WatcherChangeTypes.Deleted)
    {

        buttonHolder.Children.Remove(btnFile);
    }
}

CreateButton无效:

private void CreateButton(string buttonContent, string buttonName)
{
    Button newBtn = new Button
    {
        Content = buttonContent,
        Name = buttonName,
        BorderThickness = new Thickness(1),
        Width = 260,
        Height = 100
    };

    newBtn.Click += OpenFile;

    buttonHolder.Children.Add(newBtn);
}

但是尝试添加按钮时出现错误:

  

调用线程必须是STA,因为许多UI组件需要   这个。

我不知道要更改什么或错误意味着什么,是的,我已经进行了搜索,但是找不到解决方案或很好的解释。

1 个答案:

答案 0 :(得分:1)

让我尝试向您解释此例外情况。

首先,我尝试解释什么是后台线程和UI线程以及它们之间的区别,然后介绍在处理此异常时要遵循的两个简单规则。

我对STA和MTA,UI线程和后台线程的解释

STA代表Single Thread Apartment,它有一个对应的Multi Thread Apartment (MTA)this answer中讨论了它们的区别,如果您感兴趣,可以将STA vs. MTA用作Google搜索的关键字。当您在搜索中看到诸如“ STAThreadAttribute” /“ COM” /“ CoInitializeEx”之类的术语时,您可能会感到困惑,请不要担心,因为您并不孤单,它们是一些很少有人需要使用的疯狂老技术。

并且一个线程使用这些模型之一,STAMTA。您可能已经注意到了

之类的代码
[STAThread]
static void Main()

这意味着线程正在使用STA。在WPF中,主线程使用STA,在该线程上创建控件,并且在该线程上也执行诸如Button_Click之类的事件处理程序,我们将其称为 UI线程

您使用Thread/ThreadPool/Task之类的类开始的后台线程默认情况下使用MTASystem.Timers.Timer.Elapsed的事件处理程序也是如此,以及在这种情况下FileSystemWatcher个事件的事件处理程序。

现在,您已经知道您的代码可以在 UI线程(STA)上运行,也可以在后台线程(MTA)之一上运行。

我的简单规则

  1. 通常,您需要在 UI线程上创建控件。 (我不会在此处讨论“在后台线程上创建并使其可冻结”的替代解决方案,只是为了使规则易于遵循。正如@slugster在评论中指出的那样,您可以在上创建控件任何线程,但您只能从创建它的同一线程访问它。)

  2. 后台线程,如果要访问控件,则需要使用Dispatcher.Invoke/BeginInvoke将代码编组到UI线程-因此代码将在UI线程。否则,您会收到此异常(The calling thread must be STA...)。