65.9K
CodeProject 正在变化。 阅读更多。
Home

Visual Studio 扩展性: 创建用于在 IDE 中获取邮件通知的扩展

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.92/5 (7投票s)

2017年3月14日

CPOL

8分钟阅读

viewsIcon

14601

downloadIcon

149

在本文中, 我们将创建一个 Visual Studio 扩展( Visual Studio VSIX 包), 用于在 VisualStudio 状态栏通知我们收到的邮件

引言

设想一个场景:您在 Visual Studio 中编码数小时,没有时间在网页浏览器中查看电子邮件,因此可能会错过一些重要通知。当然,有办法获取电子邮件通知,但试想一下,如果您能直接在 Visual Studio 中收到电子邮件通知,那该有多好。这就是我们今天要做的。

在本文中,我们将创建一个 Visual Studio 扩展(Visual Studio VSIX 包),用于在 Visual Studio 状态栏中通知我们有关电子邮件的信息。

Visual Studio 扩展

Visual Studio 扩展允许开发人员为其自己的可插入 Visual Studio IDE 插件开发自定义扩展,以根据自己的需求自定义 Visual Studio 和添加功能。简单地说,您可以创建自己想要 Visual Studio 拥有的功能并将其添加进去。因此,我们将创建自己的功能来获取电子邮件通知并在 Visual Studio 的状态栏中显示它。

如果您想在继续阅读本文之前获得一个好的开始,可以先参考 Akhil Mittal 的这篇文章,它将帮助您创建第一个 VSIX 包。

我们将创建什么

让我们先一睹我们最终将要创建的内容。

自定义工具窗口,让用户登录其电子邮件帐户

Visual Studio 状态栏中的电子邮件通知

必备组件

在开始开发之前,请确保您拥有以下内容

  • Visual Studio 2013 或更高版本。我使用的是 Visual Studio 2013 社区版。
  • Visual Studio SDK
    • 对于 VS 2013,请在此处下载:这里
    • Visual Studio 2015 SDK 不再单独提供下载。相反,Visual Studio 扩展工具(SDK 和模板)已包含在 Visual Studio 安装程序的可选功能中。

动手实践

好了,我们已经谈了很多,现在是时候行动了。让我们开始为 Visual Studio 开发我们的扩展。我将指导您完成开发 VS 扩展所需的所有步骤。现在,系好安全带,准备迎接有趣的旅程。

第一步:创建 VS 包

从“扩展性”类别中创建一个类型为 Visual Studio Package 的新项目。为项目选择一个名称和位置。

单击“确定”后,将出现一个向导,引导您完成一些初始步骤。

单击“下一步”继续。

下一个向导会询问您要使用哪种编程语言进行开发。

选择 Visual C#,因为我们将使用 C# 语言编写代码,然后单击“下一步”。

下一个屏幕将询问您有关包的详细信息。

根据您的选择填写公司名称、VSPackage 名称和详细信息,然后单击“下一步”。

下一个屏幕将询问您要包含在 VSPackage 中的选项。勾选“工具窗口”,因为我们需要一个工具窗口来获取用户的电子邮件凭据。

在下一个屏幕上,输入上一步中包含的“工具窗口”的详细信息。

窗口名称是将在 Visual Studio 窗口列表中显示的名称,commandId 是将在代码中用于唯一标识它的 ID。

下一个屏幕将询问您是否要在项目中包含测试。在本文中,我没有选择此选项,因为我没有编写任何测试。这是向导的最后一步。单击“完成”。

将为您创建一个项目层次结构,其中包含 VS 包所需的文件。

第二步:创建工具窗口

默认情况下,项目中已创建了一个虚拟工具窗口,因为我们在向导中选择了一个。让我们根据需要修改该工具窗口。

打开 MyControl.xaml 并用以下代码替换:

<UserControl x:Class="Microsoft.MailNotifier.MyControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             Background="{DynamicResource VsBrush.Window}"
             Foreground="{DynamicResource VsBrush.WindowText}"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300"
             Name="MyToolWindow">
    <Grid>
        <StackPanel Orientation="Vertical">
            <TextBlock Margin="10" HorizontalAlignment="Center">Notifier</TextBlock>
            <Grid >
                <TextBlock Margin="25,0,0,0">E-Mail</TextBlock>
                <TextBox Width="200" Margin="60,0,0,0" Text="{Binding Email,UpdateSourceTrigger=PropertyChanged}"></TextBox>
            </Grid>
            <Grid >
                <TextBlock Margin="25,0,0,0">Password</TextBlock>
                <PasswordBox Width="200" Margin="60,0,0,0" Name="MyPasswordBox"></PasswordBox>
            </Grid>

            <Button Content="Sign In" Command="{Binding SignInCommand}" 
                    Margin="5"
                    Width="100" Height="30" Name="button1"
                    CommandParameter="{Binding ElementName=MyPasswordBox}"/>
        </StackPanel>
    </Grid>
</UserControl>

这基本上是用于创建一些文本块、文本框、密码控件和按钮的 XAML 代码。

我们工具窗口的用户界面已创建。

让我们看看我们还有哪些其他文件。

  • MyToolWindow.cs - 它包含 MyToolWindow 类,该类继承自 ToolWindowPane,具有其唯一 guid,并且内容设置为我们的 XAML 控件类。我们不需要修改它。
  • PkgCmdID.cs - 它包含打开工具窗口的命令的 uint 代码。我们也不需要修改它。
  • source.extension.vsixmanifest - 它包含我们包的详细信息,我们已经在向导步骤中对其进行了修改。
  • MailNotifierPackage.cs – 这是拥有我们工具窗口的主包,它具有显示和隐藏工具窗口的方法。它在 Visual Studio 菜单中添加了打开工具窗口的选项。在我们的情况下,保持不变。
  • MailNotifier.vsct – 这是您定义菜单项、它们的图标和标题、快捷键等的文件的位置,但 Visual Studio 已创建了一个用于打开工具窗口的菜单,因此我们无需触摸它。

可以看出,Visual Studio 已经完成了大部分工作。

第三步:为工具窗口创建视图模型

向项目添加一个新的类文件,并将其命名为 NotifierToolWindowViewModel。这将是我们的工具窗口的视图模型。

我们需要一个属性来绑定到电子邮件文本框,还需要一个命令来响应登录按钮的单击。

因此,请在视图模型中添加属性和委托命令。

    public class NotifierToolWindowViewModel
    {
        private string _email;
        private DelegateCommand signIn;

        public NotifierToolWindowViewModel()
        {
            signIn = new DelegateCommand(SignIn);
        }

        private void SignIn(object obj)
        {
            

           
        }
        public ICommand SignInCommand
        {
            get { return signIn; }
        }
        public string Email
        {
            get { return _email; }
            set { _email = value; }
        }

    }
    public class DelegateCommand : ICommand
    {
        private Action<object> _executeMethod;
        public DelegateCommand(Action<object> executeMethod)
        {
            _executeMethod = executeMethod;
        }
        public bool CanExecute(object parameter)
        {
            return true;
        }
        public event EventHandler CanExecuteChanged;
        public void Execute(object parameter)
        {
            _executeMethod.Invoke(parameter);
        }
    }
}

如果您的背景是 WPF,那么我敢肯定这很容易理解。

现在是时候构建解决方案并看看我们迄今为止取得了什么成就了。按 F5 运行项目。运行项目时,将打开一个实验性的 Visual Studio 实例。

转到“视图”->“其他窗口”,在那里您会找到名为“Notifier”的工具窗口。

单击它即可打开工具窗口。

是的!我们已经完成了工作的一半。现在我们需要一种机制来连接到我们的电子邮件并获取通知。.NET 中没有获取电子邮件的实现,但有 SMTP 客户端可以发送电子邮件(尽管这不能满足我们的需求)。这可以通过 IMAP(Internet Message Access Protocol)和 IMAP IDLE 来实现,IMAP IDLE 负责立即通知用户收件箱的任何更改。有可用的库可以帮助我们实现目标,所以我使用了 S22.Imap 库。可以从 Nuget 库下载:https://nuget.net.cn/packages/S22.Imap/,并使用包控制台管理器中的命令“Install-Package S22.Imap”将其包含在我们的项目中。

现在,我们的项目中已包含 S22.Imap DLL,但在我们进一步操作之前,我们需要使用强名称注册这个新添加的 DLL,因为 VS 包要求所有程序集都必须用强名称签名。要使用强名称注册 S22.Imap DLL,请遵循此链接中提到的步骤:此链接。我已经为您完成了此操作,然后将其包含在项目中,您可以将其与文章附带的 DLL 一起使用。

第四步:实现 S22.Imap 以获取电子邮件通知

S22.Imap 库非常易于使用,我们只需要创建一个 ImapClient 对象,然后传入我们的凭据即可。

让我们看看如何使用 ImapClient 的代码。

private ImapClient client;

private void InitializeClient(PasswordBox pwBox)
 {
            // Dispose of existing instance, if any.
            if (client != null)
                client.Dispose();
            client = new ImapClient("imap.gmail.com", 993,"username","password", AuthMethod.Login, true);
            // Setup event handlers.
            client.NewMessage += client_NewMessage;
            client.IdleError += client_IdleError;
  }

ImapClient 构造函数接受主机名、端口、用户名、密码和 true(表示 SSL = true)来连接您的电子邮件帐户。然后,我们可以订阅其两个事件,以便在收到新邮件时以及在等待邮件的空闲期间发生任何错误时收到通知。

        private void client_IdleError(object sender, IdleErrorEventArgs e)
        {
            // code to handle error

        }
        private void client_NewMessage(object sender, IdleMessageEventArgs e)
        {
            //code to handle new mail
        }

现在,我们可以在收到新邮件时获得通知了,但这里有一个问题。如果我们尝试连接我们的 Gmail 帐户,它将抛出一些连接错误,因为 Gmail 非常安全,出于您的安全考虑,它不允许不太安全的应用程序连接到您的帐户。因此,要使我们的应用程序能够连接到 Gmail,我们需要更改帐户的设置,允许来自不太安全应用程序的连接。

请遵循此链接设置其安全性:https://www.google.com/settings/security/lesssecureapps

现在一切都已准备好接收通知。

第五步:在 Visual Studio 状态栏中显示自定义文本

要将自定义文本显示在 Visual Studio 状态栏中,我们需要实现 Visual Studio 的 IVsStatusbar 服务,该服务提供了在状态栏中设置文本的方法。让我们看看代码。

        private IVsStatusbar StatusBar
        {
            get
            {
                if (bar == null)
                {
                    bar = Package.GetGlobalService(typeof(SVsStatusbar)) as IVsStatusbar;
                }

                return bar;
            }
        }
        /// <summary>
        /// Displays the message.
        /// </summary>
        public void DisplayMessage(string msg)
        {
            int frozen;

            StatusBar.Clear();
            StatusBar.IsFrozen(out frozen);
            StatusBar.SetText(msg);
            StatusBar.FreezeOutput(0);

        }

        /// <summary>
        /// Display message and show icon
        /// </summary>
        /// <param name="message"></param>
        /// <param name="iconToShow"></param>
        public void DisplayAndShowIcon(string message, object iconToShow)
        {
            object icon = (short)iconToShow;

            StatusBar.Animation(1, ref icon);
            StatusBar.SetText(message);
            Thread.Sleep(3000);

            StatusBar.Animation(0, ref icon);
            StatusBar.FreezeOutput(0);
            StatusBar.Clear();
        }

SetText() 方法将完成此神奇的操作。

我们已经掌握了所有零件,现在只需要将这些零件组装起来并完成我们的代码。

以下是 NotifierToolWindowViewModel 类的完整代码。

using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using S22.Imap;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Mail;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace Microsoft.MailNotifier
{
    public class NotifierToolWindowViewModel
    {
        private string _email;
        private DelegateCommand signIn;
        private IVsStatusbar bar;
        private ImapClient client;
        private AutoResetEvent reconnectEvent = new AutoResetEvent(false);

        public NotifierToolWindowViewModel()
        {
            signIn = new DelegateCommand(SignIn);
        }

        private void SignIn(object obj)
        {
            PasswordBox pwBox = obj as PasswordBox;
            System.Threading.Tasks.Task.Run(() =>
            {
                try
                {
                    while (true)
                    {
                        DisplayAndShowIcon("Connecting...", (short)Microsoft.VisualStudio.Shell.Interop.Constants.SBAI_Build);
                        InitializeClient(pwBox);
                        DisplayMessage("Connected");
                        reconnectEvent.WaitOne();
                    }
                }
                finally
                {
                    if (client != null)
                        client.Dispose();
                }
            });

           
        }
        public ICommand SignInCommand
        {
            get { return signIn; }
        }
        public string Email
        {
            get { return _email; }
            set { _email = value; }
        }
        
        private IVsStatusbar StatusBar
        {
            get
            {
                if (bar == null)
                {
                    bar = Package.GetGlobalService(typeof(SVsStatusbar)) as IVsStatusbar;
                }

                return bar;
            }
        }

        private void InitializeClient(PasswordBox pwBox)
        {
            // Dispose of existing instance, if any.
            if (client != null)
                client.Dispose();
            client = new ImapClient("imap.gmail.com", 993, _email, pwBox.Password, AuthMethod.Login, true);
            // Setup event handlers.
            client.NewMessage += client_NewMessage;
            client.IdleError += client_IdleError;
        }
        private void client_IdleError(object sender, IdleErrorEventArgs e)
        {
            DisplayMessage("An error occurred while idling: ");
            DisplayMessage(e.Exception.Message);
            reconnectEvent.Set();
        }

        private void client_NewMessage(object sender, IdleMessageEventArgs e)
        {
            MailMessage msg = client.GetMessage(e.MessageUID);
            DisplayMessage("Got a new message!" + " From: " + msg.From  +" Subject: " + msg.Subject + " Priority: " + msg.Priority);
        }
        

        /// <summary>
        /// Displays the message.
        /// </summary>
        public void DisplayMessage(string msg)
        {
            int frozen;

            StatusBar.Clear();
            StatusBar.IsFrozen(out frozen);
            StatusBar.SetText(msg);
            StatusBar.FreezeOutput(0);

        }
        /// <summary>
        /// Display message and show icon
        /// </summary>
        /// <param name="message"></param>
        /// <param name="iconToShow"></param>
        public void DisplayAndShowIcon(string message, object iconToShow)
        {
            object icon = (short)iconToShow;

            StatusBar.Animation(1, ref icon);
            StatusBar.SetText(message);
            Thread.Sleep(3000);

            StatusBar.Animation(0, ref icon);
            StatusBar.FreezeOutput(0);
            StatusBar.Clear();
        }

        


    }
    public class DelegateCommand : ICommand
    {
        private Action<object> _executeMethod;
        public DelegateCommand(Action<object> executeMethod)
        {
            _executeMethod = executeMethod;
        }
        public bool CanExecute(object parameter)
        {
            return true;
        }
        public event EventHandler CanExecuteChanged;
        public void Execute(object parameter)
        {
            _executeMethod.Invoke(parameter);
        }
    }
}

我们使用了 AutoResetEvent,它会在事件发生时通知等待的线程。

好了,伙计们,我们已经组装好所有零件,现在是时候运行应用程序并测试我们的 VS 包了。重新生成解决方案并运行应用程序。

  1. 将打开 Visual Studio 的实验实例。
  2. 转到“视图”->“其他窗口”->“Notifier”。
  3. 在工具窗口中输入您的电子邮件和密码以登录。
  4. 如果您愿意,可以关闭工具窗口。
  5. 等待新邮件。
  6. 新邮件通知将显示在状态栏中。

结论

我们已经创建了我们自己的 Visual Studio 扩展,并付出了最小的努力,这是我们今天取得的一点点成就,但借助 Visual Studio 扩展,天空是极限。

这段代码和应用程序还有很多可以改进的地方。例如,我们可以实现 Google 的 API 来获取电子邮件通知,这将更安全、更标准。我们还可以利用 S22.Imap 库提供的不同方法来获取特定邮件或搜索等。我们还可以进一步改进 UI,并更改工具窗口和菜单的默认图标。我们可以将此扩展部署到市场,让其他人使用,但我将把这些留给您去探索和改进。

源代码

完整的源代码可以从我的 github 链接下载

https://github.com/vikas0sharma/MailNotifier

© . All rights reserved.