触发由UI(ASPX)创建的线程中的对象事件

时间:2019-03-01 16:28:35

标签: c# asp.net multithreading webforms event-handling

在我浏览的大部分页面中,我都会把这个扔掉,似乎与我所读的内容不尽相同,或者我不明白他们在说什么,并希望找一个清晰的例子/理解。我是一个熟练的程序员,但是不习惯使用UI中的线程和使用自定义对象进行事件处理。

我创建了一个只有一个事件的DLL

public event EventHandler<StdMessageArgs> onSendMessage;

稍后,在类中,如果我想向创建对象的程序发送消息,我将使用此方法:

private void log(string msg, char errType)
{
    onSendMessage(this, new StdMessageArgs(msg, errType));
}

我创建了一个成功使用此控制台的应用程序。我创建对象并使用以下语句添加事件:

// set up the class object and it's event
VDKItemImportToCM.VDKItemImporter itemImport = new VDKItemImportToCM.VDKItemImporter(PSWebconnectString, x3v6connectString, tableList);
itemImport.onSendMessage += itemImport_OnMsg;

以后,我可以使用此事件显示和记录消息:

public static void itemImport_OnMsg(object sender, VDKItemImportToCM.VDKItemImporter.StdMessageArgs e)
{
    switch (e.ErrorType) 
    {
        case 'i':
            log.Info(e.Msg);
            break;
        case 'e':
            log.Error(e.Msg);
            break;
        case 'f':
            log.Fatal(e.Msg);
            Environment.Exit(-900);
            break;
    }
}

这对我的控制台应用程序非常有用,但是我想对ASP Web窗体使用相同的DLL,以便关联人员可以执行与控制台程序相同的任务。我不得不解决两个问题。首先是如何实时更新文本框,多亏了Stackoverflow,我找到了一个解决方案。这是使用计时器更新文本框的页面源:

<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
<asp:Timer runat="server" ID="Timer1" Interval="1000" Enabled="false" ontick="Timer1_Tick" />
<table style="width: 800px">
    <tr>
        <td align="center" colspan="2" height="40px">
                                        <asp:Label ID="Label1" runat="server" Text="Van Dyke Item Import to CM" class="heading1" Font-Bold="False" Font-Names="Verdana" ForeColor="Blue" Font-Size="Medium"></asp:Label>
                                    </td>
    </tr>
    <tr>
        <td colspan="2" style="padding-bottom: 30px; margin-bottom: 30px">
            <asp:Label ID="lblImportMessages" runat="server"></asp:Label>
        </td>
    </tr>
    <tr>
        <td style="width: 187px">
            <asp:Label ID="Label2" runat="server" Text="Select Excel Import File:"></asp:Label>
        </td>
        <td>
            <asp:FileUpload ID="fulExcelFile" runat="server" Width="315px" />
        </td>
    </tr>
    <tr>
        <td colspan="2" height="45px">
            <asp:Button ID="btnImportItems" runat="server" OnClick="btnImportItems_Click" Text="Import Items" />
        </td>
    </tr>
    <tr>
        <td colspan="2">Import Progress</td>
    </tr>
    <tr>
        <td colspan="2">
            <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Always">
            <ContentTemplate>
                <asp:TextBox ID="txtDisplayProgress" runat="server" Height="400px" TextMode="MultiLine" Width="100%"></asp:TextBox>
            </ContentTemplate>
            </asp:UpdatePanel>
        </td>
    </tr>
</table>

我发现他们说要执行此操作的示例是在按下按钮时在线程中启动主要工作负载,因此这是我在buttonclick上的反向代码:

protected void btnImportItems_Click(object sender, EventArgs e)
{
    string filename = Path.GetFileName(fulExcelFile.FileName);
    // check for valid file extension

    if (fulExcelFile.HasFile)
    {
        String fileExtension = System.IO.Path.GetExtension(fulExcelFile.FileName).ToLower();
        String[] allowedExtensions = { ".xlsx" };

        if (fileExtension != ".xlsx")
        {
            lblImportMessages.Text = "The file name is incorrect (Excel .xlsx)";
            return;
        }
    }

    btnImportItems.Enabled = false;
    Timer1.Enabled = true;

    Thread workerThread = new Thread(new ParameterizedThreadStart(processClientExcel));
    workerThread.Start(fulExcelFile);
    // processClientExcel(fulExcelFile);
}

您会看到它启动了一个名为processClientExcel的方法,该方法中的控制台中具有相同的对象/事件语句

VDKItemImporter itemImport = new VDKItemImporter(PSWeb, x3v6, tableList);
itemImport.onSendMessage += itemImport_OnMsg;

,在表单的后端代码中,我从itemImport对象添加了事件:

protected void itemImport_OnMsg(object sender, VDKItemImporter.StdMessageArgs e)
{
    string errorType = "";
    switch (e.ErrorType)
    {
        case 'i':
            errorType = "Info: ";
            break;
        case 'e':
            errorType = "Error: ";
            break;
        case 'f':
            errorType = "Fatal: ";
            break;
    }
    content += errorType + e.Msg + "\r\n";
    //txtDisplayProgress.Text += errorType + e.Msg + "\r\n";
}

要进行内部整理,请将这些变量添加到表单代码中以管理计时器/线程处理:

protected static string content;
protected static bool inProcess = false;
protected static bool processComplete = false;

我解决了具有文本框的第一个基本问题,并显示了使用updatePanel / timer / thread应用程序更新进度的过程。但是,此操作的关键是对象事件 itemImport_OnMsg ,并且当线程中的对象试图发送消息时,不会触发/执行该事件,因此在计时器计时和线程运行时,不会进行更新消息出现,因为该事件未执行。我已经阅读并查看了诸如调度程序,侦听器,后台工作人员,代表之类的内容,直到我的脑袋旋转为止,我无法弄清楚如何连接本地线程告诉UI线程该本地线程中的对象已被触发。

我缺少什么代码,或者必须重写多少代码才能使类库在两个不同的环境(控制台/ Web表单)之间工作。

0 个答案:

没有答案