我当然有一个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组件需要 这个。
我不知道要更改什么或错误意味着什么,是的,我已经进行了搜索,但是找不到解决方案或很好的解释。
答案 0 :(得分:1)
让我尝试向您解释此例外情况。
首先,我尝试解释什么是后台线程和UI线程以及它们之间的区别,然后介绍在处理此异常时要遵循的两个简单规则。
我对STA和MTA,UI线程和后台线程的解释
STA
代表Single Thread Apartment
,它有一个对应的Multi Thread Apartment (MTA)
。 this answer中讨论了它们的区别,如果您感兴趣,可以将STA vs. MTA
用作Google搜索的关键字。当您在搜索中看到诸如“ STAThreadAttribute” /“ COM” /“ CoInitializeEx”之类的术语时,您可能会感到困惑,请不要担心,因为您并不孤单,它们是一些很少有人需要使用的疯狂老技术。
并且一个线程使用这些模型之一,STA
或MTA
。您可能已经注意到了
[STAThread]
static void Main()
这意味着线程正在使用STA
。在WPF中,主线程使用STA
,在该线程上创建控件,并且在该线程上也执行诸如Button_Click
之类的事件处理程序,我们将其称为 UI线程。
您使用Thread/ThreadPool/Task
之类的类开始的后台线程默认情况下使用MTA
,System.Timers.Timer.Elapsed
的事件处理程序也是如此,以及在这种情况下FileSystemWatcher
个事件的事件处理程序。
现在,您已经知道您的代码可以在 UI线程(STA)上运行,也可以在后台线程(MTA)之一上运行。
我的简单规则
通常,您需要在 UI线程上创建控件。 (我不会在此处讨论“在后台线程上创建并使其可冻结”的替代解决方案,只是为了使规则易于遵循。正如@slugster在评论中指出的那样,您可以在上创建控件任何线程,但您只能从创建它的同一线程访问它。)
从后台线程,如果要访问控件,则需要使用Dispatcher.Invoke/BeginInvoke
将代码编组到UI线程-因此代码将在UI线程。否则,您会收到此异常(The calling thread must be STA...
)。