WPF Dialogs

by MikeHogg 15. July 2010 21:13

Here’s the modal dialog I used in my WPF projects, where a windowless popup obstructs the application and the app is dimmed and disabled in the background until the user responds...

The view root element uses a grid sized to the entire parent window and only slightly opaque (much like I’ve seen this done in web pages) and the actual dialog is a smaller grid inside that.

 

<DataTemplate x:Key="Dialog" DataType="DialogVM">
        <Grid Height="{Binding Height, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
              Width="{Binding Width, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}">
            <Grid.Background>
                <SolidColorBrush Color="LightGray" Opacity=".6"></SolidColorBrush>
            </Grid.Background>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="50"></ColumnDefinition>
                <ColumnDefinition Width="*"></ColumnDefinition>
                <ColumnDefinition Width="50"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="100"></RowDefinition>
                <RowDefinition Height="*"></RowDefinition>
            </Grid.RowDefinitions>
            <Border HorizontalAlignment="Center" BorderBrush="Black" BorderThickness="1"
                  Background="LightSkyBlue" Height="120" Width="320"
                   Grid.Column="1" Grid.Row="1" CornerRadius="10" >
                <Border.Effect>
                    <DropShadowEffect ShadowDepth="10"/>
                </Border.Effect>
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="20"></RowDefinition>
                        <RowDefinition Height="100"></RowDefinition>
                    </Grid.RowDefinitions>
                    <Border BorderThickness="0,0,0,1" Grid.Row="0"><!-- BorderBrush="Black"--><!-- not using border looks too much like a window the user wants to drag it -->
                        <TextBlock Text="{Binding Title}" Margin="10,3,0,0"></TextBlock>
                    </Border>
                    <Grid Grid.Row="1" >
                        <Grid.RowDefinitions>
                            <RowDefinition Height="60"></RowDefinition>
                            <RowDefinition Height="40"></RowDefinition>
                        </Grid.RowDefinitions>
                        <Grid Background="AliceBlue"  Grid.Row="0" >
                            <TextBlock Text="{Binding Message}" FontSize="14" TextWrapping="Wrap" 
                               Margin="10" TextAlignment="Center" ></TextBlock>
                        </Grid>
                        <Border Background="AliceBlue" Grid.Row="1">
                            <Border.CornerRadius>
                                <CornerRadius BottomLeft="10" BottomRight="10"></CornerRadius>
                            </Border.CornerRadius>
                            <Button Content="OK" Command="{Binding CmdOK}" Width="50" Height="20" ></Button>
                        </Border>
                    </Grid>
                </Grid>
            </Border>
        </Grid>
    </DataTemplate>

 

I use one for messages, and an extended one for Error Messages.  I put these anywhere at the bottom of one of my WPF windows, like this

 

            <ContentControl Content="{Binding Dialog}" ContentTemplate="{StaticResource Dialog}"
                            Grid.RowSpan="2" Visibility="{Binding Dialog.Visibility}">
            </ContentControl>
            <ContentControl Content="{Binding Error}" ContentTemplate="{StaticResource Error}"
                            Grid.RowSpan="2" Visibility="{Binding Error.Visibility}">
            </ContentControl>
        </Grid>
    </DataTemplate>
</ResourceDictionary>

 

And add the class as a Property to the window’s ViewModel:

 

public class SomeViewModel
    {
        #region Fields 
        ...
        DialogVMBase _dialog;
        ErrorVM _error;
        #endregion
        #region Properties 
        ...
        public DialogVMBase Dialog
        {
            get
            {
                return _dialog;
            }
            set
            {
                this._dialog = value;
                this._dialog.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(_dialog_PropertyChanged);
                this.OnPropertyChanged("Dialog"); // this one sends notice to Binding to open the dialog
            }
        }
        public ErrorVM Error
        {
            get
            {
                return _error;
            }
            set
            {
                this._error = value;
                this._error.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(_error_PropertyChanged);
                this.OnPropertyChanged("Error");
            }
        }
        // simple button OK clicks change Visibility property and we catch dialogVM.PropertyChanged here
        void _dialog_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            this.OnPropertyChanged("Dialog");  // this one sends notice to ListVM Binding (to close dialog)
        }
        void _error_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            this.OnPropertyChanged("Error");
        }
        #endregion

You see there we attach a delegate in the parent window to PropertyChanged on the Dialog, so when a property changes in the Dialog, the parent is notified.  In the Dialog next you will see clicking OK just sets the Visibility to Hidden and calls PropertyChanged, which will then call the parent window’s event handler, whose Model Binder notices the Visibility property is changed, and then registers the Hide in the UI.

    public class DialogVMBase : ViewModelBase
    {
        #region fields
        string _title;
        string _message; 
        System.Windows.Visibility _visibility;
        RelayCommand _cmdok;
        #endregion
        // derived classes must set Visible explicitly in their own constructor
        public DialogVMBase() {
            this._visibility = System.Windows.Visibility.Hidden;
        } 
        // this contructor gets Visible by default
        public DialogVMBase(string title, string message) 
        {             
            this.Message = message;
            this.Title = title;
        }
        #region Properties
        public virtual string Title
        {
            get { return _title; }
            set
            {
                SetProperty(ref _title, value, "Title");
            }
        }
        public virtual string Message
        {
            get { return _message; }
            set
            {
                SetProperty(ref _message, value, "Message");
            }
        }
        public virtual System.Windows.Visibility Visibility
        {
            get { return _visibility; }
            set
            {
                if (_visibility == value) return;
                _visibility = value;
                this.OnPropertyChanged("Visibility");
            }
        }
        #endregion
        public event EventHandler ButtonOkClicked;
        #region Commands
        public virtual RelayCommand CmdOK
        {
            get
            {
                if (_cmdok == null)
                {
                    _cmdok = new RelayCommand(
                        param => this.OK(),
                        param => this.OKEnabled()
                        );
                }
                return _cmdok;
            }
        }
        public virtual bool OKEnabled()
        {
            return true;
        }
 
        public virtual void OK()
        {
            EventHandler handler = ButtonOkClicked;
            if (handler != null) handler(this, null);
            this.Visibility = System.Windows.Visibility.Hidden;
        }
        #endregion
    }

In my ErrorVM I add some more exception properties

    public class ErrorVM : DialogVMBase
    {
        #region fields
        Exception _exception; 
        string _stack;
        Exception _inner;
        Dictionary<object,object> _data;
 
        #endregion
        public ErrorVM() { }
        public ErrorVM(Exception e)
        {
            if (e != null)
            {
                _exception = e;
                this.Title = "Error";
                this.Message = e.Message;
                _stack = e.StackTrace;
                _inner = e.InnerException;
                _data = new Dictionary<object, object>();
                // on rethrow the first exception sometimes becomes an inner
                while (e.Data.Count == 0 && e.InnerException != null)
                {
                    e = e.InnerException;
                }
                foreach (System.Collections.DictionaryEntry d in e.Data)
                    _data.Add(d.Key, d.Value);
                this.Visibility = System.Windows.Visibility.Visible;
            }
        }
        #region Properties 
        public string StackTrace
        {
            get { return _stack; }
            set
            {
                if (_stack == value) return;
                _stack = value;
                this.OnPropertyChanged("StackTrace");
            }
        }
        public Exception InnerException
        {
            get { return _inner; }
            set
            {
                if (_inner == value) return;
                _inner = value;
                this.OnPropertyChanged("InnerException");
            }
        }
        public Dictionary<object,object> Data
        {
            get { return _data; }
            set
            {
                if (_data == value) return;
                _data = value;
                this.OnPropertyChanged("Data");
            }
        }
        #endregion
         
    }

and then the parent can call it at any time, like this:

                if (MatchGroups.Count == 0) Dialog = new DialogVMBase("No Results",
                    String.Format("No Results found for your Product: {0}", SelectedProductName));

and that’s it.

Tags:

WPF

Using Impersonation to query MSMQ and putting results in a Dundas Gauge.

by MikeHogg 29. April 2009 19:58

I had a lot of functions like this, all running on BackgroundWorker threads, on a status page that refreshed itself every three minutes.  Most of them queried different database, most of them also were hooked up to more complicated Dundas Gauges and Charts.  Here’s one that’s really simple, but illustrates accessing a particular MSMQ, which needed impersonation of a service account.

    private void loadMSMQGauge()
    {
        int numInQueue = -1;
        try
        {
            Utils.ImpersonateUser iu = new Utils.ImpersonateUser();
            iu.Impersonate("corp", "someserviceaccount", "password");
            //PerformanceCounter pc = new PerformanceCounter("MSMQ Queue",
            //    "Messages in Queue", "someservername\\private$\\ssome_error_queue", "someservername");
            // HP openview kept horking the msmq perf counter, after re-registering the counters several times 
            //   and getting horked again after reboots we will take the long route.
            using (MessageQueue mq = new MessageQueue("FormatName:DIRECT=OS:someservername\\private$\\some_error_queue", QueueAccessMode.Peek))
            {
                Message[] messages = mq.GetAllMessages();
                numInQueue = messages.Length;
            }
            this.GaugeContainer1.LinearGauges[4].Pointers[0].Value = numInQueue; //  pc.RawValue;
            this.GaugeContainer1.NumericIndicators[4].Value = this.GaugeContainer1.LinearGauges[4].Pointers[0].Value;
            iu.Undo();
        }
        catch (System.InvalidOperationException)  // this was from PerfCounter object
        {
            //empty queue, do nothing
        }
        catch (MessageQueueException ex) //this comes from mq object, just write it out also
        {
            Response.Write(ex.ToString());
        }
        catch (Exception ex)
        {

 

Simple hookup, but it showed on a Widescreen TV hanging on the wall of our department, the current state of one of our systems, at a glance, with big digital numbers and red and yellow colors.

Tags:

C#

Add a background tag on all of your web pages showing the current Environment

by MikeHogg 23. March 2009 20:41

This was a neat trick.  When working with UAT and STAGE and DEV and however many other environments, it can sometimes be confusing which database your particular web server is actually hooked up to.  Here I set up an HttpHandler to write out a string as an image memory stream, and then with some CSS trickery it shows up repeating with low opacity all over each page, faint enough that it doesn’t bother you, but enough so that you won’t ever mistake yourself for being in a different environment.

First in the BasePage PreRender I check for conditional, in case, for instance, you don’t want to use this on Production:

        protected override void OnPreRender(EventArgs e)
        {
            //todo: we could make this a webresource instead of static img
            Image img = new Image(); 
            try
            {
                string prod = System.Configuration.ConfigurationSettings.AppSettings["dontShowHeaderForThisDatabase"];
                if (!LIB.Gen_Util.getDBName().ToUpper().Contains(prod.ToUpper()))
                {
                    img.ImageUrl = "DBImage.ashx";
                    img.Style.Add("width", "100%");
                    img.Style.Add("height", "100%");
                    img.Style.Add("z-index", "-1");
                    img.Style.Add("position", "absolute");
                    img.Style.Add("top", "20px"); 
                    // this is a pain- if we have <% %> tags in page then this will break
                    //this.Form.Controls.Add(img);
                    this.Page.Controls.Add(img);
                }
                base.OnPreRender(e);
            }

 

 

DBImage.ashx is created once then cached in the HttpHandler:

    public class HttpHandler :IHttpHandler
    {
        #region IHttpHandler Members
        public bool IsReusable
        {
            get { return false; }
        }
        public void ProcessRequest(HttpContext context)
        {
            try
            {
                byte[] ba;
                if (HttpContext.Current.Cache["dbimage"] == null)
                {
                    ba = Gen_Util.CreateHeaderImage(Gen_Util.getDBName());
                    if (ba != null)
                    {
                        HttpContext.Current.Cache["dbimage"] = ba;
                    }
                }
                else
                {
                    ba = (byte[])HttpContext.Current.Cache["dbimage"];
                }
                if (ba != null)
                {
                    context.Response.BinaryWrite(ba);
                }
                context.Response.End();
            } 
        }
        #endregion
    }

 

It will get called for each Request, with this line in the web.config:

    <httpHandlers>
      ...
      <add verb="GET" path="DBImage.ashx" type="CEG.CPS.Settlements.LIB.HttpHandler" />

 

And the CreateHeaderImage is the tricky CSS part:

        public static byte[] CreateHeaderImage(string text)
        { 
            try
            {
                Bitmap bm = new Bitmap(320, 240, PixelFormat.Format32bppArgb);
                Graphics g = Graphics.FromImage(bm);
                g.SmoothingMode = SmoothingMode.HighQuality;        // ?
                g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;   // ?
                g.Clear(Color.White);
                GraphicsPath p = new GraphicsPath();
                Font f = new Font("Impact", 20F);
                Rectangle r = new Rectangle(0, 0, 320, 240);
                StringFormat sf = new StringFormat();
                String repeatedText = string.Empty;
                for (int x = 0; x < 48; x++)  // 8 rows of 6
                { 
                    if (x % 6 == 0 && x != 0)
                    {
                        repeatedText += "\n";
                    } repeatedText += text + "  "; 
                }  
                p.AddString(repeatedText, f.FontFamily, (int)f.Style, f.Size, r, sf);
                
                // transparency shade 75
                SolidBrush b = new SolidBrush(Color.FromArgb(75,Color.Gray));
                
                g.FillPath(b, p);
                
                f.Dispose();
                b.Dispose();
                g.Dispose();
                MemoryStream ms = new MemoryStream();
                bm.Save(ms, ImageFormat.Bmp);
                bm.Dispose();
                return ms.GetBuffer();
            }
    }

 

And that’s it.

Tags:

C# | ASP.Net

Using an abstract Server Control as an updateable module

by MikeHogg 19. March 2009 14:15

The main part of this feature was to show a grid of some data, different database uptimes and other performance metrics, on a web page.  The interesting part was that the databases that were tracked changed often.  Sometimes there were 12, and then one would get killed, and two more were created, and the next week another new one would be built. Not only would it be better to put the datasources in a config of some sort, but it would be even better if a manager could edit that config through an easy-to-use UI on a web page. 

Rather than build two separate pages, I saw that there were some re-useable components in this use case, and so I created a Server control, with an XML config.  Most of the XML I/O I put in an Abstract class, and then from that I inherited and Admin Control and a Display Control.

First the Common Elements:

    interface IGrid
    { 
        DataTable getDataSource();
        void createColumns(DataTable aTable);
        void styleGrid();
    }
    public abstract class AGrid : GridView, IGrid
    {
        public string xFile{get; set;}
        public string headerBackColor{get; set;}
        public string gridBackColor {get;set;} 
        public string title { get; set; }        
        protected DataTable myTable; 
        protected override void OnInit(EventArgs e)
        {
            try
            {
                base.OnInit(e);
                this.AutoGenerateColumns = false;
                myTable = getDataSource();
                if (Page.IsPostBack)
                {
                    this.DataSource = myTable;
                    if (this.EditIndex > -1 )
                    {
                        this.DataBind();
                    }
                }
            } 
        }
        protected override void OnLoad(EventArgs e)
        { 
                base.OnLoad(e);
                if (!Page.IsPostBack)
                {
                    if (myTable != null)
                    {
                        createColumns(myTable);
                        this.DataSource = myTable;
                        this.DataBind();
                        styleGrid();
                    }
                }  
        } 
        public DataTable getDataSource()
        { 
            <snip>                      
        }
        public virtual void createColumns(DataTable myTable){
            try
            { <snip>
        }
        public void styleGrid()
        {
            try
            {
                if (this.gridBackColor != String.Empty)
                {
                    this.BackColor = System.Drawing.Color.FromName(this.gridBackColor);
                }
                else this.BackColor = System.Drawing.Color.Silver;
                this.BorderStyle = BorderStyle.Ridge;
                this.Font.Size = 10;
                this.Font.Name = "Verdana";
                this.Font.Bold = true;
                
                this.GridLines = GridLines.Horizontal;
                this.Style.Add("font-variant", "small-caps");
                this.Style.Add("text-align", "right");
                this.CellPadding = 2;
                if (this.headerBackColor != String.Empty)
                {
                    this.HeaderStyle.BackColor = System.Drawing.Color.FromName(this.headerBackColor);
                }
                else this.HeaderStyle.BackColor = System.Drawing.Color.MidnightBlue;
                this.HeaderStyle.ForeColor = System.Drawing.Color.White;
                this.HeaderStyle.Font.Size = 12;
                if (this.title != String.Empty)
                {
                    this.Caption = this.title;
                }
                else this.Caption = "Grid Monitor 1.0";
                
                this.CaptionAlign = TableCaptionAlign.Top;
                this.BorderWidth = 1;
            }
            catch (NullReferenceException e)
            {
                Console.WriteLine("Probably xml issue: " + e.ToString());
            }
        }

 

The Admin Grid was just a simple DataGrid and used the built-in OnDataBound to add editting controls and commands, and the OnRowEditing events to Add/Edit/Remove nodes in the XML file.

 

    [DefaultProperty("Text")]
    [ToolboxData("<{0}:AdminGrid runat=server></{0}:AdminGrid>")]
    public class AdminGrid : AGrid
    {
        [Bindable(true)]
        [Category("Appearance")]
        [DefaultValue("")]
        [Localizable(true)]
          
        protected override void OnInit(EventArgs e)
        {
            this.AutoGenerateEditButton = true;
            this.ShowFooter = true;
            
            base.OnInit(e); 
        } 
        protected override void OnPagePreLoad(object sender, EventArgs e)
        {
            base.OnPagePreLoad(sender, e);
            if (Page.IsPostBack && this.EditIndex == -1)
            {
                this.DataBind();
            }
        } 
        protected override void OnDataBound(EventArgs e)
        {
            base.OnDataBound(e);
            foreach (DataControlFieldCell cell in this.FooterRow.Cells)
            {
                if (cell.ContainingField.GetType().Equals(typeof(System.Web.UI.WebControls.BoundField)))
                {
                    TextBox myC = new TextBox();
                    cell.Controls.Add(myC);
                }
                else if (cell.ContainingField.GetType().Equals(typeof(System.Web.UI.WebControls.CheckBoxField)))
                {
                    CheckBox myC = new CheckBox();
                    cell.Controls.Add(myC);
                }
                else if (cell.ContainingField.GetType().Equals(typeof(System.Web.UI.WebControls.CommandField)))
                {
                    LinkButton myC = new LinkButton();
                    myC.Text = "Add New";
                    myC.ID = "btnAddNew";
                    myC.CommandName = "New";
                    myC.CommandArgument = "New";
                    cell.Controls.Add(myC);
                }
            }
         
        protected override void OnRowCommand(GridViewCommandEventArgs e)
        {
            try
            {
                base.OnRowCommand(e);
                if (e.CommandName == "New")
                {
                    DataRow newRow = myTable.NewRow();
                    //insert
                    for (int x = 0; x < myTable.Columns.Count; x++)
                    {
                        Control myControl = this.FooterRow.Cells[x + 1].Controls[0];
                        if (myControl.GetType().Equals(typeof(CheckBox)))
                        {
                            newRow[x] = ((CheckBox)myControl).Checked;
                        }
                        else if (myControl.GetType().Equals(typeof(TextBox)))
                        {
                            newRow[x] = ((TextBox)myControl).Text;
                        }
                    }
                    myTable.Rows.Add(newRow);
                    WriteXml(myTable, HttpContext.Current.Server.MapPath(xFile));
                    this.DataSource = myTable;
                    this.DataBind();
                }
            }
        protected override void OnRowUpdating(GridViewUpdateEventArgs e)
        { 
            //DataTable myTable = (DataTable)HttpContext.Current.Session["myTable"];
            DataTable oldTable = (DataTable)this.DataSource;
            GridViewRow myRow = this.Rows[e.RowIndex];
            for (int x = 0; x < myRow.Cells.Count; x++)
            {
                Control myControl = myRow.Cells[x].Controls[0];
                
                if (myControl.GetType().Equals(typeof(CheckBox)))
                {
                    oldTable.Rows[e.RowIndex][x - 1] = ((CheckBox)myControl).Checked;
                    //myTable.Rows[e.RowIndex][]
                }
                else if (myControl.GetType().Equals(typeof(TextBox)))
                {
                    oldTable.Rows[e.RowIndex][x - 1] = ((TextBox)myControl).Text;
                }
                    WriteXml(myTable, HttpContext.Current.Server.MapPath(xFile));
                    this.DataSource = myTable;
                    this.DataBind();
                }
            }
        }

 

The DisplayGrid has some neat UI components

[assembly: WebResource("DBMonitor.Resources.button_red.png","image/png")]
[assembly: WebResource("DBMonitor.Resources.button_green.png", "image/png")]
[assembly: WebResource("DBMonitor.Resources.button_yellow.png", "image/png")]
namespace DBMonitor
{
    [DefaultProperty("Text")]
    [ToolboxData("<{0}:ServerControl1 runat=server></{0}:ServerControl1>")]
    public class DisplayGrid : AGrid
    {
        [Bindable(true)]
        [Category("Appearance")]
        [DefaultValue("")]
        [Localizable(true)]
        public string Text <snip>
        protected override void OnInit(EventArgs e)
        { 
                this.title = "Database Environments";
                this.headerBackColor = "MidnightBlue";
                this.gridBackColor = "Silver";
                this.xFile = "Config/dbmon.xml";
                base.OnInit(e); 
        }
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            DataTable myTable = this.DataSource as DataTable;
            if (myTable.Columns.IndexOf("Active") >= 0)
            {
                myTable.DefaultView.RowFilter = "Active = true";
                this.DataBind();
            }
        }
        protected override void OnRowDataBound(GridViewRowEventArgs e)
        {
            base.OnRowDataBound(e);
            try
            {
                //find BE Last Refreshed value
                int x = ((DataTable)this.DataSource).Columns.IndexOf("BE_Last_Run");
                if (x >= 0 && e.Row.RowType != DataControlRowType.Header)
                {
                    System.Web.UI.WebControls.Image myLight = new System.Web.UI.WebControls.Image();
                      
                    DateTime lastBE = new DateTime();
                    if (e.Row.Cells[x].Text.Contains(':'))
                    {
                        try
                        {
                            lastBE = DateTime.Parse(e.Row.Cells[x].Text);
                        }
                        catch (FormatException ex)
                        {
                            lastBE = DateTime.MinValue;
                            
                        }
                        e.Row.Cells[x].Text = lastBE.ToShortTimeString();
                        TimeSpan myAge = DateTime.Now - lastBE;
                        if (ConfigurationSettings.AppSettings["debug"] == "true")
                        {
                            logThis("myAge: " + myAge.ToString() + " and now is " + DateTime.Now.ToString() +
                                " and lastBE is " + lastBE.ToString() +
                                " and timespan.fromhours(1) is " +
                            TimeSpan.FromHours(1).ToString() +
                            " and now minus lastBE is " + myAge, EventLogEntryType.Information);
                        }
                        if (myAge > TimeSpan.FromHours(1))
                        {
                            myLight.ImageUrl = this.Page.ClientScript.GetWebResourceUrl(this.GetType(),
                                    "DBMonitor.Resources.button_red.png");
                        }
                        else if (myAge > TimeSpan.FromMinutes(10))
                        {
                            myLight.ImageUrl = this.Page.ClientScript.GetWebResourceUrl(this.GetType(),
                                "DBMonitor.Resources.button_yellow.png");
                        }
                        else
                        {
                            myLight.ImageUrl = this.Page.ClientScript.GetWebResourceUrl(this.GetType(),
                                "DBMonitor.Resources.button_green.png");
                        }
                    }
                    else
                    {
                        myLight.ImageUrl = this.Page.ClientScript.GetWebResourceUrl(this.GetType(),
                                    "DBMonitor.Resources.button_red.png");
                    }
                    e.Row.Cells[0].Controls.Add(myLight);
                    e.Row.Cells[0].BackColor = Color.White;
                }
            }

Adding it to a page is just a couple lines then- here the first line registers the namespace, and the last selected line places it in a div.  In this case I populated my attributes in the DisplayGrid class, but if I was to use this in several other places, I could remove those and populate my attributes here in the html element.

<%@ Register Assembly="DBMonitor" Namespace="DBMonitor" TagPrefix="dbm"  %> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Status Page</title>
    <link href="StyleSheet.css" rel="stylesheet" type="text/css" />
    <link href="mobile.css" rel="stylesheet" type="text/css" media="handheld"/>
    
    <script src="stock-ticker.js" type="text/javascript"></script>
     <meta http-equiv=Refresh content="120" />
     
    <script type="text/javascript">
        
        var ticker;
        function loadTicker(){
            ticker = new Ticker('ticker','myTicker',4,15);
            ticker.start();
            
        }
 
    </script>
</head>
<body onload="loadTicker();">
    <form id="form1" runat="server"> 
    
   <table style="table-layout:fixed"><tr><td style="width:200px;overflow:hidden">
       <div id="myTicker" class="ticker" style="width:5000px" nowrap="nowrap">
            <asp:Xml DocumentSource="~/Config/ticker.xml" runat="server" ID="ticker" TransformSource="~/ticker.xslt"></asp:Xml>
   </div></td></tr></table>
   
    <div id="newEnv">
        <dbm:DisplayGrid ID="something" runat="server"></dbm:DisplayGrid>
    </div>

 

And that’s it.

Using .Net to let users create Scheduled Tasks to send themselves issue tracking reports from MySql

by MikeHogg 11. March 2009 19:39

I had written a reporting page in asp.Net that queried the MySql database of our issue tracking system.  Users in different teams could use the page to create reports on the issues created, resolved, and worked on by their team as well as filtering by any number of other attributes. 

The page in itself was neat, and necessary, as the issue tracking system had grown at a pace of 50 new issues weekly for a couple of years.  But it was neater to build a way for different users to save their more complicated report filters, and even neater to allow them to send them an automatic email containing their report on any daily or weekly schedule.  Although it sounded like a lot of work at first, it turned out to be easy to set up.

The first part was to only show each user their own saved reports, and allow them to edit and save again, without affecting or viewing anybody else’s.  Because we were in an intranet environment, this was easy enough, using Windows as our authentication scheme meant I always already had the current user, and I could then save the reports in a table with their Id, and only show links to reports that they owned. 

The report page had over twenty filters and group by’s and sorts so saving all of the settings individually was not the way to go.  Instead I would take the output of that page, the actual query, generated dynamically, and save that.  Then all I had to do was get the query, saved in the database, and run it.  The code that created the query can be a great exercise for refactoring, and it is incredibly long, but you can imagine it was quick to write initially, and I only ever had to add one or two features to it …

        using (MySqlDataAdapter myAdp = new MySqlDataAdapter())
        {
            myCmdQuery.Append("select distinct iss_id  Issue_ID ");
            if (project) { myCmdQuery.Append(",prj_title Project "); };
            if (category) { myCmdQuery.Append(",prc_title Category "); };
            if (summary) { myCmdQuery.Append(",iss_summary  Summary "); };
            if (assignee)
            {
                myCmdQuery.Append(", (select group_concat(usr_full_name order by usr_full_name separator ', ') " +
                    " from eventum_user join eventum_issue_user " +
                    " on isu_usr_id = usr_id where isu_iss_id = iss_id) Assignees ");
            };
            if (status) { myCmdQuery.Append(",sta_title Status "); };
            if (priority) { myCmdQuery.Append(",pri_title Priority "); };
            if (createDate) { myCmdQuery.Append(",iss_created_date Created_Date "); };
            if (createAge) { myCmdQuery.Append(",datediff(curdate(), iss_created_date) Days_Old "); };
            if (updateDate) { myCmdQuery.Append(",iss_updated_date Updated_Date "); };
            if (updateAge) { myCmdQuery.Append(", datediff(curdate(),iss_updated_date) Days_Since_Update "); };
            if (updateUser)
            {
                myCmdQuery.Append(",(select usr_full_name from eventum_user join eventum_issue_history " +
                      "on usr_id = his_usr_id where his_id = (select max(his_id) from " +
                      "eventum_issue_history where his_iss_id = iss_id)) Update_User ");};
            if (updateSummary) { myCmdQuery.Append(",(select his_summary from eventum_issue_history where " +
                "his_id = (select max(his_id) from eventum_issue_history where his_iss_id = iss_id)) Update_Summary "); }; 
            if (abbrev)
            {
                if (notes) { myCmdQuery.Append(", left(n.not_note,200) Last_Internal_Note "); };
                if (closeComments) { myCmdQuery.Append(", left(nc.not_note,200) Closed_Comment "); };
            }
            else
            {
                if (notes) { myCmdQuery.Append(", n.not_note Last_Internal_Note "); };
                if (closeComments) { myCmdQuery.Append(", nc.not_note Closed_Comment "); };
            }
            if (custom) { myCmdQuery.Append(", cfo_value Custom "); };
            if (expResDate) { myCmdQuery.Append(", iss_expected_resolution_date Exp_Res_Date "); };
            if (dupe) {myCmdQuery.Append(", iss_duplicated_iss_id dupe "); };
            myCmdQuery.Append("FROM eventum_issue e ");
            myCmdQuery.Append("join eventum_status s on e.iss_sta_id = s.sta_id ");
            myCmdQuery.Append("join eventum_project p on e.iss_prj_id = p.prj_id ");
            myCmdQuery.Append("join eventum_project_category pc on e.iss_prc_id = pc.prc_id ");
            if (priority) { myCmdQuery.Append("join eventum_project_priority pp on e.iss_pri_id = pp.pri_id "); };
            if (notes)
            {
                myCmdQuery.Append("left outer join eventum_note n on iss_id = not_iss_id and not_id = " +
                    "  (select max(not_id) from eventum_note nn " +
                    "   where n.not_iss_id = nn.not_iss_id and nn.not_title <> 'Issue closed comments') ");
            }
            if (closeComments)
            {
                myCmdQuery.Append("left outer join eventum_note nc on iss_id = nc.not_iss_id and nc.not_id = " +
                    "  (select max(ncc.not_id) from eventum_note ncc " +
                    "   where nc.not_iss_id = ncc.not_iss_id and ncc.not_title = 'Issue closed comments') ");
            }
            if (custom)
            {
                myCmdQuery.Append("left outer join eventum_issue_custom_field on icf_iss_id = iss_id " +
                    "left outer join eventum_custom_field_option on cfo_id = icf_value ");
            } 
            if (this.ddlUserAssignment.SelectedIndex > 0)
            {
                myCmdQuery.Append("join eventum_issue_user on iss_id = isu_iss_id ");
            }
            myCmdQuery.Append("WHERE prj_status = 'active' ");
            if (this.ddlProject.SelectedIndex > 0)
            {
                myCmdQuery.Append("and prj_id = " + this.ddlProject.SelectedValue.ToString() + " ");
            }
            if (this.ddlUserAssignment.SelectedIndex > 0)
            {
                myCmdQuery.Append(" and isu_usr_id = " + this.ddlUserAssignment.SelectedValue.ToString() + " ");
            }
            if (this.rblDate.SelectedIndex == 3)  //NOT touched
            {
                myCmdQuery.Append("and not exists(select 3 from eventum_issue_history where his_iss_id = iss_id " );
                
                if (this.ddlGroupAction.SelectedIndex > 0)
                {
                    myCmdQuery.Append(" and his_usr_id in (select usr_id from eventum_user where usr_grp_id = " +
                        ddlGroupAction.SelectedValue + ") ");
                }
                if (this.ddlUserAction.SelectedIndex > 0)
                {
                    myCmdQuery.Append(" and his_usr_id = " + ddlUserAction.SelectedValue + " ");
                }
                if (numberOfDays != null)
                {
                    myCmdQuery.Append(" and datediff(curdate(), his_created_date) <= " + numberOfDays + " ");
                }
                else
                {
                    if (start != null)
                    {
                        myCmdQuery.Append(" and his_created_date >= '" + start + "' ");
                    }
                    if (end != null)
                    {
                        myCmdQuery.Append(" and his_created_date <= '" + end + "' ");
                    }
                }
                myCmdQuery.Append(") ");
            }
            if (this.rblDate.SelectedIndex == 2)  //touched
            {
                myCmdQuery.Append("and exists(select 2 from eventum_issue_history where his_iss_id = iss_id ");
                
                if (this.ddlGroupAction.SelectedIndex > 0)
                {
                    myCmdQuery.Append(" and his_usr_id in (select usr_id from eventum_user where usr_grp_id = " +
                        ddlGroupAction.SelectedValue + ") ");
                }
                if (this.ddlUserAction.SelectedIndex > 0)
                {
                    myCmdQuery.Append(" and his_usr_id = " + ddlUserAction.SelectedValue + " ");
                }
                 
                if (numberOfDays != null)
                {
                    myCmdQuery.Append(" and datediff(curdate(), his_created_date) <= " + numberOfDays + " ");
                }
                else
                {
                    if (start != null) { myCmdQuery.Append(" and his_created_date >= '" + start + "' "); };
                    if (end != null) { myCmdQuery.Append(" and his_created_date <= '" + end + "' "); }
                }
                myCmdQuery.Append(") ");
            }
            else if (this.rblDate.SelectedIndex == 1)  //closed
            {
                myCmdQuery.Append("and exists(select 1 from eventum_issue_history where his_iss_id = iss_id " + 
                    "and his_htt_id = 23 ");
                
                if (this.ddlGroupAction.SelectedIndex > 0)
                {
                    myCmdQuery.Append(" and his_usr_id in (select usr_id from eventum_user where usr_grp_id = " +
                        ddlGroupAction.SelectedValue + ") ");
                }
                if (this.ddlUserAction.SelectedIndex > 0)
                {
                    myCmdQuery.Append(" and his_usr_id = " + ddlUserAction.SelectedValue + " ");
                }
                if (numberOfDays != null)
                {
                    myCmdQuery.Append(" and datediff(curdate(), iss_closed_date) <= " + numberOfDays + " ");
                }
                else
                {
                    if (start != null) { myCmdQuery.Append(" and iss_closed_date >= '" + start + "' "); };
                    if (end != null) { myCmdQuery.Append(" and iss_closed_date <= '" + end + "' "); };
                }
                myCmdQuery.Append(") ");
            }
            else if (this.rblDate.SelectedIndex == 0)    //created
            {
                if (this.ddlGroupAction.SelectedIndex > 0)
                {
                    myCmdQuery.Append(" and iss_usr_id in (select usr_id from eventum_user where usr_grp_id = " + 
                        ddlGroupAction.SelectedValue + ") ");
                }
                if (this.ddlUserAction.SelectedIndex > 0)
                {
                    myCmdQuery.Append(" and iss_usr_id = " + ddlUserAction.SelectedValue + " ");
                }
                
                if (numberOfDays != null)
                {
                    myCmdQuery.Append(" and datediff(curdate(), iss_created_date) <= " + numberOfDays + " ");
                }
                else
                {
                    if (start != null) { myCmdQuery.Append(" and iss_created_date >= '" + start + "' "); };
                    if (end != null) { myCmdQuery.Append(" and iss_created_date <= '" + end + "' "); };
                }
                
            }
            if (closed && !open) { myCmdQuery.Append(" and iss_closed_date is not null "); };
            if (open && !closed) { myCmdQuery.Append(" and iss_closed_date is null "); };
            if (!open && !closed) { Response.Write("umm.  Not Closed and Not Open?  Kindly try again."); return; };
            if (this.ddlGroupAssignment.SelectedIndex > 0)
            {
                myCmdQuery.Append(" and iss_id in (select isu_iss_id from eventum_issue_user " +
                    "join eventum_user on isu_usr_id = usr_id " +
                    "where usr_grp_id = " + this.ddlGroupAssignment.SelectedValue + ") ");
            }
            if (this.txtKeyword.Text.Length > 0)
            { 
                myCmdQuery.Append(" and iss_id in ( select * from ((SELECT DISTINCT(iss_id)  FROM evdb.eventum_issue " +
                    "WHERE  MATCH(iss_summary, iss_description) AGAINST ('" + this.txtKeyword.Text + "' IN BOOLEAN MODE) " +
                    ") UNION (  SELECT DISTINCT(not_iss_id)  FROM  evdb.eventum_note " +
                    "WHERE  MATCH(not_note) AGAINST ('" + this.txtKeyword.Text + "' IN BOOLEAN MODE) " +
                    ") UNION ( SELECT DISTINCT(sup_iss_id) FROM evdb.eventum_support_email, " +
                    "evdb.eventum_support_email_body WHERE sup_id = seb_sup_id AND " +
                    "MATCH(seb_body) AGAINST ('" + this.txtKeyword.Text + "' IN BOOLEAN MODE) ) ) a )   ");
            }
            if (this.ddlCategory.SelectedIndex > 0) { myCmdQuery.Append(" and iss_prc_id = " + this.ddlCategory.SelectedValue.ToString()); };
            if (this.ddlPriority.SelectedIndex > 0) { myCmdQuery.Append(" and iss_pri_id = " + this.ddlPriority.SelectedValue.ToString()); };
            myAdp.SelectCommand = new MySqlCommand(myCmdQuery.ToString());
            myAdp.SelectCommand.Connection = new MySqlConnection(ConfigurationManager.ConnectionStrings["evdbConn"].ToString());
            Session["_query"] = myCmdQuery.ToString();
            DataTable myTable = new DataTable();
            try
            {
                myAdp.Fill(myTable);
                DataView myDV = new DataView(myTable);
                // insert sorts in reverse order here  FIRST USER SORTS, THEN GROUPINGS
                if (this.ddlSort3.SelectedIndex > 0)
                {
                    addSort(ddlSort3.SelectedItem.Text.Replace(" ", "_"), ref myDV);
                }
                if (this.ddlSort2.SelectedIndex > 0)
                {
                    addSort(ddlSort2.SelectedItem.Text.Replace(" ", "_"), ref myDV);
                }
                if (this.ddlSort1.SelectedIndex > 0)
                {
                    addSort(ddlSort1.SelectedItem.Text.Replace(" ", "_"), ref myDV);
                }
                
                if (this.chkGroupAssignees.Checked)
                {
                    addSort("Assignees", ref myDV);
                }
                if (this.chkGroupCategory.Checked)
                {
                    addSort("Category", ref myDV);
                }
                if (this.chkGroupProject.Checked)
                {
                    addSort("Project", ref myDV);
                }

The result grid also had grouping on any of three separate attributes and was also written in the long declarative style, which I am not proud of, but it resulted in an organized and detailed report grid.

 

The second part required the use of the webserver’s Scheduled tasks to run daily and check the database for any reports that were scheduled to go at that time.  The task ran a simple console script.  I saved the task using impersonation and  .Net interfaced with Scheduled Tasks easily.

        ScheduledTasks st = new ScheduledTasks();
        Trigger newTrigger;
        Task newTask;
        string username = User.Identity.Name.Replace("SOMECORP\\","").ToLower();
        string reportName = "QBR" + username + this.txtScheduledReportName.Text;
        if (this.txtScheduledReportName.Text.Length > 0 && username.Length > 0)
        {
            try
            {
                int adjustedHour = (this.ddlAM.SelectedIndex == 0) ? Convert.ToInt16(this.ddlHour.SelectedValue) :
                    (Convert.ToInt16(this.ddlHour.SelectedValue)) + 12;
                st.DeleteTask(reportName); // in case we are saving new version
                DeleteReport(reportName);
                newTask = st.CreateTask(reportName);
                if (newTask != null)
                {
                    newTask.ApplicationName = "c:\\bin\\QbertEmailer.exe";
                    newTask.Parameters = "\"" + reportName + "\"";
                    newTask.SetAccountInformation("someserviceaccount", "password");
                    if (this.ddlFrequency.SelectedIndex == 0)
                    {
                        newTrigger = new DailyTrigger(
                            (short)adjustedHour,
                            (short)Convert.ToInt16(this.ddlMinute.SelectedValue.Replace(":", "")));
                    }
                    else
                    {
                        newTrigger = new WeeklyTrigger(
                            (short)adjustedHour,
                            (short)Convert.ToInt16(this.ddlMinute.SelectedValue.Replace(":", "")),
                            (DaysOfTheWeek)Convert.ToInt16(this.ddlWeekday.SelectedValue));
                    }
                    newTask.Triggers.Add(newTrigger);
                    // Call LogonUser to get a token for the user
                    Utils.ImpersonateUser iu = new Utils.ImpersonateUser();
                    iu.Impersonate("corp", "someserviceaccount", "password");
                    newTask.Save();
                    newTask.Close();
                    iu.Undo();
                    this.bindGrid();
                    this.SaveReport(reportName, true);
                    resetScheduleForm();
                }
                else
                {
                    Response.Write("Problem creating report name.  Please try again");
                }
            }
            catch (Exception ex)
            {
                logThis(ex.ToString(), EventLogEntryType.Error);
                Response.Write("Could not save report.  Click back and try again or contact your favorite QBERT support representative");
                //continue
            }
            finally
            {
                st.Dispose();
            }
        }
        else
        {
            Response.Write("Bad User or Report Name, cannot save.");
        }
    }

 

AD lookups provided email addresses.  I didn’t put the email addresses in our database, because that would just add another place users had to manage their email addresses, and employees already managed their emails through the company’s AD system. 

                using (DirectorySearcher ds = new DirectorySearcher("(samaccountname=" + user + ")"))
                {
                    
                    ds.PropertiesToLoad.Add("mail");
                    SearchResult sr = ds.FindOne();
                    if (sr == null)
                    {
                        return;
                    }
                    if (sr.Path.Length == 0)
                    {
                        return;
                    }
                    user = sr.Properties["mail"][0].ToString();
                }

The email was simple html, just formatting the results of the saved report query, run at that moment.

htmlReport.Append("<html><table cellspacing='0' border='1'style='border-collapse:collapse;font-size:9pt'>" + 
                        "<tr style='background-color:Silver;font-size:X-Small;'>");
                        foreach (DataColumn dc in dt.Columns)
                        {
                            htmlReport.Append("<td>" + dc.ColumnName + "</td>");
                        }
                        htmlReport.Append("</tr><tr>");
                        int x = 1;
                        foreach (DataRow dr in dt.Rows)
                        {
                            if (x == 1)
                            {
                                htmlReport.Append("<tr>");
                                x = x * -1;
                            }
                            else
                            {
                                htmlReport.Append("<tr style='background-color:AliceBlue;'>");
                                x = x * -1;
                            }
                            foreach (object fieldVal in dr.ItemArray)
                            {
                                if (dr.ItemArray[0].ToString().Equals(fieldVal.ToString()))
                                {
                                    htmlReport.Append("<td><a href='http://boitqueue/view.php?id=" +
                                        fieldVal.ToString() + "'>" + fieldVal.ToString() + "</td>");
                                }
                                else
                                {
                                    htmlReport.Append("<td>" + fieldVal.ToString() + "</td>");
                                }
                            }
                            htmlReport.Append("</tr>");
                        }
                        htmlReport.Append("</table></html>");

Exception Handling and Canceling Background Threads

by MikeHogg 11. March 2009 13:08

I read once the idea that Exceptions should only be caught if you can handle them, and this makes sense to me.  Besides a Top level, uncaught exception handler for client facing applications, I usually avoid the try catch blocks unless I am going to do something specific at that point in the code.  I wrote  a database monitor that was used to continually run in a Windows Service and involved several factors that called for robust program recovery- database timeouts, file I/O, multi threaded operations. So there were specific failures that I knew about and could write around.  Here are some of the routines:

}
                else
                {
                    myTimer.Dispose();
                    e.Result = filename;
                }
                try
                { 
                    DataTable myTable = new DataTable();
                    myTable.ExtendedProperties["filename"] = filename;
                    myAdp.Fill(myTable);
                    e.Result = myTable;
                    if (bw.CancellationPending)
                    {
                        e.Result = filename;
                        e.Cancel = true;
                        myTimer.Dispose();
                    }
                }
                catch (OracleException ex)
                {
                    logThis(ex.ToString(), EventLogEntryType.Warning);
                    e.Result = filename;  //move on
                }
                catch (Exception ex)
                {
                    logThis(ex.ToString(), EventLogEntryType.Error);
                    throw;
                }
                finally
                {
                    if (myAdp.SelectCommand.Connection.State == ConnectionState.Open)
                    {
                        myAdp.Dispose();
                    }
                    myTimer.Dispose();
                }

In this case we are pinging a database batch process every few minutes, and occasionally we might get a timeout, but we don’t want to bring the whole Service down, because it also monitors other data sources, and we can try again in a few minutes and find the same database is now responsive.  The end result of a timeout might be a blip on a graph or chart, or a yellow light on a status gauge, that turns green if the database comes back and turns red the longer it has been since the database was responsive.

Here we are storing our ping result info in local xml files.  In retrospect, there may have been easier options, but this allows us to add monitored sources dynamically, and manage the XML files dynamically, rather than have to create them in a database ahead of time.  I wrote in a level of tolerance for concurrent file access Reads and Writes just using sleep and loops and it works perfectly for this case.  Once again, we don’t need a perfect level of detail since we are running every few minutes.   An occasional lost entry does not get noticed.

        private void saveXML(XElement myRoot, string filename)
        {
            int retrySave = 10;
            while (retrySave > 0)
            {
                try
                {
                    myRoot.Save(filename);
                }
                catch (IOException exIO)
                {
                    Thread.Sleep(1000);
                    retrySave--;
                    continue;
                }
                break;
            } 
        }
        private XElement loadXML(string filename)
        {
            XElement myRoot = null;
            using (FileStream myStream =
                new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                int retryLoad = 10;
                while (retryLoad > 0)
                {
                    try
                    {
                        using (XmlReader xr = XmlReader.Create(myStream))
                        {
                            myRoot = XElement.Load(xr);
                        }
                    }
                    catch (XmlException x)
                    {
                        // we expect some rootElement is missing ... from other threads
                        retryLoad--;
                        Thread.Sleep(1000);
                        continue;
                    }
                    break;
                }
            }
            return myRoot;
        }

One of the other features of this monitor, was that we would run against an unknown number of data sources, and they weren’t related.  Each source was separate, and so threading out the pings was simple, but some datasources acted differently than others.  Oracle TNS Listener would simply not return if there was no database endpoint, and so we had to run another thread using the Timer to cancel our thread in case this happened. I think at the time I put this together, a parallel concurrency library had just come out, but wasn’t officially part of .Net, or it wasn’t out and so up to this point most of the threading projects I did and I researched were all done with BackgroundWorkers.

private void dbMonStart(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker bw = sender as BackgroundWorker;
            String db = e.Argument.ToString();
            // set a watch timer to cancel thread on sql timeout (used more for dev when taken offline not prod
            System.Threading.Timer myTimer = new System.Threading.Timer(cancelMyThread, bw, 30000, System.Threading.Timeout.Infinite);
            using (OracleDataAdapter myAdp = new OracleDataAdapter())
            {
                myAdp.SelectCommand = cmd;
                myAdp.SelectCommand.Connection = new OracleConnection(conn);
                try
                {
                    DataTable myTable = new DataTable();
                    myTable.ExtendedProperties["dbname"] = db;
                    myAdp.Fill(myTable);
                    e.Result = myTable;
                    if (bw.CancellationPending)
                    {
                        e.Result = db;
                        e.Cancel = true;
                        myTimer.Dispose();
                    }
                }

 

CancelMyThread is simple enough:

private void cancelMyThread(object myThread)
        {
            BackgroundWorker bw = myThread as BackgroundWorker;
            if (bw.IsBusy) { bw.CancelAsync(); }

 

and that’s it.

Using a heartbeat to find time of death on constantly changing databases

by MikeHogg 1. April 2008 20:31

Another unusual request I had one day that I enjoyed writing.  The team I worked on employed up to a dozen copies of the same database, some of them in different stages of testing like Prod and UAT and Stage and Stage2, and some of them purposed for data analysis or short investigative projects. 

Only some of these were active, in that they were running our continuous batch processing procedures, which continually imported data from upstream systems and processed it.  At times we wanted to know, in the cases of the inactive systems, when they were taken offline, and made inactive.  When they stopped receiving upstream data. 

So I set up a console application that had two functions.  The first was to update a particular database with a timestamp, running every five minutes or so.  I slipped the command for this into the same scheduler that ran the batch processing, so it would only run on Active databases.  Also, the timestamp was to be encrypted. It was the case sometimes that we absolutely needed to know that our timestamp wasn’t modified by outside sources.  It might be a little overboard but it was an interesting project.

The second part was a method to find this out from any database.  So this same console app, dropped in a NAS script location, allowed anybody on the network to find on their command line the actual last timestamp, decrypted, of a given database.

Here are the two routines:

        static private int updateDB()
        {
            byte[] key = {};
            byte[] IV = {  0x49, 0xCD, 0x24, 0x37, 0x95, 0xDB, 0xFE, 0xBD };
            String systimestamp;
            String timecode;
             
            OracleCommand myCmd = new OracleCommand("",Conn);
            try
            {
                
                key = System.Text.Encoding.UTF8.GetBytes("pacmanlx");
                 
                
                DESCryptoServiceProvider myDES = new DESCryptoServiceProvider();
                //Rijndael myR = Rijndael.Create();
                
                byte[] inputString = Encoding.UTF8.GetBytes(DateTime.Now.ToString( "MMM-dd" ));
                System.IO.MemoryStream ms = new System.IO.MemoryStream();
                CryptoStream cs = new CryptoStream(ms, myDES.CreateEncryptor(key, IV), CryptoStreamMode.Write);
                cs.Write(inputString, 0, inputString.Length);
                cs.FlushFinalBlock();
                //encrypt this baby
                timecode = Convert.ToBase64String(ms.ToArray());
                
                //not this one           
                systimestamp = DateTime.Now.ToString();
                myCmd.CommandText = "update some.valid_general_cd v set v.decode = '" +
                systimestamp + "', column_cat_cd = '" + timecode + "' where v.code_id = 4444";
                Conn.Open();
                int myReturn = myCmd.ExecuteNonQuery();
                myCmd.Dispose(); 
                Conn.Close();
                return myReturn;
                
            }
            catch (Exception ex) {
                if (Conn.State == System.Data.ConnectionState.Open)
                {
                    Conn.Close();
                }
                
                throw new Exception(ex.ToString());
            }
        }
        static private void decryptTime(string db)
        {
            byte[] key = { };
            byte[] IV = {  0x49, 0xCD, 0x24, 0x37, 0x95, 0xDB, 0xFE, 0xBD };
            byte[] inputByteArray = { };
            OracleConnection Conn = new OracleConnection("Data Source=" + db + ".somecorp.net;User Id=somedbuser;Password=password;");
            OracleCommand myCmd = new OracleCommand("",Conn);
            String outputString;
            try
            {
                key = System.Text.Encoding.UTF8.GetBytes("pacmanlx");
                DESCryptoServiceProvider myDES = new DESCryptoServiceProvider();
                //Rijndael myR = Rijndael.Create();
                myCmd.CommandText = "select v.column_cat_cd from some.valid_general_cd v where v.code_id = 4444";
                Conn.Open();
                
                OracleDataReader myReader = myCmd.ExecuteReader();
                myReader.Read();
                //object = myReader.getsomething
                outputString = myReader.GetString(0);
                
                //decrypt object
                inputByteArray = Convert.FromBase64String(outputString);
                
                //now decrypt the regular string
                System.IO.MemoryStream ms = new System.IO.MemoryStream();
                CryptoStream cs = new CryptoStream(ms, myDES.CreateDecryptor(key, IV),
                                               CryptoStreamMode.Write);
                cs.Write(inputByteArray, 0, inputByteArray.Length);
                cs.FlushFinalBlock();
                
                System.Text.Encoding encoding  = System.Text.Encoding.UTF8;
                //write date to console
                Console.WriteLine(encoding.GetString(ms.ToArray()));
                myReader.Close();
                
            }
            catch (Exception ex)
            {
                if (Conn.State == System.Data.ConnectionState.Open)
                {
                    Conn.Close();
                }
                Console.WriteLine(ex.ToString());
            }

AJAX and Javascript libraries

by MikeHogg 5. April 2007 09:44

AJAX is becoming a popular buzzword.  Prototyping is something I am just getting into, but for the most part I copy paste/adapt my personal js library functions in each new project I start  (as well as using free open sourced libraries from the internet).  Here is an example of some of my javascript in action.

    // BA.js
 //  modules for BA.aspx
 // mhogg jan07
   //---------------------
 // Functions-----------
 //  showSearch()                    three show() functions 
 //  showEmployee()                  ^
 //  showDependent(depID, EFID)      ^
 //  removeNavPath()                     show() helper
 //  addResultsLink()                    show() helper        
 //  setTitle(titleVal)                  show() helper
 //  showDiv(divID)                      show() helper
 //  LoadXMLDoc(url)                 requests employee data from server (1)
 //  blinkProgress()                     helper- blinks cute yellow box  (2)
 //  BuildXMLResults()                catches response                   (3)
 //  setData(rX)                      does something with data I forget  (4) calls next two functions
 //  setPeriodLinks()                creates date links on left side of page  (4a)
 //  fillDetails(instanceNum)        the meat of the page- uses two helper functions that follow this one (4b)
 //  clearData()                         helper- clears any values left from previous Instance 
 //  findOlderVal(pRoot, arrayNum)       helper- sets most recent value for a field in case of null value; always black
     //---------------------
 // Global vars
 var navPath; //initialized in showSearch()
 var tid;  // used for Progress BlinkBox
 var empXml; // this holds the Dataset Tables XML for one Employee
 var empName,empID; //set in fillDetails(), used in show() functions
 // the next two vars need to match each other for processing each XML value to a form field (span)
 var fieldArray = new Array('EIGname','EIGempid','EIGaddress','EIGcity'…
 var valArray = new Array('NAME','EID','ADDRESS','CITYANDZIP','HOME_PHONE'…
   //--------------------
 // Bodies
 function showSearch() {
     navPath = document.getElementById('divNavPath');  //first time initialization/ used in other functions
     showDiv('divFilterAndResultsList');
     setTitle('Search Results');
       empXml = null;
     setPeriodLinks();  //indirectly clears period links
     removeNavPath(); 
     navPath.appendChild(document.createTextNode('> Search Results ')); 
 }
   function showDependent(depID, EFID) {
     showDiv('divDependentInstance');
       //set values for each span
     //js xpath??
       if ( document.evaluate ) {  // W3C implemenation; else IE implementation     
         var xPathExp = "/NewDataSet/Table1[EFID=" + EFID + " and DEPID=" + depID + "]/DNAME";
         var xName = document.evaluate(xPathExp, empXml, null, XPathResult.ANY_TYPE, null );
         depName = xName.singleNodeValue.textContent;
       }else depName = empXml.selectNodes("/NewDataSet/Table1[EFID=" + EFID + " and DEPID=" + depID + "]/DNAME")[0].firstChild.nodeValue ;
       removeNavPath();
     addResultsLink();       
     node = document.createElement('a');
     node.href = 'javascript:showEmployee();';    
     node.appendChild(document.createTextNode(empName));
     navPath.appendChild(document.createTextNode(' > '));
     navPath.appendChild(node);
     navPath.appendChild(document.createTextNode(' > ' + depName));
       setTitle(depName);
     //setPeriodInstances for dependent
   }

 

So there was creating DOM manipulation, note the js XPath implementation that was actually easier in IE back then. That code breaks today since IE has become more compliant.  Here’s my ajax:

//-----------------------
 function LoadXMLDoc(url){ 
   if (window.XMLHttpRequest){ //Mozilla, Firefox, Opera 8.01, Safari, and now IE7?
     reqXML = new XMLHttpRequest(); 
     reqXML.onreadystatechange = BuildXMLResults; 
     reqXML.open("POST", url, true); 
     reqXML.send(null); 
   }
   else if(window.ActiveXObject){ // old IE
     reqXML = new ActiveXObject("Microsoft.XMLHTTP"); 
     if (reqXML) { 
       reqXML.onreadystatechange = BuildXMLResults; 
       reqXML.open("POST", url, true); 
       reqXML.send(); 
     } 
   }
   else{ //Older Browsers
     alert("Your Browser does not support XMLHttp!");
   }
   blinkProgress();
 } 
   function blinkProgress() {
     document.getElementById('inProgress').style.left = (document.body.clientWidth - 300) / 2;
     if (document.getElementById('inProgress').style.display=="none") {
         document.getElementById('inProgress').style.display="";
     } else document.getElementById('inProgress').style.display="none";
     tid = setTimeout('blinkProgress()', 500);
 }
  function BuildXMLResults(){  if(reqXML.readyState == 4){ //completed state    clearTimeout(tid);    document.getElementById('inProgress').style.display="none";    if(reqXML.status == 200){ //We got a success page back      if(reqXML.responseXML.documentElement && reqXML.responseXML.documentElement.getElementsByTagName('TABLE') != null){   //dummy test        //window.status = reqXML.responseXML; //display the message in the status bar                setData(reqXML.responseXML.documentElement);      }      else{        //Something's not right        alert("There was a problem retrieving the XML data:\nMissing ID; " + reqXML.responseText);      }    }     else{      //display server code not be accessed      alert("There was a problem retrieving the XML data:\nMissing 200; " + reqXML.responseText);    }              }}

We wrote our own progress bars back then, and animations, like here:

 

  // the search Results page slides up or down
 function toggleDisplay()  {
     //alert('display');  //debug
     // match gets an array, the second value ([1]) is what we are looking for- the value of display
     var cV = document.cookie.match ( 'display=(.*?)(;|$)' );
     if ( (cV) && cV[1] == "down" )  {
         document.cookie = "display=up";
         move("up");
         document.getElementById('ddlCsearch').style.display = ''; 
         document.getElementById('lbIsearch').style.display = ''; 
         document.getElementById('tblSearch').style.width = '800px';
     } else {
         document.cookie = "display=down";
         move("down");
         document.getElementById('ddlCsearch').style.display = 'none';  //ie6
         document.getElementById('lbIsearch').style.display = 'none';   //ie6 
         document.getElementById('tblSearch').style.width = '300px';
     }
 }
 function move(direction){
     //alert('#2:  move' +  document.getElementById('pnlResults').style.top);  //debug
     var currTop = document.getElementById('pnlResults').style.top.substr(0, document.getElementById('pnlResults').style.top.length - 2);
       if (direction == 'down') {
         if (currTop <= 30) {
             return;
         }else {
             document.getElementById('pnlResults').style.top = currTop - 30;
             setTimeout('move("down")',20);
         }
     }else {
         if (currTop >= 340) {
             return;
         }else {
             document.getElementById('pnlResults').style.top = eval(currTop) + 30;
             setTimeout('move("up")',20);
         }
     }
 }

Also our own validation:

 

  // ----- custom client side validation 
 function validate() {
   var badFields = '';
   var aTR = document.getElementsByTagName('TR');
   var aRowChecker = 0;
   var focusFlag = false;   
     for (var row=0;row<aTR.length;row++) {   //for each table row in form
     var cell = 0;
     while (aTR[row].childNodes[cell]){     // for each table cell in form
       var cellchild = 0;
         while (aTR[row].childNodes[cell].childNodes[cellchild]) {      // for each DOM obj in cell 
         var object = aTR[row].childNodes[cell].childNodes[cellchild];
         if (object.tagName == 'SELECT'){
           if (aTR[row].childNodes[0].innerHTML) {          // IE
             var myText = aTR[row].childNodes[0]
           } else { var myText = aTR[row].childNodes[1]}    //Mozilla 
              // Drop down validation
           if (object.id.indexOf('result') > 0 && 
             object.selectedIndex == 0) {
                 badFields += myText.innerHTML + '\n';
                 myText.style.color = 'red';
                 if (focusFlag == false){object.focus();focusFlag=true;}
           } else  {myText.style.color = 'black';}
         } 
         if (object.tagName == 'INPUT') {
           if (aTR[row].childNodes[0].innerHTML) {            // IE
             var myText = aTR[row].childNodes[0]
             } else { var myText = aTR[row].childNodes[1]}    //Mozilla 
              // textbox validation
           if (object.getAttribute('type') == 'text' && 
             object.value.length == 0 &&
             object.id.indexOf('result') > 0 &&
             object.maxLength < 200) {  //comments not a required field
               badFields += myText.innerHTML + '\n';
               myText.style.color = 'red';
               if (focusFlag == false){object.focus();focusFlag=true;}
           }  // Regex validation    
           else if ( object.getAttribute('type') == 'text'  &&  
               ( object.id.indexOf('result') > 0 || (object.id.indexOf('Q') == 0 && object.maxLength > 200) ) &&   
               !object.value.match(/^[-a-zA-Z,. @!()\''""?0-9]*$/)) { 
                   alert('Please use normal characters in ' + myText.innerHTML + object.value);                            
                   object.focus();
                   myText.style.color = 'red';
                   return false;
           }  // Radio validation   */
            else if (object.getAttribute('type') == 'radio') {
             var aRadio = document.getElementsByName(object.name);
             if(aRowChecker != row){                                             // aRowChecker used so we don't prompt user five times for each missed question
               if (myText.firstChild.nodeType == 1) {myText=myText.firstChild;}  // in case -style.color=red- applied, just get text
               if (!aRadio[0].checked && !aRadio[1].checked && !aRadio[2].checked && !aRadio[3].checked && !aRadio[4].checked){
                 badFields += myText.innerHTML + '\n';
                 myText.style.color = 'red';
                 aRowChecker=row;  
                 if (focusFlag == false){object.focus();focusFlag=true;}
               }   
               else {myText.style.color = 'black';}
             }
           } else  {myText.style.color = 'black';}
         }
         cellchild++;
       }  
       cell++;
     }
   } 
   if (badFields.length > 0) {
     alert('Please fill in the following fields:\n' + badFields);  
     return false;
   }
   else return true;
 }

My approach for browser compliance was to work in the major or main four browsers by using the simplest code possible.  I avoided hacks if I could. 

Tags:

Javascript

What’s wrong with this picture

by MikeHogg 20. November 2006 19:01

 

Here’s one of my first production web apps, a small Survey app that I designed and wrote in VB.Net.  This is funny to me, because I forgot that I ever used VB outside of college.  This was against Oracle… .Net 1.1 or 2.0

	Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
		'Put user code to initialize the page here
		Dim myQuery As New ArrayList
		Dim dctValidation As New Regex("^[0-9][0-9]-[JFMASOND][aepuco][nbrylgptvc]-[0-9][0-9]$")
		Dim txtValidation As New Regex("^[-a-zA-Z0-9 !@?\'"".,]+$")
		If Not IsPostBack Then
			GetInstructors()
			GetCourses()
			txtDCT.Text = DateTime.Now.ToString("dd-MMM-yy")
			GetQuestions("S", scaleRepeater)			' Scale Questions
			GetQuestions("C", commentRepeater)			' Comment Questions
		Else		'posting an Eval
			'Validation
			If Request.Form("txtFN").Length > 50 Or Request.Form("txtLN").Length > 50 _
			 Or Request.Form("ddlInstructor") <= 0 Or Request.Form("ddlCourse") <= 0 _
			 Or Request.Form("ddlInstructor") > 9999999 Or Request.Form("ddlCourse") > 9999999 _
			 Or Not dctValidation.IsMatch(Request.Form("txtDCT")) Or Not txtValidation.IsMatch(Request.Form("txtFN")) _
			 Or Not txtValidation.IsMatch(Request.Form("txtLN")) Then
				lblMisc.Text = "We have a problem here, missing some required information.  <br>" & _
				 "Enable javascript for details and use your browser's back button to return to the Evaluation."
			Else			 ' valid Eval, post transaction to db
				myQuery.Add("insert into surveys (SURVEY_ID, STUDENT_LAST_NAME, STUDENT_FIRST_NAME, COURSE_DATE," & _
				  " INSTRUCTOR_ID, COURSE_ID, UPDATE_USERID, UPDATE_DATE) values(SURVEY_ID_SEQ.NEXTVAL, '" & _
				  Request.Form("txtLN").Replace("'", "''") & "', '" & _
				  Request.Form("txtFN").Replace("'", "''") & "','" & Request.Form("txtDCT") & "','" & _
				  Request.Form("ddlInstructor") & "','" & Request.Form("ddlCourse") & "', " & _
				  "'WebUser', SYSDATE)")
				'POST method relies on Request.forms key names to put query together- 
				'  all number keys have types as values, 
				'  the Qx keys have the answers as values.
				Dim field As String
				Dim myQ As Regex
				For Each field In Request.Form.AllKeys
					If myQ.IsMatch(field, "[1-9][0-9]?") Then					' (one or two digit number)
						If Request.Form(field) = "S" Then
							myQuery.Add("insert into SURVEY_SCALED_ANSWER (SCALE_ANSWER_ID, SURVEY_ID," & _
							   "QUESTION_ID, SCALE_VALUE, UPDATE_USERID, UPDATE_DATE) values (" & _
							   "SCALED_ANSWER_ID_SEQ.NEXTVAL, SURVEY_ID_SEQ.CURRVAL, " & field & _
							   ", '" & Request.Form("Q" & field) & "', 'WebUser', SYSDATE)")
						ElseIf Request.Form(field) = "C" And Not Request.Form("Q" + field) = "" Then
							myQuery.Add("insert into SURVEY_COMMENTS (COMMENT_ID, SURVEY_ID," & _
							 "QUESTION_ID, COMMENT_DESC, UPDATE_USERID, UPDATE_DATE) values (" & _
							 "COMMENTS_ID_SEQ.NEXTVAL, SURVEY_ID_SEQ.CURRVAL, " & field & _
							 ", '" & Request.Form("Q" & field).Replace("'", "''") & "', 'WebUser', SYSDATE)")
						End If
					End If
				Next
				If UpdateTables(myQuery) Then				' transaction processed
					Response.Write("Your evaluation has been recorded.  <p>Your feedback is important, so that OIT can continue to provide <br>Baltimore County Employees with the best possible training. </p> <p>Thank you." & _
				 "</p><p><input type='button' id='btnEnd' onclick='javascript:window.location.href=""/TE""'" & _
				 " value='Fill in another Evaluation' /></p>")
					pnlForm.Visible = False
				End If
			End If
		End If
	End Sub

 

I was really proud of writing this dynamic questions Query array processing routine, instead of hardcoding 20 or 30 inserts, or however many questions were on the survey, but one glaring redball stares at me when I review this code- SQL injection.  Thankfully, this was an intranet web app. 

I was also surprised to read my documentation, now years later, and see how superior it is to my current documentation Smile

TrainEval Application Notes 
** For production implementation:
** 1. zip the entire TE directory
** 2. remove the two (2) web.config files from the zip archive
** 3. remove the TE/Admin/FormsAuth/Users.xml file from the zip archive
** 4. unzip to intranetprod/wwwroot
** 5. enjoy!
audience: developers, troubleshooters
purpose: describe the app in summary, pointing out the main functions and their locations
_______________
TE/default.aspx
---------------
...provides the actual evaluation that users fill out.
custom javascript validation (regex) and server-side validation (regex).
 The client side custom js validation is rather cumbersome, and although it was written with
 multiple browsers in mind and the possibility of different questions or numbers of questions, it 
 should be pretty robust if Evals change at all.
 
a couple other javascript features are included at the bottom of the Eval.aspx html.
uses js.calendar (needs 4 files in directory, see <script> includes in html page)- 
Gets questions from Oracle DB dynamically.  In order to change questions, add or remove, 
 all that needs to be done is to edit the database.  The app will display only questions 
 and text that are active (Active Flag), and display them according to their type- S or C.
 
___________________
TE/Admin/Admin.aspx
-------------------
....provides Edit and Reporting functionality for somebody and one assistant.
uses js.calendar as above.
uses graphics/ directory for button images.
stores Crystal Reports in crystal directory but doesn't use these copies.
uses FormsAuth directory (need to remove read permissions from all other users when implementing)
four sections: 
  Instructors  	(updateable repeater)
  Courses	(updateable repeater)
  Edit Evals	(search form and meat of the program- see below)
  Reports	(search form and two links to Crystal reports)
  
The Instructors and Courses are simple repeaters, allowing changes to Active status and 
 updates to names.  New items can be added at the bottom of each repeater (footer).  I added a filter
 system of alphabet buttons to the Courses page to make it quicker (big improvement) and more user friendly.
 
Reports uses Crystal Enterprise.  This is straightforward.  Couldn't implement the 'Most Recent # Evals' search 
 parameter with Crystal, though.
The Edit Evals section uses httprequest object (AJAX) to display individual results (each eval) without posting back
 each time you page through them.  All Evals are returned and held in Dataset on server when queried, then one at a time is 
 sent to the client as you page through.  This got kind of tricky with stuff like keeping the page count 
 and returning to the same page after an update.  Page count is held in lblMisc... display status (toggle up/down) is 
 held in cookie... and all features in regular Eval are available- validation, Overall Autocalculate,
 and each Eval can be resubmitted (Submit Changes).  One main difference between this page and Evals page is that this 
 page also includes all Instructors and Courses, not just Active ones.  
  
_____________________
Diffs of Dev and Prod
---------------------
 
 To promote to production only the webconfig needs to be changed.

 

I had some neat features in this project.  Not only was it an application for internal members who had taken a course to fill out a survey on an instructor, but it also included the Administrator back end to select subsets of data based on instructor names or courses and then page through the results one by one or view a Crystal Report in the browser.  Here I was constructing controls dynamically, some with fancy mouseovers, another a filter of all the letters of the alphabet, that the Admin could click on to view only surveys on instructors with names ending with that letter.  The Admin Area was written in c#:

        // Setting controls
        private void setButtonsAndPanels(ImageButton btnSelected)
        {
            ImageButton[] ibCollection = { btnInstructors, btnCourses, btnEditEvals, btnReports };
            foreach (ImageButton myButton in ibCollection) {
                if (myButton != btnSelected) {
                    myButton.Attributes.Add("onmouseover","this.src='graphics/btnInv" + myButton.ID.Remove(0,3) + ".gif';");
                    myButton.Attributes.Add("onmouseout","this.src='graphics/btn" + myButton.ID.Remove(0,3) + ".gif';");
                    myButton.ImageUrl="graphics/btn" + myButton.ID.Remove(0,3) + ".gif";
                }
                else {
                    myButton.Attributes.Remove("onmouseover");
                    myButton.Attributes.Remove("onmouseout");
                    myButton.ImageUrl="graphics/btnSel" + myButton.ID.Remove(0,3) + ".gif";
                }
            }
            
            Panel[] pCollection = { pnlCourses, pnlInstructors, pnlEditEvals, pnlReports, pnlSearch, pnlResults};
            foreach (Panel myPanel in pCollection) {
                if (myPanel.ID.Remove(0,3) != btnSelected.ID.Remove(0,3)){
                    myPanel.Visible = false;
                }
                else myPanel.Visible = true;
            }
        }
        private void loadAlphaButtons() {
            LinkButton myButton;
            int counter;
            char letter;
            try {
                pnlAlpha.Controls.AddAt(0,new LiteralControl("Filter: "));
                for (counter = 0;counter <=25;counter++) {
                    letter = (char) (counter+65);
                    myButton = new LinkButton();
                    pnlAlpha.Controls.AddAt( (counter * 2) + 1, myButton);
                    myButton.ID = letter.ToString();
                    myButton.Text = letter.ToString();
                    myButton.ForeColor = Color.Blue;
                    
                    //if (counter < 25) {
                    pnlAlpha.Controls.AddAt(pnlAlpha.Controls.IndexOf(myButton) + 1, new LiteralControl("&nbsp;|&nbsp;"));
                    //}
                    myButton.Click += new System.EventHandler(this.btnAlpha_Click);
                }
                LinkButton btnClear = new LinkButton();
                btnClear.Text = "Clear";
                btnClear.Click += new System.EventHandler(this.btnClear_Click);
                pnlAlpha.Controls.AddAt(53, btnClear);
            }
            catch (Exception ex) {
                throwEx(ex.ToString(), ex.Source.ToString());
            }
        }

This was also the beginnings of my personal library functions.  I believe Logging to be a standard feature, and a developer needs of course the boilerplate Data Access library, as well as some standard exception handling techniques.  All of which grew over time, but these were my first techniques…

 {
            DataSet myDS = new DataSet();
            try {
                OracleDataAdapter oraAdp = new OracleDataAdapter(myQuery, oraConn);
                oraAdp.Fill(myDS);
                myRepeater.DataSource=myDS.Tables[0];
                myRepeater.DataBind();
            } 
            catch (Exception ex){
                if (oraConn.State == ConnectionState.Open){ 
                    oraConn.Close();
                }
                throwEx(ex.ToString(), ex.Source.ToString());
            }  
            finally 
            {
                if (oraConn.State == ConnectionState.Open)
                { 
                    oraConn.Close();
                }
            }          
        }
        // Helpers
        private bool updateTables( ArrayList pQuery )
        {
            try {
                OracleCommand oraCmd = new OracleCommand();
                try {
                    oraConn.Open();
                } catch (Exception ex){
                    writeLog(ex.ToString(), ex.Source.ToString());
                    oraConn.Close();
                    oraConn.Open();
                }
                    
                OracleTransaction tran = oraConn.BeginTransaction();
                oraCmd.Connection = oraConn;
                oraCmd.Transaction = tran;   // necessary for MS OracleClient
                try {
                    foreach (string myQ in pQuery){
                        oraCmd.CommandText = myQ;
                        if (myQ != null){
                            oraCmd.ExecuteNonQuery();
                        }
                    }               
                tran.Commit();
                return true;
                }
                catch (Exception ex) {
                    tran.Rollback();
                    throwEx(ex.ToString(), ex.Source.ToString());
                    return false;
                }
            }
            catch (Exception ex){
                throwEx(ex.ToString(), ex.Source.ToString());
                return false;
            }
            finally  {
                if (oraConn.State == ConnectionState.Open) { oraConn.Close();}
            } 
        }
    
        private void throwEx( string errorMess, string sender ) 
        {
            hidePanels(this);
            lblMisc.Text = "<br>We're sorry, the operation you have attempted was not successful.  Use your browser's" +
            " Back Button and try again and if you are still not successful, then call the Help Desk at 8200.";
            lblMisc.Visible = true;
            writeLog(errorMess, sender);
        }
        private void writeLog( string exMsg, string sender ) 
        {
            DateTime time = DateTime.Now;
            FileStream fs = new FileStream(Server.MapPath("logs/errlog.txt"), FileMode.OpenOrCreate, FileAccess.Write);
            StreamWriter s = new StreamWriter(fs);
            s.BaseStream.Seek(0, SeekOrigin.End);
            s.WriteLine(time.ToString() + ":" + sender + ":" + exMsg);
            s.Close();
        }

I also didn’t learn about JSON until later, but still got by using XML to pass data and messages back and forth from server to a web page to change DOM elements on the fly.  Here a server method…

        //      xmlhttprequest interaction 
        private void getResultRow(int pageNum){
            try {
                DataTable dt = new DataTable();
                dt = (DataTable)Cache["myX" + Session.SessionID];
                DataRow dr = dt.Rows[pageNum];
                
                StringBuilder xResult = new StringBuilder("<Survey>");
                foreach ( DataColumn c in dt.Columns ) {
                    string colName = c.ColumnName;
                    string colVal = dr[colName].ToString().Replace("&","&amp;");   // xml issue with &s
                    xResult.Append("<" + colName + ">" + colVal + "</" + colName + ">");
                }
                xResult.Append("</Survey>");
                
                Response.ContentType = "text/xml";
                Response.Write(xResult.ToString());
                Response.End();
            }
            catch (Exception ex){
                if ( ex.GetBaseException().GetType().Name == "ThreadAbortException" ) {
                    return;
                }
                throwEx(ex.ToString(), ex.Source.ToString());
            }
        }

The javascript was fun to write.  I did a lot of DOM manipulation:

var myPage, pageTotal;
function getPage(pageNum) {
    //alert('getPage');  //debug
    // NOTE: pageNum/myPage comes from pagebuttons already inc/decremented.  
    // this function is also called by server in btnUpdate_click where pageNum is lost so...
    myPage = pageNum;  
    pageTotal = document.getElementById('lblPageTotal').innerHTML;
    
    if (pageNum >= 0) {
        if (pageNum < pageTotal) {
            document.getElementById('lblPageCount').innerHTML = (pageNum + 1);
            LoadXMLDoc("Admin.aspx?Page=" + pageNum);   
        } else {alert('Page ' + pageNum + ': No next page');myPage--;} //set increment back
    } else {alert('Page 1:Cannot go back'); myPage++;}  // set decrement back
}
var reqXML;
function LoadXMLDoc(url){ 
  //alert(url);       //debug
  if (window.XMLHttpRequest){ //Mozilla, Firefox, Opera 8.01, Safari, and now IE7?
    reqXML = new XMLHttpRequest(); 
    reqXML.onreadystatechange = BuildXMLResults; 
    reqXML.open("POST", url, true); 
    reqXML.send(null); 
  }
  else if(window.ActiveXObject){ //IE
    reqXML = new ActiveXObject("Microsoft.XMLHTTP"); 
    if (reqXML) { 
      reqXML.onreadystatechange = BuildXMLResults; 
      reqXML.open("POST", url, true); 
      reqXML.send(); 
    } 
  }
  else{ //Older Browsers
    alert("Your Browser does not support Ajax!");
  }
  blinkProgress();
} 
var tid
function blinkProgress() {
    document.getElementById('inProgress').style.left = (document.body.clientWidth - 200) / 2;
    //alert('blink');  //debug
    if (document.getElementById('inProgress').style.display=="none") {
        document.getElementById('inProgress').style.display="";
    } else document.getElementById('inProgress').style.display="none";
    tid = setTimeout('blinkProgress()', 500);
}
function BuildXMLResults(){
  if(reqXML.readyState == 4){ //completed state
    clearTimeout(tid);
    document.getElementById('inProgress').style.display="none";
    if(reqXML.status == 200){ //We got a sucess page back
      if(reqXML.responseText.indexOf("ID") >= 0){   //dummy test
        //window.status = reqXML.responseXML; //display the message in the status bar
        //alert('Success: \n' + reqXML.responseText);   //debug
        setData(reqXML.responseXML.documentElement);
      }
      else{
        //Something's not right
        //alert('XML:\n' + reqXML.responseXML + 'Text:\n' + reqXML.responseText);   //debug 
        alert("There was a problem retrieving the XML data:\n" + reqXML.statusText);
      }
    } 
    else{
      //display server code not be accessed
      //alert('readyState: ' + reqXML.readyState + '\nstatus: ' + reqXML.status + 'responseText: ' + reqXML.responseText);   //debug
      alert("There was a problem retrieving the XML data:\n" + reqXML.statusText);
    }		
  }
}
//----- fills in Eval with answers from XML response, uses the three functions following this one
function setData(rX){
    clearData();
    
    if(rX == null) {
        alert('An error has occured, setData did not receive any data');
        return;
    }
    for(var c=0;c<rX.childNodes.length;c++){
        var myElement = rX.childNodes[c].nodeName;
        if (rX.childNodes[c].textContent){
            var myText = rX.childNodes[c].textContent;  // for diff DOMS
        }else var myText = rX.childNodes[c].text;       // for diff DOMS
        switch(myElement) {
            case "SURVEY_ID" : document.getElementById('txtIDresults').value = myText;break;
            case "FIRST_NAME" : document.getElementById('txtFNresults').value=myText;break;
            case "LAST_NAME" : document.getElementById('txtLNresults').value=myText;break;
            case "DATE_COURSE_TAKEN" : document.getElementById('txtDCTresults').value=myText;break;
            case "INSTRUCTOR" : setDDL("ddlIresults",myText);break; 
            case "COURSE" : setDDL("ddlCresults",myText);break;
            default: if (myElement.indexOf("Q")==0) {
                setQ(myElement, myText);
                }
        }
    }
    // set any reds from a previous validation to black again
    var aTD = document.getElementsByTagName('TD');    
    var cellNum = 0;
    while ( aTD[cellNum] ) {
        if ( aTD[cellNum].style && aTD[cellNum].style.color && aTD[cellNum].style.color == 'red' ) {
            aTD[cellNum].style.color = 'black';
        }
        var cellChild = 0;
        while ( aTD[cellNum].childNodes[cellChild] ){
            var obj = aTD[cellNum].childNodes[cellChild];
            if ( obj.style && obj.style.color && obj.style.color == 'red' ) {
                obj.style.color = 'black';
            }
            cellChild++;
        }
        cellNum++;
    }
}
//----- clears any values left from previous Eval
function clearData() {
  for (c=0; c < document.forms[0].length; c++) {
    ele = document.forms[0].elements[c];
    if ( ele.getAttribute('type') == 'text' && ele.id.indexOf('search') < 0 ) {
        ele.value = '';
    }
    else if ( ele.getAttribute('type') == 'radio' ) {
        ele.checked = false;
    }
    else if ( ele.tagName == 'SELECT' && ele.id.indexOf('search') < 0 ) {
        ele.selectedIndex = 0;
    }
  } 
}
//----- makes Course and Instructor Drop Down List selections
function setDDL(aDDLName, sText){
    //alert(sText);   //debug
    var aDDL = document.getElementById(aDDLName);
    //alert('len = ' + aDDL.length + '\nsText = ' + aDDL.options[2].text);   //debug
    for(var c = 0;c < aDDL.length;c++){
        if (sText == aDDL.options[c].value){
            aDDL.selectedIndex = c;
        }
    }
}
//----- fills in scale and comment answers
function setQ(qNum, answer){
    var qEle = document.getElementsByName(qNum);
    if (qEle[4]){    // radio question
        if (answer < 6 && answer > 0) {  // test valid answer
            for(var c = 0;c < 5;c++){
                if (qEle[c].value == answer){
                    qEle[c].checked = true;
                }
            }
        }
    } else {qEle[0].value = answer;}  //Comment Question
}

 

I also did a fair amount of animation…

// the search Results page slides up or down
function toggleDisplay()  {
    //alert('display');  //debug
    // match gets an array, the second value ([1]) is what we are looking for- the value of display
    var cV = document.cookie.match ( 'display=(.*?)(;|$)' );
    if ( (cV) && cV[1] == "down" )  {
        document.cookie = "display=up";
        move("up");
        document.getElementById('ddlCsearch').style.display = ''; 
        document.getElementById('lbIsearch').style.display = ''; 
        document.getElementById('tblSearch').style.width = '800px';
    } else {
        document.cookie = "display=down";
        move("down");
        document.getElementById('ddlCsearch').style.display = 'none';  //ie6
        document.getElementById('lbIsearch').style.display = 'none';   //ie6 
        document.getElementById('tblSearch').style.width = '300px';
    }
}
function move(direction){
    //alert('#2:  move' +  document.getElementById('pnlResults').style.top);  //debug
    var currTop = document.getElementById('pnlResults').style.top.substr(0, document.getElementById('pnlResults').style.top.length - 2);
    if (direction == 'down') {
        if (currTop <= 30) {
            return;
        }else {
            document.getElementById('pnlResults').style.top = currTop - 30;
            setTimeout('move("down")',20);
        }
    }else {
        if (currTop >= 340) {
            return;
        }else {
            document.getElementById('pnlResults').style.top = eval(currTop) + 30;
            setTimeout('move("up")',20);
        }
    }
}

Tags:

VB.Net | Javascript | C# | ASP.Net

About Mike Hogg

Mike Hogg is a c# developer in Brooklyn.

More Here

Favorite Books

This book had the most influence on my coding style. It drastically changed the way I write code and turned me on to test driven development even if I don't always use it. It made me write clearer, functional-style code using more principles such as DRY, encapsulation, single responsibility, and more. amazon.com

This book opened my eyes to a methodical and systematic approach to upgrading legacy codebases step by step. Incrementally transforming code blocks into testable code before making improvements. amazon.com

More Here