RSS weather layer
WeatherItemSelectionDlg.cs
// Copyright 2010 ESRI
// 
// All rights reserved under the copyright laws of the United States
// and applicable international laws, treaties, and conventions.
// 
// You may freely redistribute and use this sample code, with or
// without modification, provided you include the original copyright
// notice and use restrictions.
// 
// See the use restrictions.
// 

using System;
using System.Data;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Threading;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.esriSystem;

namespace RSSWeatherLayer
{
  /// <summary>
  /// Select by city name dialog
  /// </summary>
  /// <remarks>Allows users to select items according to city names.</remarks>
  public class WeatherItemSelectionDlg : System.Windows.Forms.Form
  {
    private System.Windows.Forms.GroupBox grpWeatherItems;
    private System.Windows.Forms.ListBox lstWeatherItemNames;
    private System.Windows.Forms.Button btnRefreshList;
    private System.Windows.Forms.Label lblSelect;
    private System.Windows.Forms.TextBox txtSelect;
    private System.Windows.Forms.CheckBox chkNewSelection;
    private System.Windows.Forms.Button btnSelect;
    private System.Windows.Forms.Button btnDismiss;
    private System.Windows.Forms.ProgressBar progressBar1;
    private System.Windows.Forms.ContextMenu contextMenu1;
    private System.Windows.Forms.MenuItem menuZoomTo;

    //Class members
    private IActiveView                m_activeView          = null;
    private RSSWeatherLayerClass    m_weatherLayer        = null;
    private string[]                  m_cityNames            = null;
    private DataTable                  m_weatherItemsTable    = null;


    // This delegate enables asynchronous calls for setting
    // the text property on a TextBox control.
    private delegate void IncrementProgressBarCallback();
    private delegate void AddListItmCallback(string item);
    private delegate void ShowProgressBarCallBack();
    private delegate void HideProgressBarCallBack();


    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.Container components = null;

    /// <summary>
    /// class constructor
    /// </summary>
    /// <param name="weatherLayer"></param>
    public WeatherItemSelectionDlg(RSSWeatherLayerClass weatherLayer, IActiveView activeView)
    {

      InitializeComponent();

      //get the layer
      m_weatherLayer  =  weatherLayer;
      m_activeView    =  activeView;

      //get the list of all citynames for all items in the layer
      m_cityNames      = m_weatherLayer.GetCityNames();

      //create a table to host the citynames
      m_weatherItemsTable = new DataTable("CityNames");
      m_weatherItemsTable.Columns.Add("CITYNAME", typeof(string));

      //populate the listbox and build a table containing the items
      PopulateList();
    }

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    protected override void Dispose( bool disposing )
    {
      if( disposing )
      {
        if(components != null)
        {
          components.Dispose();
        }
      }
      base.Dispose( disposing );
    }

    #region Windows Form Designer generated code
    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
      this.grpWeatherItems = new System.Windows.Forms.GroupBox();
      this.progressBar1 = new System.Windows.Forms.ProgressBar();
      this.txtSelect = new System.Windows.Forms.TextBox();
      this.lblSelect = new System.Windows.Forms.Label();
      this.btnRefreshList = new System.Windows.Forms.Button();
      this.lstWeatherItemNames = new System.Windows.Forms.ListBox();
      this.contextMenu1 = new System.Windows.Forms.ContextMenu();
      this.menuZoomTo = new System.Windows.Forms.MenuItem();
      this.chkNewSelection = new System.Windows.Forms.CheckBox();
      this.btnSelect = new System.Windows.Forms.Button();
      this.btnDismiss = new System.Windows.Forms.Button();
      this.grpWeatherItems.SuspendLayout();
      this.SuspendLayout();
      // 
      // grpWeatherItems
      // 
      this.grpWeatherItems.Controls.Add(this.progressBar1);
      this.grpWeatherItems.Controls.Add(this.txtSelect);
      this.grpWeatherItems.Controls.Add(this.lblSelect);
      this.grpWeatherItems.Controls.Add(this.btnRefreshList);
      this.grpWeatherItems.Controls.Add(this.lstWeatherItemNames);
      this.grpWeatherItems.Location = new System.Drawing.Point(4, 8);
      this.grpWeatherItems.Name = "grpWeatherItems";
      this.grpWeatherItems.Size = new System.Drawing.Size(200, 328);
      this.grpWeatherItems.TabIndex = 0;
      this.grpWeatherItems.TabStop = false;
      // 
      // progressBar1
      // 
      this.progressBar1.Location = new System.Drawing.Point(8, 256);
      this.progressBar1.Name = "progressBar1";
      this.progressBar1.Size = new System.Drawing.Size(184, 23);
      this.progressBar1.Step = 1;
      this.progressBar1.TabIndex = 4;
      // 
      // txtSelect
      // 
      this.txtSelect.Location = new System.Drawing.Point(92, 296);
      this.txtSelect.Name = "txtSelect";
      this.txtSelect.TabIndex = 3;
      this.txtSelect.Text = "";
      this.txtSelect.TextChanged += new System.EventHandler(this.txtSelect_TextChanged);
      // 
      // lblSelect
      // 
      this.lblSelect.Location = new System.Drawing.Point(8, 300);
      this.lblSelect.Name = "lblSelect";
      this.lblSelect.Size = new System.Drawing.Size(52, 16);
      this.lblSelect.TabIndex = 2;
      this.lblSelect.Text = "Select";
      // 
      // btnRefreshList
      // 
      this.btnRefreshList.Location = new System.Drawing.Point(64, 256);
      this.btnRefreshList.Name = "btnRefreshList";
      this.btnRefreshList.TabIndex = 1;
      this.btnRefreshList.Text = "Refresh List";
      this.btnRefreshList.Click += new System.EventHandler(this.btnRefreshList_Click);
      // 
      // lstWeatherItemNames
      // 
      this.lstWeatherItemNames.ContextMenu = this.contextMenu1;
      this.lstWeatherItemNames.Location = new System.Drawing.Point(8, 16);
      this.lstWeatherItemNames.Name = "lstWeatherItemNames";
      this.lstWeatherItemNames.Size = new System.Drawing.Size(184, 225);
      this.lstWeatherItemNames.Sorted = true;
      this.lstWeatherItemNames.TabIndex = 0;
      this.lstWeatherItemNames.MouseDown += new System.Windows.Forms.MouseEventHandler(this.lstWeatherItemNames_MouseDown);
      this.lstWeatherItemNames.DoubleClick += new System.EventHandler(this.lstWeatherItemNames_DoubleClick);
      // 
      // contextMenu1
      // 
      this.contextMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
                                                                                 this.menuZoomTo});
      // 
      // menuZoomTo
      // 
      this.menuZoomTo.Index = 0;
      this.menuZoomTo.Text = "Zoom To";
      this.menuZoomTo.Click += new System.EventHandler(this.menuZoomTo_Click);
      // 
      // chkNewSelection
      // 
      this.chkNewSelection.Checked = true;
      this.chkNewSelection.CheckState = System.Windows.Forms.CheckState.Checked;
      this.chkNewSelection.Location = new System.Drawing.Point(12, 344);
      this.chkNewSelection.Name = "chkNewSelection";
      this.chkNewSelection.TabIndex = 1;
      this.chkNewSelection.Text = "New Selection";
      // 
      // btnSelect
      // 
      this.btnSelect.Location = new System.Drawing.Point(8, 380);
      this.btnSelect.Name = "btnSelect";
      this.btnSelect.TabIndex = 2;
      this.btnSelect.Text = "Select";
      this.btnSelect.Click += new System.EventHandler(this.btnSelect_Click);
      // 
      // btnDismiss
      // 
      this.btnDismiss.Location = new System.Drawing.Point(124, 384);
      this.btnDismiss.Name = "btnDismiss";
      this.btnDismiss.TabIndex = 3;
      this.btnDismiss.Text = "Dismiss";
      this.btnDismiss.Click += new System.EventHandler(this.btnDismiss_Click);
      // 
      // WeatherItemSelectionDlg
      // 
      this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
      this.ClientSize = new System.Drawing.Size(206, 416);
      this.Controls.Add(this.btnDismiss);
      this.Controls.Add(this.btnSelect);
      this.Controls.Add(this.chkNewSelection);
      this.Controls.Add(this.grpWeatherItems);
      this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
      this.Name = "WeatherItemSelectionDlg";
      this.ShowInTaskbar = false;
      this.Text = "Select weather item";
      this.TopMost = true;
      this.Load += new System.EventHandler(this.WeatherItemSelectionDlg_Load);
      this.VisibleChanged += new System.EventHandler(this.WeatherItemSelectionDlg_VisibleChanged);
      this.grpWeatherItems.ResumeLayout(false);
      this.ResumeLayout(false);

    }
    #endregion

    /// <summary>
    /// Event handler for Form::Load
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void WeatherItemSelectionDlg_Load(object sender, System.EventArgs e)
    {
      txtSelect.Text = "";
      //btnRefreshList.Visible  = true;
      //progressBar1.Visible    = false;
      lstWeatherItemNames.ClearSelected();
    }

    /// <summary>
    /// dialog visible change event handler
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void WeatherItemSelectionDlg_VisibleChanged(object sender, System.EventArgs e)
    {
      if(this.Visible)
      {
        txtSelect.Text = "";
        lstWeatherItemNames.ClearSelected();
      }
    }

    /// <summary>
    /// listbox's event handler for mousedown
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void lstWeatherItemNames_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
    {
      //if right click then select a record
      if(e.Button == MouseButtons.Right)
      {
        //given the point, return the item index
        Point pt = new Point(e.X,e.Y);
        int index = lstWeatherItemNames.IndexFromPoint(pt);
        if(index > 0)
        {
          //select the item pointed by the index
          lstWeatherItemNames.ClearSelected();
          lstWeatherItemNames.SelectedIndex = index;
          lstWeatherItemNames.Refresh();
        }
      }  
    }

    /// <summary>
    /// listbox's double-click event handler
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void lstWeatherItemNames_DoubleClick(object sender, System.EventArgs e)
    {
      //set the dialog results to OK
      this.DialogResult = DialogResult.OK;

      //select the items which the user double-clicked
      SelectWeatherItems();
    }

    /// <summary>
    /// refresh button click event handler
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void btnRefreshList_Click(object sender, System.EventArgs e)
    {
      //clear all the items on the list
      m_weatherItemsTable.Rows.Clear();

      //get an up-to-date list of citynames
      m_cityNames = m_weatherLayer.GetCityNames();

      //add the citynames to the listbox
      PopulateList();
    }


    /// <summary>
    /// selection textbox text change event handler
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void txtSelect_TextChanged(object sender, System.EventArgs e)
    {
      //clear the items of the listbox
      lstWeatherItemNames.Items.Clear();

      //spawn a thread to populate the list with items that match the selection criteria
      Thread t = new Thread(new ThreadStart(PopulateSubListProc));
      t.Start();
    }

    /// <summary>
    /// Select button click event handler
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void btnSelect_Click(object sender, System.EventArgs e)
    {
      //set the dialog results to OK
      this.DialogResult = DialogResult.OK;

      //select all the weather items with are selected in the listbox
      SelectWeatherItems();
    }


    /// <summary>
    /// dismiss button event handler
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void btnDismiss_Click(object sender, System.EventArgs e)
    {
      //set the dialog results to OK
      this.DialogResult = DialogResult.No;

      //hide the dialog
      this.Hide();
    }

    /// <summary>
    /// Populate the listbox with the citynames
    /// </summary>
    private void PopulateList()
    {
      //spawn the population thread (it populate both the listbox and the DataTable)
      Thread t = new Thread(new ThreadStart(PopulateWeatherItemsTableProc));
      t.Start();

      return;
    }

    /// <summary>
    /// Populate the listbox with the layer's list of cityNames
    /// </summary>
    private void PopulateWeatherItemsTableProc()
    {
      //hide the refresh button and show the progressbar
      ShowProgressBar();

      //iterate through the citynames
      foreach(string s in m_cityNames)
      {
        //create new record
        DataRow r = m_weatherItemsTable.NewRow();

        //add the cityname to the record
        r[0] = s;

        //add the record to the table
        lock(m_weatherItemsTable)
        {
          m_weatherItemsTable.Rows.Add(r);
        }
        
        //add the cityName to the listbox
        AddListItemString(s);

        //set the progress of the progressbar
        IncrementProgressBar();
      }

      //hide the progressbar and show the refresh button
      HideProgressBar();
    }

    /// <summary>
    /// Make Thread-Safe Calls to Windows Forms Controls
    /// </summary>
    /// <param name="item"></param>
    private void AddListItemString(string item)
    {
      // InvokeRequired required compares the thread ID of the
      // calling thread to the thread ID of the creating thread.
      // If these threads are different, it returns true.
      if (this.lstWeatherItemNames.InvokeRequired)
      {
        //call itself on the main thread
        AddListItmCallback d = new AddListItmCallback(AddListItemString);
        this.Invoke(d, new object[] { item });
      }
      else
      {
        //guaranteed to run on the main UI thread
        this.lstWeatherItemNames.Items.Add(item);
      }
    }

    /// <summary>
    /// show the progressbar and hide the refresh button
    /// </summary>
    private void ShowProgressBar()
    {
      //test whether Invoke is required (was this call made on a different thread than the main thread?)
      if (this.lstWeatherItemNames.InvokeRequired)
      {
        //call itself on the main thread
        ShowProgressBarCallBack d = new ShowProgressBarCallBack(ShowProgressBar);
        this.Invoke(d);
      }
      else
      {
        //clear all the rows from the table
        m_weatherItemsTable.Rows.Clear();

        //clear all the items of the listbox
        lstWeatherItemNames.Items.Clear();

        //set the progressbar properties
        int count = m_cityNames.Length;
        progressBar1.Maximum = count;
        progressBar1.Value = 0;
        btnRefreshList.Visible = false;
        progressBar1.Visible = true;
        this.UpdateStyles();
      }
    }

    /// <summary>
    /// hide the progressbar and show the refresh button
    /// </summary>
    private void HideProgressBar()
    {
      //test whether Invoke is required (was this call made on a different thread than the main thread?)
      if (this.progressBar1.InvokeRequired)
      {
        //call itself on the main thread
        ShowProgressBarCallBack d = new ShowProgressBarCallBack(HideProgressBar);
        this.Invoke(d);
      }
      else
      {
        //set the visibility
        btnRefreshList.Visible = true;
        progressBar1.Visible = false;
      }
    }

    /// <summary>
    /// increments the progressbar of the refresh list button
    /// </summary>
    private void IncrementProgressBar()
    {
      // InvokeRequired required compares the thread ID of the
      // calling thread to the thread ID of the creating thread.
      // If these threads are different, it returns true.
      if (this.progressBar1.InvokeRequired)
      {
        //call itself on the main thread
        IncrementProgressBarCallback d = new IncrementProgressBarCallback(IncrementProgressBar);
        this.Invoke(d);
      }
      else
      {
        this.progressBar1.Increment(1);
      }

    }

    /// <summary>
    /// select a weather items given selected items from the listbox
    /// </summary>
    private void SelectWeatherItems()
    {
      //get the selected list from the listbox
      bool newSelection = this.chkNewSelection.Checked;

      //in case of a new selection, unselect all items first
      if(newSelection)
        m_weatherLayer.UnselectAll();

      IPropertySet propSet = null;
      object o;
      long zipCode;
      //iterate through the selected items of the listbox
      foreach(int index in lstWeatherItemNames.SelectedIndices)
      {
        //get the weatheritem properties according to the zipCode of the item in the listbox
        propSet = m_weatherLayer.GetWeatherItem(Convert.ToString(lstWeatherItemNames.Items[index]));
        if(null == propSet)
          continue;

        o = propSet.GetProperty("ZIPCODE");
        if(null == o)
          continue;
        
        zipCode = Convert.ToInt64(o);

        //select the item in the weather layer
        m_weatherLayer.Select(zipCode, false);
      }

      //refresh the display
      m_activeView.PartialRefresh(esriViewDrawPhase.esriViewGeography, m_weatherLayer, m_activeView.Extent);
      m_activeView.ScreenDisplay.UpdateWindow();
    }


    /// <summary>
    /// Populate the listbox according to a selection criteria
    /// </summary>
    private void PopulateSubListProc()
    {
      //get the selection criteria
      string exp = txtSelect.Text;

      //in case that the user did not specify a criteria, populate the entire citiname list
      if(exp == "")
      {
        PopulateWeatherItemsTableProc();
        return;
      }

      //set query
      exp = "CITYNAME LIKE '" + exp + "%'";

      //do the criteria selection against the table
      DataRow[] rows = m_weatherItemsTable.Select(exp);

      //iterate through the selectionset
      foreach(DataRow r in rows)
      {
        //add the cityName to the listbox
        AddListItemString(Convert.ToString(r[0]));
      }
    }

    /// <summary>
    /// ZoomTo menu click event handler
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void menuZoomTo_Click(object sender, System.EventArgs e)
    {
      if(null == m_weatherLayer)
        return;

      //get the selected item
      string cityName = Convert.ToString(lstWeatherItemNames.SelectedItem);

      //ask the layer to zoom to that cityname
      m_weatherLayer.ZoomTo(cityName);
    }
  }
}