Skip to main content
Commonmark migration
Source Link

##Building a simple app with Soapi.CS

Building a simple app with Soapi.CS

##Building a simple app with Soapi.CS

Building a simple app with Soapi.CS

Post Undeleted by Sky Sanders
deleted 6170 characters in body; added 81 characters in body
Source Link
Sky Sanders
  • 12.1k
  • 3
  • 32
  • 60

As a simple demonstration I spend about 30 minutes and wrote User Display. The code demonstrates using the api endpoint discovery service at http://soapi.info/rest/map to get the current list of sites.

I simply added a reference to Soapi.CSSuppose you would like to a winforms app, added Soapi.Responses.Users asbuild a data source and dragged it onto the form.

Subclassed WebBrowser so I could directly bind to about_mecomplete dataset containing all of your content and scrounged a nifty combo box implementation offactivity for all of the internet so I could display the site's icon.

The user is numeric updown responds to cursor keys so you can flip through users.

The project is in the source controlyour accounts on all Stack Exchange sites.

alt text http://www.freeimagehosting.net/uploads/781e8422b8.png You would need to:

alt text http://www.freeimagehosting.net/uploads/33281308a3.png

  1. Get a list of your accounts from stackauth /users/{id}/associated
  2. For each account
    1. Get all of your questions
    2. Get all of your answers
    3. Get all of your reputation events
    4. Get all of your timeline events

Complete source (minus form designer generated code):The following class does that with Soapi. It uses a separate thread for each site. The throttle and cache are thread-safe.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Net;
using System.Windows.Forms;
using Newtonsoft.Json;
using Soapi.Responses;
using System.Linq;
using System.Threading;
using Soapi;
using Soapi.Domain;

namespace Soapi.CS.SampleAppSamples
{
    public partial class Form1 : Form
    {
        private const string ApiKey = "qgAq_KfDu0KYzlNG-qaTuw";

        private readonly DALayer _dal;

        public Form1()
        {
            InitializeComponent();

            pictureBox1.DataBindings["ImageLocation"].Format +=
                (snd, arg) => { arg.Value = string.Format("http://www.gravatar.com/avatar/{0}?s=128", arg.Value); };

            _dal = new DALayer(ApiKey);

            comboBoxEx1.DropDownStyle = ComboBoxStyle.DropDownList;

            comboBoxEx1.ImageList = _dal.SmallIcons;

            for (int i = 0; i < _dal.ApiMap.Count; i++)
            {
                ApiMapItem apiMapItem = _dal.ApiMap[i];
                comboBoxEx1.Items.Add(new ComboBoxExItem(apiMapItem.DisplayName, i)
                    {
                        Tag = apiMapItem
                    });
                if (apiMapItem.SiteId == "sa")
                {
                    comboBoxEx1.SelectedIndex = i;
                }
            }
            FetchUser();
        }

        private void FetchUser()
        {
            usersBindingSource.DataSource = _dal.GetUser(((ComboBoxExItem) comboBoxEx1.SelectedItem).Tag.SiteId,
                                                         (int) numericUpDown1.Value);
        }

        private void numericUpDown1_KeyUp(object sender, KeyEventArgs e)
        {
            // forces a valueChanged
            numericUpDown1.Value = numericUpDown1.Value;
        }

        private void FetchUserEvent(object sender, EventArgs e)
        {
            FetchUser();
        }

        private void LaunchLinkLabel(object sender, EventArgs e)
        {
            Process.Start(((LinkLabel)sender).Text);
        }
    }


    /// <summary>
    /// ABuilds simplea datacomplete layerstack exchange dataset of a single user
    /// </summary>
    public class DALayerAllMyStuff
    {

        public Users GetUser(string siteId, int userId)
        {
            ApiMapItem apiMapItem = ApiMap.First(s => s.SiteId == siteId);
            Users user;
            try
            {

                List<Users> users =
                    new RouteFactory(apiMapItem.ApiPath, ApiKey)
                        .UsersById(userId)
                        .GetResponse().Items;

                user = users.Count == 1 ? users[0] : new Users();
            }
            catch
            {
                user = new Users();
            }
            return user;
        }

        private readonly string _apiKey;

        private readonly ImageList _smallIcons = new ImageList { ColorDepth = ColorDepth.Depth32Bit, ImageSize = new Size(32, 32) };

        private List<ApiMapItem> _apiMap;


        public DALayer(string apiKey)
        {
            _apiKey = apiKey;
            BuildMap();
        }

        /// <summary>
        /// Contains all your data on all sites
        /// </summary>
        public ImageListList<UserData> SmallIconsAccounts
        {
            get { return _smallIcons;_accounts; }
        }

        public string ApiKey
        {
            get { return _apiKey; }
        }

        public List<ApiMapItem> ApiMap
  private const string ApiKey = "Your {Key";
         private readonly List<UserData> get_accounts;
 { return _apiMap; }
     private readonly ApiContext }_context;

        private/// void<summary>
 BuildMap       /// Provide a reference user from which to get all 
        /// associated accounts.
        /// </summary>
        /// <param name="endpoint"></param>
        /// <param name="userId"></param>
        public AllMyStuff(string endpoint, int userId)
        {
            WebClient client2_context = new WebClientApiContext(ApiKey);
            string_context.Options.LazyLoadingEnabled(true);
 mapJson = client2         _context.DownloadStringInitialize("http://soapi.info/rest/map"false);
            _apiMap_accounts = (List<ApiMapItem>)JsonConvert_context.DeserializeObjectSites.First(mapJson,s typeof=> s.ApiEndpoint.StartsWith(List<ApiMapItem>endpoint));

            GetIcons();
     .User(userId).Associated.Select(u => new }UserData(u)).ToList();

        private void GetIcons()
        {
            WebClientvar clientinitEvent = new WebClientAutoResetEvent(false);

            foreach (ApiMapItemvar item in _apiMap_accounts)
            {
                using (
                    MemoryStreamUserData iconStreamuserData =
                        new MemoryStream(client.DownloadData(item.IconPath.Replace("{size}", "32"))))
                {item;
                    _smallIcons.Images.Add(item.SiteId, ImageThreadPool.FromStreamQueueUserWorkItem(iconStream));
                }
            }
        }
   o }
=>
    /// <summary>
    /// Class for deseralizing the API map from soap.info/rest/map
    /// </summary>
    public class ApiMapItem
    {
        [JsonProperty("site_id")]
        public string SiteId { get; set; }

        [JsonProperty("display_name")]
        public string DisplayName {User get;account set;= }userData.User;

        [JsonProperty                userData.Questions = account.Questions
                            .PageCount("base_path"-1)]
        public string BasePath { get; set; }              .FromDate(new DateTime(1980, 1, 1))
                            .ToDate(new DateTime(2020, 1, 1))
                            .ToList();

        [JsonProperty                userData.Answers = account.Answers
                            .PageCount("api_path"-1)]
        public string ApiPath { get; set; }              .FromDate(new DateTime(1980, 1, 1))
                            .ToDate(new DateTime(2020, 1, 1))
                            .ToList();

        [JsonProperty                userData.Reputation = account
                            .RepChanges
                            .PageCount("icon_path"-1)]
        public string IconPath { get; set; }              .FromDate(new DateTime(1980, 1, 1))
    }                        .ToDate(new DateTime(2020, 1, 1))
                            .ToList();

                        userData.Timeline = account
                            .Timeline
                            .PageCount(-1)
                            .FromDate(new DateTime(1980, 1, 1))
                            .ToDate(new DateTime(2020, 1, 1))
                            .ToList();

    /// <summary>
    /// Just added a bindable property to set HTML
    /// </summary>
    internal class BindableBrowser : WebBrowser
    {
        privateuserData.Initialized string= _html;true;

        [Bindable(true)]
        public string Html
       lock {(_accounts)
            get { return _html; }
            setif (_accounts.All(u => u.Initialized))
            {
                _html = value;  initEvent.Set();
                Navigate("about:blank"    });
            }
        }

        protected override void OnNavigated(WebBrowserNavigatedEventArgs e)
        {
            base.OnNavigated(e);
            if (Document != null)
                Document.Write(
                    stringinitEvent.FormatWaitOne(
                        "<html><head><link rel='stylesheet' href='http://stackapps.com/content/sa/all.css'></head><body style='background:#eaefef;'>{0}</body></html>",
                        _html ?? string.Empty));
        }
    }

    /// <summary>
    /// A simple Image combo found on the internet
    /// http://www.csharphelp.com/2006/08/combobox-with-images/
    /// </summary>
    internal class ComboBoxEx : ComboBox
    {
        private ImageList _imageList;

        public ComboBoxEx()
        {
            DrawMode = DrawMode.OwnerDrawFixed;
        }

        public ImageList ImageList
        {
            get { return _imageList; }
            set { _imageList = value; }
        }

        protected override void OnDrawItem(DrawItemEventArgs ea)
        {
            ea.DrawBackground();
            ea.DrawFocusRectangle();

            ComboBoxExItem item;
            Size imageSize = _imageList.ImageSize;
            Rectangle bounds = ea.Bounds;

            try
            {
                item = (ComboBoxExItem)Items[ea.Index];

                if (item.ImageIndex != -1)
                {
                    _imageList.Draw(ea.Graphics, bounds.Left, bounds.Top,
                                    item.ImageIndex);
                    ea.Graphics.DrawString(item.Text, ea.Font, new
                                                                   SolidBrush(ea.ForeColor),
                                           bounds.Left + imageSize.Width, bounds.Top);
                }
                else
                {
                    ea.Graphics.DrawString(item.Text, ea.Font, new
                                                                   SolidBrush(ea.ForeColor), bounds.Left, bounds.Top);
                }
            }
            catch
            {
                if (ea.Index != -1)
                {
                    ea.Graphics.DrawString(Items[ea.Index].ToString(), ea.Font, new
                                                                                    SolidBrush(ea.ForeColor),
                                           bounds.Left, bounds.Top);
                }
                else
                {
                    ea.Graphics.DrawString(Text, ea.Font, new
                                                              SolidBrush(ea.ForeColor), bounds.Left, bounds.Top);
                }
            }

            base.OnDrawItem(ea);
        }
    }

    internalpublic class ComboBoxExItemUserData
    {
        public ApiMapItem Tag;

        public ComboBoxExItem()
            : this("")
        {
        }

        public ComboBoxExItem(string text)
            : this(text, -1)
        {
        }

        public ComboBoxExItemUserData(string text, intUser imageIndexuser)
        {
            TextUser = text;user;
            ImageIndexApiEndpoint = imageIndex;user.Site.ApiEndpoint;
        }

        public stringList<Answer> TextAnswers { get; set; }
 
        public intstring ImageIndexApiEndpoint { get; set; }
        public bool Initialized { get; set; }
        public overrideList<Question> stringQuestions ToString(){ get; set; }
        public List<RepChange> Reputation { get; set; }
        public List<UserTimeline> Timeline { returnget; Text;set; }
        public User User { get; set; }
    }
}

}

As a simple demonstration I spend about 30 minutes and wrote User Display. The code demonstrates using the api endpoint discovery service at http://soapi.info/rest/map to get the current list of sites.

I simply added a reference to Soapi.CS to a winforms app, added Soapi.Responses.Users as a data source and dragged it onto the form.

Subclassed WebBrowser so I could directly bind to about_me and scrounged a nifty combo box implementation off of the internet so I could display the site's icon.

The user is numeric updown responds to cursor keys so you can flip through users.

The project is in the source control.

alt text http://www.freeimagehosting.net/uploads/781e8422b8.png

alt text http://www.freeimagehosting.net/uploads/33281308a3.png

Complete source (minus form designer generated code):

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Net;
using System.Windows.Forms;
using Newtonsoft.Json;
using Soapi.Responses;
using System.Linq;

namespace Soapi.CS.SampleApp
{
    public partial class Form1 : Form
    {
        private const string ApiKey = "qgAq_KfDu0KYzlNG-qaTuw";

        private readonly DALayer _dal;

        public Form1()
        {
            InitializeComponent();

            pictureBox1.DataBindings["ImageLocation"].Format +=
                (snd, arg) => { arg.Value = string.Format("http://www.gravatar.com/avatar/{0}?s=128", arg.Value); };

            _dal = new DALayer(ApiKey);

            comboBoxEx1.DropDownStyle = ComboBoxStyle.DropDownList;

            comboBoxEx1.ImageList = _dal.SmallIcons;

            for (int i = 0; i < _dal.ApiMap.Count; i++)
            {
                ApiMapItem apiMapItem = _dal.ApiMap[i];
                comboBoxEx1.Items.Add(new ComboBoxExItem(apiMapItem.DisplayName, i)
                    {
                        Tag = apiMapItem
                    });
                if (apiMapItem.SiteId == "sa")
                {
                    comboBoxEx1.SelectedIndex = i;
                }
            }
            FetchUser();
        }

        private void FetchUser()
        {
            usersBindingSource.DataSource = _dal.GetUser(((ComboBoxExItem) comboBoxEx1.SelectedItem).Tag.SiteId,
                                                         (int) numericUpDown1.Value);
        }

        private void numericUpDown1_KeyUp(object sender, KeyEventArgs e)
        {
            // forces a valueChanged
            numericUpDown1.Value = numericUpDown1.Value;
        }

        private void FetchUserEvent(object sender, EventArgs e)
        {
            FetchUser();
        }

        private void LaunchLinkLabel(object sender, EventArgs e)
        {
            Process.Start(((LinkLabel)sender).Text);
        }
    }


    /// <summary>
    /// A simple data layer 
    /// </summary>
    public class DALayer
    {

        public Users GetUser(string siteId, int userId)
        {
            ApiMapItem apiMapItem = ApiMap.First(s => s.SiteId == siteId);
            Users user;
            try
            {

                List<Users> users =
                    new RouteFactory(apiMapItem.ApiPath, ApiKey)
                        .UsersById(userId)
                        .GetResponse().Items;

                user = users.Count == 1 ? users[0] : new Users();
            }
            catch
            {
                user = new Users();
            }
            return user;
        }

        private readonly string _apiKey;

        private readonly ImageList _smallIcons = new ImageList { ColorDepth = ColorDepth.Depth32Bit, ImageSize = new Size(32, 32) };

        private List<ApiMapItem> _apiMap;


        public DALayer(string apiKey)
        {
            _apiKey = apiKey;
            BuildMap();
        }

        public ImageList SmallIcons
        {
            get { return _smallIcons; }
        }

        public string ApiKey
        {
            get { return _apiKey; }
        }

        public List<ApiMapItem> ApiMap
        {
            get { return _apiMap; }
        }

        private void BuildMap()
        {
            WebClient client2 = new WebClient();
            string mapJson = client2.DownloadString("http://soapi.info/rest/map");
            _apiMap = (List<ApiMapItem>)JsonConvert.DeserializeObject(mapJson, typeof(List<ApiMapItem>));

            GetIcons();
        }

        private void GetIcons()
        {
            WebClient client = new WebClient();

            foreach (ApiMapItem item in _apiMap)
            {
                using (
                    MemoryStream iconStream =
                        new MemoryStream(client.DownloadData(item.IconPath.Replace("{size}", "32"))))
                {
                    _smallIcons.Images.Add(item.SiteId, Image.FromStream(iconStream));
                }
            }
        }
    }

    /// <summary>
    /// Class for deseralizing the API map from soap.info/rest/map
    /// </summary>
    public class ApiMapItem
    {
        [JsonProperty("site_id")]
        public string SiteId { get; set; }

        [JsonProperty("display_name")]
        public string DisplayName { get; set; }

        [JsonProperty("base_path")]
        public string BasePath { get; set; }

        [JsonProperty("api_path")]
        public string ApiPath { get; set; }

        [JsonProperty("icon_path")]
        public string IconPath { get; set; }
    }


    /// <summary>
    /// Just added a bindable property to set HTML
    /// </summary>
    internal class BindableBrowser : WebBrowser
    {
        private string _html;

        [Bindable(true)]
        public string Html
        {
            get { return _html; }
            set
            {
                _html = value;
                Navigate("about:blank");
            }
        }

        protected override void OnNavigated(WebBrowserNavigatedEventArgs e)
        {
            base.OnNavigated(e);
            if (Document != null)
                Document.Write(
                    string.Format(
                        "<html><head><link rel='stylesheet' href='http://stackapps.com/content/sa/all.css'></head><body style='background:#eaefef;'>{0}</body></html>",
                        _html ?? string.Empty));
        }
    }

    /// <summary>
    /// A simple Image combo found on the internet
    /// http://www.csharphelp.com/2006/08/combobox-with-images/
    /// </summary>
    internal class ComboBoxEx : ComboBox
    {
        private ImageList _imageList;

        public ComboBoxEx()
        {
            DrawMode = DrawMode.OwnerDrawFixed;
        }

        public ImageList ImageList
        {
            get { return _imageList; }
            set { _imageList = value; }
        }

        protected override void OnDrawItem(DrawItemEventArgs ea)
        {
            ea.DrawBackground();
            ea.DrawFocusRectangle();

            ComboBoxExItem item;
            Size imageSize = _imageList.ImageSize;
            Rectangle bounds = ea.Bounds;

            try
            {
                item = (ComboBoxExItem)Items[ea.Index];

                if (item.ImageIndex != -1)
                {
                    _imageList.Draw(ea.Graphics, bounds.Left, bounds.Top,
                                    item.ImageIndex);
                    ea.Graphics.DrawString(item.Text, ea.Font, new
                                                                   SolidBrush(ea.ForeColor),
                                           bounds.Left + imageSize.Width, bounds.Top);
                }
                else
                {
                    ea.Graphics.DrawString(item.Text, ea.Font, new
                                                                   SolidBrush(ea.ForeColor), bounds.Left, bounds.Top);
                }
            }
            catch
            {
                if (ea.Index != -1)
                {
                    ea.Graphics.DrawString(Items[ea.Index].ToString(), ea.Font, new
                                                                                    SolidBrush(ea.ForeColor),
                                           bounds.Left, bounds.Top);
                }
                else
                {
                    ea.Graphics.DrawString(Text, ea.Font, new
                                                              SolidBrush(ea.ForeColor), bounds.Left, bounds.Top);
                }
            }

            base.OnDrawItem(ea);
        }
    }

    internal class ComboBoxExItem
    {
        public ApiMapItem Tag;

        public ComboBoxExItem()
            : this("")
        {
        }

        public ComboBoxExItem(string text)
            : this(text, -1)
        {
        }

        public ComboBoxExItem(string text, int imageIndex)
        {
            Text = text;
            ImageIndex = imageIndex;
        }

        public string Text { get; set; }
 
        public int ImageIndex { get; set; }

        public override string ToString()
        {
            return Text;
        }
    }

}

Suppose you would like to build a complete dataset containing all of your content and activity for all of your accounts on all Stack Exchange sites.

You would need to:

  1. Get a list of your accounts from stackauth /users/{id}/associated
  2. For each account
    1. Get all of your questions
    2. Get all of your answers
    3. Get all of your reputation events
    4. Get all of your timeline events

The following class does that with Soapi. It uses a separate thread for each site. The throttle and cache are thread-safe.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Soapi;
using Soapi.Domain;

namespace Samples
{
    /// <summary>
    /// Builds a complete stack exchange dataset of a single user
    /// </summary>
    public class AllMyStuff
    {

        /// <summary>
        /// Contains all your data on all sites
        /// </summary>
        public List<UserData> Accounts
        {
            get { return _accounts; }
        }


        private const string ApiKey = "Your Key";
        private readonly List<UserData> _accounts;
        private readonly ApiContext _context;

        /// <summary>
        /// Provide a reference user from which to get all 
        /// associated accounts.
        /// </summary>
        /// <param name="endpoint"></param>
        /// <param name="userId"></param>
        public AllMyStuff(string endpoint, int userId)
        {
            _context = new ApiContext(ApiKey);
            _context.Options.LazyLoadingEnabled(true);
            _context.Initialize(false);
            _accounts = _context.Sites.First(s => s.ApiEndpoint.StartsWith(endpoint))
                .User(userId).Associated.Select(u => new UserData(u)).ToList();

            var initEvent = new AutoResetEvent(false);

            foreach (var item in _accounts)
            {
                UserData userData = item;
                ThreadPool.QueueUserWorkItem(o =>
                    {
                        User account = userData.User;

                        userData.Questions = account.Questions
                            .PageCount(-1)
                            .FromDate(new DateTime(1980, 1, 1))
                            .ToDate(new DateTime(2020, 1, 1))
                            .ToList();

                        userData.Answers = account.Answers
                            .PageCount(-1)
                            .FromDate(new DateTime(1980, 1, 1))
                            .ToDate(new DateTime(2020, 1, 1))
                            .ToList();

                        userData.Reputation = account
                            .RepChanges
                            .PageCount(-1)
                            .FromDate(new DateTime(1980, 1, 1))
                            .ToDate(new DateTime(2020, 1, 1))
                            .ToList();

                        userData.Timeline = account
                            .Timeline
                            .PageCount(-1)
                            .FromDate(new DateTime(1980, 1, 1))
                            .ToDate(new DateTime(2020, 1, 1))
                            .ToList();

                        userData.Initialized = true;

                        lock (_accounts)
                            if (_accounts.All(u => u.Initialized))
                                initEvent.Set();
                    });
            }
            initEvent.WaitOne();
        }

 
    }

    public class UserData
    {
        public UserData(User user)
        {
            User = user;
            ApiEndpoint = user.Site.ApiEndpoint;
        }

        public List<Answer> Answers { get; set; }
        public string ApiEndpoint { get; set; }
        public bool Initialized { get; set; }
        public List<Question> Questions { get; set; }
        public List<RepChange> Reputation { get; set; }
        public List<UserTimeline> Timeline { get; set; }
        public User User { get; set; }
    }
}
Post Deleted by Sky Sanders
Source Link
Sky Sanders
  • 12.1k
  • 3
  • 32
  • 60

##Building a simple app with Soapi.CS

As a simple demonstration I spend about 30 minutes and wrote User Display. The code demonstrates using the api endpoint discovery service at http://soapi.info/rest/map to get the current list of sites.

I simply added a reference to Soapi.CS to a winforms app, added Soapi.Responses.Users as a data source and dragged it onto the form.

Subclassed WebBrowser so I could directly bind to about_me and scrounged a nifty combo box implementation off of the internet so I could display the site's icon.

The user is numeric updown responds to cursor keys so you can flip through users.

The project is in the source control.

alt text http://www.freeimagehosting.net/uploads/781e8422b8.png

alt text http://www.freeimagehosting.net/uploads/33281308a3.png

Complete source (minus form designer generated code):

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Net;
using System.Windows.Forms;
using Newtonsoft.Json;
using Soapi.Responses;
using System.Linq;

namespace Soapi.CS.SampleApp
{
    public partial class Form1 : Form
    {
        private const string ApiKey = "qgAq_KfDu0KYzlNG-qaTuw";

        private readonly DALayer _dal;

        public Form1()
        {
            InitializeComponent();

            pictureBox1.DataBindings["ImageLocation"].Format +=
                (snd, arg) => { arg.Value = string.Format("http://www.gravatar.com/avatar/{0}?s=128", arg.Value); };

            _dal = new DALayer(ApiKey);

            comboBoxEx1.DropDownStyle = ComboBoxStyle.DropDownList;

            comboBoxEx1.ImageList = _dal.SmallIcons;

            for (int i = 0; i < _dal.ApiMap.Count; i++)
            {
                ApiMapItem apiMapItem = _dal.ApiMap[i];
                comboBoxEx1.Items.Add(new ComboBoxExItem(apiMapItem.DisplayName, i)
                    {
                        Tag = apiMapItem
                    });
                if (apiMapItem.SiteId == "sa")
                {
                    comboBoxEx1.SelectedIndex = i;
                }
            }
            FetchUser();
        }

        private void FetchUser()
        {
            usersBindingSource.DataSource = _dal.GetUser(((ComboBoxExItem) comboBoxEx1.SelectedItem).Tag.SiteId,
                                                         (int) numericUpDown1.Value);
        }

        private void numericUpDown1_KeyUp(object sender, KeyEventArgs e)
        {
            // forces a valueChanged
            numericUpDown1.Value = numericUpDown1.Value;
        }

        private void FetchUserEvent(object sender, EventArgs e)
        {
            FetchUser();
        }

        private void LaunchLinkLabel(object sender, EventArgs e)
        {
            Process.Start(((LinkLabel)sender).Text);
        }
    }


    /// <summary>
    /// A simple data layer 
    /// </summary>
    public class DALayer
    {

        public Users GetUser(string siteId, int userId)
        {
            ApiMapItem apiMapItem = ApiMap.First(s => s.SiteId == siteId);
            Users user;
            try
            {

                List<Users> users =
                    new RouteFactory(apiMapItem.ApiPath, ApiKey)
                        .UsersById(userId)
                        .GetResponse().Items;

                user = users.Count == 1 ? users[0] : new Users();
            }
            catch
            {
                user = new Users();
            }
            return user;
        }

        private readonly string _apiKey;

        private readonly ImageList _smallIcons = new ImageList { ColorDepth = ColorDepth.Depth32Bit, ImageSize = new Size(32, 32) };

        private List<ApiMapItem> _apiMap;


        public DALayer(string apiKey)
        {
            _apiKey = apiKey;
            BuildMap();
        }

        public ImageList SmallIcons
        {
            get { return _smallIcons; }
        }

        public string ApiKey
        {
            get { return _apiKey; }
        }

        public List<ApiMapItem> ApiMap
        {
            get { return _apiMap; }
        }

        private void BuildMap()
        {
            WebClient client2 = new WebClient();
            string mapJson = client2.DownloadString("http://soapi.info/rest/map");
            _apiMap = (List<ApiMapItem>)JsonConvert.DeserializeObject(mapJson, typeof(List<ApiMapItem>));

            GetIcons();
        }

        private void GetIcons()
        {
            WebClient client = new WebClient();

            foreach (ApiMapItem item in _apiMap)
            {
                using (
                    MemoryStream iconStream =
                        new MemoryStream(client.DownloadData(item.IconPath.Replace("{size}", "32"))))
                {
                    _smallIcons.Images.Add(item.SiteId, Image.FromStream(iconStream));
                }
            }
        }
    }

    /// <summary>
    /// Class for deseralizing the API map from soap.info/rest/map
    /// </summary>
    public class ApiMapItem
    {
        [JsonProperty("site_id")]
        public string SiteId { get; set; }

        [JsonProperty("display_name")]
        public string DisplayName { get; set; }

        [JsonProperty("base_path")]
        public string BasePath { get; set; }

        [JsonProperty("api_path")]
        public string ApiPath { get; set; }

        [JsonProperty("icon_path")]
        public string IconPath { get; set; }
    }


    /// <summary>
    /// Just added a bindable property to set HTML
    /// </summary>
    internal class BindableBrowser : WebBrowser
    {
        private string _html;

        [Bindable(true)]
        public string Html
        {
            get { return _html; }
            set
            {
                _html = value;
                Navigate("about:blank");
            }
        }

        protected override void OnNavigated(WebBrowserNavigatedEventArgs e)
        {
            base.OnNavigated(e);
            if (Document != null)
                Document.Write(
                    string.Format(
                        "<html><head><link rel='stylesheet' href='http://stackapps.com/content/sa/all.css'></head><body style='background:#eaefef;'>{0}</body></html>",
                        _html ?? string.Empty));
        }
    }

    /// <summary>
    /// A simple Image combo found on the internet
    /// http://www.csharphelp.com/2006/08/combobox-with-images/
    /// </summary>
    internal class ComboBoxEx : ComboBox
    {
        private ImageList _imageList;

        public ComboBoxEx()
        {
            DrawMode = DrawMode.OwnerDrawFixed;
        }

        public ImageList ImageList
        {
            get { return _imageList; }
            set { _imageList = value; }
        }

        protected override void OnDrawItem(DrawItemEventArgs ea)
        {
            ea.DrawBackground();
            ea.DrawFocusRectangle();

            ComboBoxExItem item;
            Size imageSize = _imageList.ImageSize;
            Rectangle bounds = ea.Bounds;

            try
            {
                item = (ComboBoxExItem)Items[ea.Index];

                if (item.ImageIndex != -1)
                {
                    _imageList.Draw(ea.Graphics, bounds.Left, bounds.Top,
                                    item.ImageIndex);
                    ea.Graphics.DrawString(item.Text, ea.Font, new
                                                                   SolidBrush(ea.ForeColor),
                                           bounds.Left + imageSize.Width, bounds.Top);
                }
                else
                {
                    ea.Graphics.DrawString(item.Text, ea.Font, new
                                                                   SolidBrush(ea.ForeColor), bounds.Left, bounds.Top);
                }
            }
            catch
            {
                if (ea.Index != -1)
                {
                    ea.Graphics.DrawString(Items[ea.Index].ToString(), ea.Font, new
                                                                                    SolidBrush(ea.ForeColor),
                                           bounds.Left, bounds.Top);
                }
                else
                {
                    ea.Graphics.DrawString(Text, ea.Font, new
                                                              SolidBrush(ea.ForeColor), bounds.Left, bounds.Top);
                }
            }

            base.OnDrawItem(ea);
        }
    }

    internal class ComboBoxExItem
    {
        public ApiMapItem Tag;

        public ComboBoxExItem()
            : this("")
        {
        }

        public ComboBoxExItem(string text)
            : this(text, -1)
        {
        }

        public ComboBoxExItem(string text, int imageIndex)
        {
            Text = text;
            ImageIndex = imageIndex;
        }

        public string Text { get; set; }

        public int ImageIndex { get; set; }

        public override string ToString()
        {
            return Text;
        }
    }

}