会议纪要和任务跟踪工具






4.62/5 (6投票s)
一个软件,它使用模板提供给用户创建和发送会议纪要的选项,标记与会者,创建下一次跟进会议,将任务分配给人员 - 所有这些都来自一个统一的屏幕。
- 引言
- 功能详情
- 背景
- Using the Code
- 关注点
引言
很多时候,当我参加会议时,我都会面临一个枯燥但又相当必要的挑战——撰写会议纪要。我们都知道将会议纪要分发给利益相关者是多么重要,但有时这会成为一项效率低下的任务。最重要的是,撰写会议纪要并不是任务的结束——在大多数情况下,我们需要安排下一次跟进会议,并将待办事项列表分发给会议与会者。
如果我们能在 Outlook 中内置一个模块来完成这些,那不是很好吗?想象一下能够右键单击 Outlook 会议并点击这样的菜单 -
点击此“撰写会议纪要”菜单会打开一个表单,您可以在其中撰写纪要,标记与会者,安排下一次跟进会议,分配任务并发送会议纪要给所有人 - 所有这些都在一个表单中。为了节省您的时间,此工具会在会议纪要正文中预填充大量数据(例如,主题、日期/时间、会议室等)。
我创建了一个小型 Outlook 插件,可以完成所有这些工作。在此我将详细介绍该软件。
我为此工具使用了一些开源组件以及我的一些原创工作。使用的一些组件包括 YARTE 文本编辑器、RicherTextBox 文本编辑器。
功能详情
此加载项可在 Outlook 2010 及更高版本的所有 Outlook 版本上运行。我已在 Outlook 2010 和 2013 上进行了测试。
安装此插件后,如果您在日历上选择任何会议项目并右键单击它,您将看到一个菜单选项,如下图所示(撰写会议纪要)-
如果单击此菜单,插件将打开一个如下所示的表单 -
表单分为三个部分。
顶部部分 大部分是自动填充的,它提取会议信息并在此处显示。许多项目,如日期、时间、主持人、纪要撰写者,都是由该工具获取的。
当您最终提交此表单时,所有这些都将自动添加到会议纪要中。
中间部分 用于安排后续会议。该工具会尝试找出是否已针对同一主题安排了后续会议,如果没有,则会显示一条警报“此主题的下一次会议尚未安排。您可以在此屏幕上创建下一次会议的详细信息”。您可以在此处输入下一次会议的详细信息,包括正文并选择受邀者列表。请注意,受邀者列表具有类似于 Outlook 的自动完成功能,可以轻松地从您最近使用的联系人列表中选择受邀者。这个最近使用的联系人列表也是由该工具计算的,它会扫描您所有的近期会议并从中查找所有受邀者的详细信息。最后,当您提交表单时,将在我们的日历上使用您在此处提交的详细信息创建下一次会议。
最后一个部分 包含两个子部分。左侧的文本编辑器允许您撰写当前会议的纪要正文。您还可以使用附件链接将文件附加到会议纪要正文中 -
右侧是任务分配模块。这个模块允许您编写任务的简短描述,并将其分配给参加会议的任何人。您还可以为每个分配的任务提及提醒设置。此外,您还可以为每个分配的任务附加文件。提交表单后,所有这些任务将单独发送给每个受让人。此外,分配的任务将在最终的会议纪要正文中列出,供所有人查看。
完成以上所有操作后,您可以单击位于表单中心的提交按钮。这将创建后续会议,发送任务给受让人,创建最终的会议纪要,并将其发送给会议的受邀者。此工具使用内置的会议纪要模板,您可以根据需要更改。最终的会议纪要看起来像这样 -
背景
此工具的源代码主要分为以下模块 -
1. ThisAddin:对于任何 Microsoft Office 加载项应用程序,此类是必需的。它基本上是您 Office 应用程序的入口点,并且允许任何自定义应用程序与 Office 应用程序一起运行。
2. 功能区:从 Outlook 2013 开始,这是在 Outlook 中创建自定义上下文菜单的唯一方法。我们需要在此处自定义上下文菜单以添加“撰写会议纪要”菜单。
3. 将功能区与 ThisAddin 绑定:我们需要告知 ThisAddin 使用自定义上下文菜单。
3. 主窗体 (MOM.cs):这是用户单击“撰写会议纪要”后打开的主窗体。该窗体包含许多重要模块 -
- 自动完成文本框:此文本框允许用户为下一次会议选择受邀者列表。这个自动完成文本框是一个自定义的。
- 文本编辑器:在此项目中,我使用了两个开源文本编辑器 - YARTE 和 Rich Text Editor。
- 创建下一次会议:此模块创建具有用户在此窗体中传递的所有详细信息的下一次会议。
- 分配任务:此模块创建带有附件的 Outlook 任务项,并将任务发送给受让人。
- 使用内置模板创建最终的会议纪要电子邮件:此模块汇总了用户提交的所有详细信息,并创建会议纪要电子邮件并将其发送给会议的所有受邀者。
继续阅读以全面了解代码 -
Using the Code
使用代码非常简单。只需下载附件中的解决方案,解压缩并用 Visual Studio 打开。所有必需的库已添加到解决方案中。以下是代码中的一些重要详细信息,如背景所示 -
ThisAddin
要理解这个类,最好参考 Microsoft 在 msdn 上提供的内容 -
“当您通过创建 VSTO 加载项来扩展 Microsoft Office 应用程序时,您将直接针对项目中的 ThisAddIn 类进行编码。您可以使用此类来执行任务,例如访问 Microsoft Office 宿主应用程序的对象模型、自定义应用程序的用户界面 (UI) 以及将 VSTO 加载项中的对象暴露给其他 Office 解决方案。您可以在 ThisAddIn 类中开始编写 VSTO 加载项代码。Visual Studio 会自动在 VSTO 加载项项目中的 ThisAddIn.vb (Visual Basic) 或 ThisAddIn.cs (C#) 代码文件中生成此类。当 Microsoft Office 应用程序加载您的 VSTO 加载项时,Visual Studio Tools for Office 运行时会自动实例化此类。”
VSTO 指的是 Visual Studio Tools for Office - Microsoft 的专有技术。
基本上,当您想创建一个与 Microsoft Office 应用程序交互的项目时,您可以在 Visual Studio 中选择项目类型 -
当您选择加载项类型的项目时,Visual Studio 将创建 ThisAddin.cs 文件的骨架。您必须在此文件中写入任何自定义内容。
功能区
下载源代码附件并解压缩后,您将看到一个 Visual Studio 解决方案。您需要使用 Visual Studio 打开此解决方案。
打开后,您可以看到以下文件 -
这些是项目的“功能区”组件。
那么,这个“功能区”是什么?
要在 Outlook 中添加自定义菜单项,您必须在项目中添加一个称为功能区 (Ribbon) 的组件。从 Outlook 2013 开始,功能区是创建自定义上下文菜单的唯一方法(在 Outlook 2010 之前还有另一种选项可用于自定义菜单)。要添加功能区,您可以右键单击项目,选择“添加新项”,然后选择“功能区”。
此功能区组件的所有配置都通过 Ribbon.xml 文件(在我的项目中是 Ribbon1.xml)进行控制。
这是我们 xml 文件的内容 -
//
<?xml version="1.0" encoding="UTF-8"?>
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="Ribbon_Load">
<ribbon>
</ribbon>
<contextMenus>
<contextMenu idMso="ContextMenuCalendarItem">
<button id="MOMbutton" onAction="ShowMOMFormOnClick" getImage="GetIcon" getLabel="GetCustomLabel"/>
</contextMenu>
</contextMenus>
</customUI>
//
我在这里重点介绍了最重要的部分。请看 contextMenu 标签内的内容。我们可以在此标签内定义任意数量的上下文菜单。对于此项目,我只关心当用户右键单击日历项时显示的上下文菜单。我的项目如何告诉 Outlook 我们想向此上下文菜单添加一个新项目? 诀窍在于 idMso 值。 Outlook 中的每个上下文菜单都有 Microsoft 设置的不同 idMso 值。日历上下文菜单的 idMso 值为 ContextMenuCalendarItem。
在指定了正确的 idMso 值后,我们必须指定按钮的 id,指定用户单击它时要调用的事件处理程序 (ShowMOMFormOnClick),指定用于显示此新按钮的图像的事件处理程序 (GetIcon),以及指定用于显示按钮上文本的事件处理程序 (GetCustomLabel)。
现在,让我们看看当用户单击“撰写会议纪要”按钮时将调用的事件处理程序。
首先要理解 Explorer 对象。此对象指向桌面上当前最高级别的活动应用程序。由于用户当前正在单击 Outlook 菜单,因此最高级别的应用程序是 Outlook 日历。
您可以在此处阅读有关 Explorer 的更多信息 - https://msdn.microsoft.com/en-us/library/office/ff870017.aspx
Explorer 对象有一个名为 Selection 的属性,使用它可以验证用户是否选择了任何内容。如果用户选择了任何内容,代码会检查所选内容是否确实是 Outlook AppointmentItem。如果是约会项目,那么以下代码将初始化 MOM_form - 这基本上是用户可以撰写会议纪要和其他详细信息的首要窗体。
//
public void ShowMOMFormOnClick(Office.IRibbonControl control)
{
Explorer explorer = ThisAddIn.app.ActiveExplorer();
if (explorer != null && explorer.Selection != null && explorer.Selection.Count > 0)
{
object item = explorer.Selection[1];
if (item is AppointmentItem)
{
AppointmentItem mailItem = item as AppointmentItem;
string entryid = mailItem.EntryID;
String recpList = "";
if (mailItem.Recipients != null)
foreach (Outlook.Recipient rcp in mailItem.Recipients)
{
String modifiedRcpName = rcp.Name.IndexOf("@") < 0 ? rcp.Name + "(" + rcp.Address + ")" : rcp.Name;
recpList = recpList.Equals("") ? modifiedRcpName : recpList + "; " + modifiedRcpName;
}
new MOM_Form(entryid, ThisAddIn.calendar, ThisAddIn.ns, recpList, ThisAddIn.emailNameMappingDict, ThisAddIn.app).ShowDialog();
}
}
}
//
将功能区与 ThisAddin 绑定
我们必须告知 ThisAddin(这是加载项的入口点)使用我们创建的自定义功能区。这是一个简单的步骤。 ThisAddin.cs 中的以下行完成了这项工作 -
protected override Microsoft.Office.Core.IRibbonExtensibility CreateRibbonExtensibilityObject()
{
return new Ribbon1();
}
主窗体 (MOM.cs)
窗体包含许多重要模块 -
- 自动完成文本框:此文本框允许用户为下一次会议选择受邀者列表。这个自动完成文本框是一个自定义的。
- 文本编辑器:在此项目中,我使用了两个开源文本编辑器 - YARTE 和 Rich Text Editor。
- 创建下一次会议:此模块创建具有用户在此窗体中传递的所有详细信息的下一次会议。
- 分配任务:此模块创建带有附件的 Outlook 任务项,并将任务发送给受让人。
- 使用内置模板创建最终的会议纪要电子邮件:此模块汇总了用户提交的所有详细信息,并创建会议纪要电子邮件并将其发送给会议的所有受邀者。
自动完成文本框
当用户为后续会议选择受邀者列表时,需要此文本框。想法是为用户提供类似 Outlook 的自动完成功能,以便在向受邀者列表添加电子邮件 ID 时。
Windows 窗体没有提供现成的自动完成文本框。所以我不得不专门为此目的创建一个。关于我如何创建这个自动完成文本框的详细信息在我之前的一篇文章中进行了描述:https://codeproject.org.cn/Tips/881637/Type-Ahead-Suggestion-Box-using-Listbox。最终结果是类似这样的文本框 -
当用户输入任何首字母时,文本框会提供一个建议列表,用户可以从中选择任何电子邮件 ID。
建议列表中的电子邮件 ID 是如何填充的?
在项目加载时,当 ThisAddin 被运行时调用时,方法 populateAddressListFromOutlook() 会扫描日历中过去 30 天内的所有 Outlook 会议。从每个会议中,它会提取受邀者列表并创建一个字典。
这是 populateAddressListFromOutlook() 方法的摘录 -
app = this.Application;
ns = app.GetNamespace("MAPI");
calendar = ns.GetDefaultFolder(Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderCalendar);
String StringToCheck = "";
StringToCheck = "[Start] >= " + "\"" + startDate.ToString().Substring(0, startDate.ToString().IndexOf(" ")) + "\""
+ " AND [End] <= \"" + endDate.ToString().Substring(0, endDate.ToString().IndexOf(" ")) + "\"";
Microsoft.Office.Interop.Outlook.Items oItems = (Microsoft.Office.Interop.Outlook.Items)calendar.Items;
Microsoft.Office.Interop.Outlook.Items restricted;
oItems.Sort("[Start]", false);
oItems.IncludeRecurrences = true;
restricted = oItems.Restrict(StringToCheck);
restricted.Sort("[Start]", false);
restricted.IncludeRecurrences = true;
Microsoft.Office.Interop.Outlook.AppointmentItem oAppt = (Microsoft.Office.Interop.Outlook.AppointmentItem)restricted.GetFirst();
Dictionary<string, string=""> comboDict = new Dictionary<string, string="">();
comboDict.Add("ALL", "ALL");
//Loop through each appointment item to find out the unique recipient list and add to the combo box
while (oAppt != null)
{
oAppt = (Microsoft.Office.Interop.Outlook.AppointmentItem)restricted.GetNext();
if (oAppt != null)
foreach (Microsoft.Office.Interop.Outlook.Recipient rcp in oAppt.Recipients)
{
//Display the email id in bracket along with the name in case the name rcp.Name does not contain the email address
//This will help in situtation where the the same recipient with multiple emails address need to be distinguised
String recpDisplayString = rcp.Name.IndexOf("@") < 0 ? rcp.Name + "(" + rcp.Address + ")" : rcp.Name;
if (!comboDict.ContainsKey(recpDisplayString))
{
comboDict.Add(recpDisplayString, recpDisplayString);
if (recpDisplayString != null && !emailNameMappingDict.ContainsKey(recpDisplayString))
emailNameMappingDict.Add(recpDisplayString, rcp.Address != null ? rcp.Address : rcp.Name);
if (rcp.Name != null && !MOM_Form.autoCompleteList.Contains(recpDisplayString))
MOM_Form.autoCompleteList.Add(recpDisplayString);
}
}
}
</string,></string,>
文本编辑器
我在此项目中使用了两个开源文本编辑器 - YARTE 和 RicherTextBox。这些编辑器中的每一个都提供了项目所需的各种功能。
当用户创建后续会议并需要撰写下一次会议的正文时,我正在使用 RicherTextBox。原因是,出于某种奇怪的原因,Outlook 约会/会议项目的正文需要以 rtf 格式读取,而不是以普通字符串或 html 内容读取。RicherTextBox 可以让您轻松地将文本框内的内容转换为 rtf 格式。
另一方面,以 html 格式创建 Outlook 电子邮件内容是最方便的。因此,此工具创建的会议纪要会在最终发送电子邮件之前将纪要内容创建为 html。YARTE 是一个编辑器,可以轻松地将内容转换为 html。这就是为什么我在撰写会议纪要正文时使用 YARTE 作为文本编辑器。
创建下一次会议
主窗体打开后 (MOM.cs),它会尝试找出是否已安排本次会议日期之后的同一主题的会议。如果找不到这样的发生,则会在窗体上显示一条消息 -
* 此主题的下一次会议尚未安排。您可以在此屏幕上创建下一次会议的详细信息
如果在同一主题行中找到后续发生,它会提取信息并填充窗体上的下一次会议部分。MOM.cs 中的 findNextOccuranceOfThisMeeting 方法负责所有这些活动。
public Microsoft.Office.Interop.Outlook.AppointmentItem findNextOccuranceOfThisMeeting(String subject, Microsoft.Office.Interop.Outlook.MAPIFolder calendar, Outlook.AppointmentItem calItem, String inviteeList)
{
Microsoft.Office.Interop.Outlook.Items oItems = (Microsoft.Office.Interop.Outlook.Items)calendar.Items;
DateTime startDate = calItem.Start;
oItems.Sort("[Start]", false);
oItems.IncludeRecurrences = true;
String StringToCheck = "";
StringToCheck = "[Start] > " + "\'" + startDate.ToString().Substring(0, startDate.ToString().IndexOf(" ") + 1).Trim() + "\'"
+ " AND [Subject] = '" + calItem.Subject.Trim() + "'";
// StringToCheck = "[Start] > " + "\'" + startDate.AddDays(1).ToString().Substring(0, startDate.ToString().IndexOf(" ")) + "\'"
// + " AND [Subject] = '" + calItem.Subject.Trim() + "'";
Microsoft.Office.Interop.Outlook.Items restricted;
restricted = oItems.Restrict(StringToCheck);
restricted.Sort("[Start]", false);
restricted.IncludeRecurrences = true;
Microsoft.Office.Interop.Outlook.AppointmentItem oAppt = (Microsoft.Office.Interop.Outlook.AppointmentItem)restricted.GetFirst();
if (oAppt != null) //Next occurance found
{
//Outlook.MailItem em=
Outlook.MailItem em = app.CreateItem(Outlook.OlItemType.olMailItem) as Outlook.MailItem;
em.HTMLBody = oAppt.Body;
label_next_meeting.Visible = false;
nextmeetingSubject.Text = oAppt.Subject.Trim();
nextMeetingLocation.Text = oAppt.Location != null ? oAppt.Location.Trim() : "";
//MemoryStream stream = new MemoryStream(oAppt.RTFBody);
nextMeetingBody.Rtf = System.Text.Encoding.ASCII.GetString(oAppt.RTFBody);
//ASCIIEncoding.Default.GetBytes(
//byte[] b =
// nextMeetingBody.Rtf=stream.
// nextMeetingBody.Html = em.HTMLBody;
String timeTemp = oAppt.Start.ToString().Substring(oAppt.Start.ToString().IndexOf(" ") + 1);
nextmeetingStartTime.Text = timeTemp.Substring(0, timeTemp.LastIndexOf(":")) + " " + (timeTemp.EndsWith("AM") ? "AM" : "PM");
timeTemp = oAppt.End.ToString().Substring(oAppt.End.ToString().IndexOf(" ") + 1);
nextmeetingEndTime.Text = timeTemp.Substring(0, timeTemp.LastIndexOf(":")) + " " + (timeTemp.EndsWith("AM") ? "AM" : "PM");
dateTimePicker_nextMeetingDate.Checked = true;
dateTimePicker_nextMeetingDate.Text = oAppt.Start.ToString();
styleInviteeList(inviteeList);
//Load the attachment list if there are already uploaded attachment details for the next meeting
if (oAppt.Attachments != null && oAppt.Attachments.Count > 0)
{
foreach (Outlook.Attachment atchItem in oAppt.Attachments)
{
dataGridView_next_meeting_atch.Rows.Add(atchItem.FileName.Trim(), "");
}
}
//textBox_next_invitees.Text = inviteeList;
//=Convert.ToDateTime(oAppt.Start.ToString().Substring(oAppt.Start.ToString().IndexOf(" ") + 1));
return oAppt;
}
else
{
label_next_meeting.Visible = true;
nextmeetingSubject.Text = subject;
styleInviteeList(inviteeList);
//textBox_next_invitees.Text = inviteeList;
//textBox_next_invitees.textfr
return null;
}
}
分配任务
窗体右下角有一个区域,供用户创建任务并将其分配给会议的受邀者。在创建任务时,用户可以提供简短的描述、选择任务的到期日期、设置提醒时间、附加文件并从列表中分配给受邀者。
此处创建的所有任务将作为 Outlook 任务项发送给各自的个人。此外,当最终会议纪要发送时,这些任务的详细信息将自动写入程序生成的会议纪要正文中 -
当有人单击操作项列表中的超链接时,将在 Outlook 中打开相应的任务详细信息 -
现在让我们看看所有这些是如何完成的。
用户单击窗体内的提交按钮后,所执行的操作之一是创建任务并将其分配给目标人员。以下 assignTasks 方法主要负责创建任务项、创建任务正文并将其发送给个人。
该方法循环遍历任务列表中数据网格视图中插入的每个项目,并创建一个 TaskItem。每个 TaskItem 都使用 send() 方法发送给目标接收者。
此方法还创建包含任务列表的会议纪要正文的 html 部分。
private String assignTasks()
{
Dictionary<int, string=""> taskList = new Dictionary<int, string="">();
String returnHTML = "";
foreach (DataGridViewRow dr in dataGridView_action_items.Rows)
{
try
{
//Use the Outlook application object created in the previous form
Outlook.TaskItem taskObj = app.CreateItem(Outlook.OlItemType.olTaskItem) as Outlook.TaskItem;
taskObj.Subject = dr.Cells[0].Value.ToString();
taskObj.Body = dr.Cells[0].Value.ToString();
taskObj.DueDate = DateTime.Parse(dr.Cells[1].Value.ToString());
//taskObj.Owner = dr.Cells[4].Value.ToString();
taskObj.Assign();
taskObj.Recipients.Add(dr.Cells[4].Value.ToString());
taskObj.ReminderSet = true;
taskObj.ReminderTime = Convert.ToDateTime(taskObj.DueDate.ToShortDateString() + " " + comboBox_reminder.Text.Trim());
if (!dr.Cells[3].Value.ToString().Equals(""))
taskObj.Attachments.Add(dr.Cells[3].Value.ToString(), Outlook.OlAttachmentType.olByValue, 1, dr.Cells[3].Value.ToString());
taskObj.Send();
//Create the HTML content for the MOM
returnHTML += "<tr style='mso-yfti-irow:0;mso-yfti-firstrow:yes;mso-yfti-lastrow:yes'>" + "<td style='padding:.75pt .75pt .75pt .75pt'>" + "<p class=MsoNormal style='margin-top:3.0pt;margin-right:0cm;margin-bottom:3.0pt;margin-left:0cm'><b><span style='font-size:8.0pt;font-family:\"Book Antiqua\",\"serif\"'>" + "<a href=Outlook:" + taskObj.EntryID + ">" + taskObj.Subject + "</a>" + "</span></b></p> </td>" + "<td style='padding:.75pt .75pt .75pt .75pt'>" + "<p class=MsoNormal style='margin-top:3.0pt;margin-right:0cm;margin-bottom: 3.0pt;margin-left:0cm'><span style='font-size:8.0pt;font-family:\"Book Antiqua\",\"serif\"'>" + taskObj.Owner + "</span></b></p> </td>" + "<td style='padding:.75pt .75pt .75pt .75pt'>" + "<p class=MsoNormal style='margin-top:3.0pt;margin-right:0cm;margin-bottom: 3.0pt;margin-left:0cm'><span style='font-size:8.0pt;font-family:\"Book Antiqua\",\"serif\"'>" + taskObj.DueDate.ToShortDateString() + "</span></b></p> </td>"; +
}
catch (Exception ex)
{
MessageBox.Show("Error Sending Task" + ex.ToString(), "Error", MessageBoxButtons.OK);
}
}
return returnHTML;
}</int,></int,>
使用内置模板创建最终的会议纪要电子邮件
用户单击提交按钮后,程序会尝试创建下一个后续会议,分配任务,然后创建会议纪要正文。会议纪要正文是 HTML 内容,并且是使用内置模板创建的。
模板存储在 Resources.resx 文件内的 template.html 文件中。
可以更改此模板以创建不同格式的会议纪要电子邮件。在其当前形式下,template.html 文件具有如下屏幕截图所示的占位符 -
所有占位符(写为 [...])都是字符串,在用户提交表单时,这些字符串会在运行时被替换为正确的 html 内容。在 HtmlTemplate 类中编写了用实际值替换模板中占位符的逻辑。
此类有一个 Render 方法,该方法负责用字符串替换占位符。
//
public string Render(object values)
{
string output = _html;
foreach (var p in values.GetType().GetProperties())
output = output.Replace("[" + p.Name + "]", (p.GetValue(values, null) as string).Trim() ?? string.Empty);
return output;
}
//
此 render 方法从 createMOMHtml 方法调用,该方法基本上将每个占位符的值与占位符名称一起传递。例如,占位符 TITLE 与会议名称一起传递,DATEOFMEETING 与会议的实际日期一起传递等。
//
private String createMOMHtml(string taskHTML)
{
var template = new HtmlTemplate();
String attendeeList = "";
for (int i = 0; i < checkedListBox_attendee.CheckedIndices.Count; i++)
attendeeList = attendeeList.Equals("") ? checkedListBox_attendee.Items[i].ToString() : attendeeList + "; " + checkedListBox_attendee.Items[i].ToString();
var output = template.Render(new
{
TITLE = meetingname.Text.Trim(),
DATEOFMEETING = meetingDate.Text.Trim(),
STARTTIME = starttime.Text.Trim(),
LOCATION = location.Text.Trim(),
CHAIR = chair.Text.Trim(),
MINUTETAKEN = minutestaken.Text.Trim(),
ENDTIME = endtime.Text.Trim(),
SUBJECT = meetingname.Text.Trim(),
TOPIC = meetingNotes.Html,
NEXTMEETINGDATE = dateTimePicker_nextMeetingDate.Text,
NEXTMEETINGTIME = nextmeetingStartTime.Text,
NEXTMEETINGLOCATION = nextMeetingLocation.Text,
NEXTMEETINGSUBJECT = nextmeetingSubject.Text,
ACTIONITEMS = taskHTML,
ALLATTENDEES = attendeeList
});
return output;
}
//
兴趣点
我希望保持此项目开放供大家贡献并进一步发展。此项目的即时改进包括将窗体移至 Metro 风格,并结合 EWS 获取会议室的可用性(在预订下一次会议时)等。请随时发布您的反馈/想法。
历史