Interesting programming ideas, solutions, and logic that I have used to solve problems or have come across throughout my career.
About Me
- William Andrus
- Northglenn, Colorado, United States
- I'm primarily a BI Developer on the Microsoft stack. I do sometimes touch upon other Microsoft stacks ( web development, application development, and sql server development).
Wednesday, April 14, 2010
Only display # rows per page in a SSRS report.
Step 1: SQL
So, first thing is to get your sql query to return, not row numbers but page numbers. So in this case, I used the row_number function found in SQL-Server subtracting 1 and then divided by the number of rows I needed per page:
((row_number() over(order by license_no)) -1) /22 as 'Page'
Step 2: Add Parent Group
In my report I needed to display a header, a footer, detail information, and empty rows if not exceeding 22. This is of course done with SSRS's Table.
First add a new parent group and in the Group Properties/General add a group on to the "Page" field. This group should span the whole page.
Don't bother with the page break sections, this will just mess things up.
Step 3: Add Child Group with blank rows
The child group is what I used to hold the blank rows. Within that group I added 22 rows with no information in the textboxes.
Step 4: Change visibility of blank rows
Right-clicking on the row, and select Row Visibility. Select the bullet: "Show or hide based on an expression". Within each row the expression will check if the row count is less than the ((page + 1) * rows_per_page) - (rows_per_page - position)
Example (Note:"section2" is my dataset):
[Row 22 Expression]: =IIF(CountRows("Section2") < ((Fields!Page.Value + 1) * 22),false,true)
[Row 21 Expression]: =IIF(CountRows("Section2") < ((Fields!Page.Value + 1) * 22) - 1,false,true)
...
This is all that is needed to acomplish this task.
If you need to hide a row until the end, I used the following visibility expression:
=IIF(Fields!Page.Value = Last(Fields!Page.Value,"Section2"),false,true)
And of course if you need to repeat the header on each page, like I do. This is found in the Tablix Properites/General and just put a check mark in the "Repeat header rows on each page".
Friday, February 19, 2010
Precision Timer
Note: The precision of multithreaded timers depends on the operating system, and is typically in the 10-20 milliseconds region. This class is used to generate greater precision using the P/Invoke interop and calls the Windows multimedia timer; which has a precision of 1 ms. But that increased responsiveness comes at a cost - since the system scheduler is running more often, the system spends more time scheduling tasks, context switching, etc. This can ultimately reduce overall system performance, since every clock cycle the system is processing "system stuff" is a clock cycle that isn't being spent running your application.
So here is some code I found and changed up a bit that uses the winmm.dll timesetevent:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Runtime.InteropServices;
- using System.Diagnostics;
- public class PrecisionTimer : IDisposable
- {
- //Lib API declarations
- [DllImport("Winmm.dll", CharSet = CharSet.Auto)]
- static extern uint timeSetEvent(uint uDelay, uint uResolution, TimerCallback lpTimeProc, UIntPtr dwUser, uint fuEvent);
- [DllImport("Winmm.dll", CharSet = CharSet.Auto)]
- static extern uint timeKillEvent(uint uTimerID);
- [DllImport("Winmm.dll", CharSet = CharSet.Auto)]
- static extern uint timeGetTime();
- [DllImport("Winmm.dll", CharSet = CharSet.Auto)]
- static extern uint timeBeginPeriod(uint uPeriod);
- [DllImport("Winmm.dll", CharSet = CharSet.Auto)]
- static extern uint timeEndPeriod(uint uPeriod);
- //Timer type definitions
- [Flags]
- public enum fuEvent : uint
- {
- TIME_ONESHOT = 0, //Event occurs once, after uDelay milliseconds.
- TIME_PERIODIC = 1,
- TIME_CALLBACK_FUNCTION = 0x0000, /* callback is function */
- //TIME_CALLBACK_EVENT_SET = 0x0010, /* callback is event - use SetEvent */
- //TIME_CALLBACK_EVENT_PULSE = 0x0020 /* callback is event - use PulseEvent */
- }
- //Delegate definition for the API callback
- delegate void TimerCallback(uint uTimerID, uint uMsg, UIntPtr dwUser, UIntPtr dw1, UIntPtr dw2);
- private fuEvent f;
- private uint ms;
- //IDisposable code
- private bool disposed = false;
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
- private void Dispose(bool disposing)
- {
- if (!this.disposed)
- {
- if (disposing)
- {
- Stop();
- }
- }
- disposed = true;
- }
- ~PrecisionTimer()
- {
- Dispose(false);
- }
- ///
- /// The current timer instance ID
- ///
- uint id = 0;
- ///
- /// The callback used by the the API
- ///
- TimerCallback thisCB;
- ///
- /// The timer elapsed event
- ///
- public event EventHandler Timer;
- protected virtual void OnTimer(EventArgs e)
- {
- if (Timer != null)
- Timer(this, e);
- }
- ///
- /// Initialize
- ///
- ///
- ///
- public PrecisionTimer(uint ms, bool repeat)
- {
- //Initialize the API callback
- thisCB = CBFunc;
- this.ms = ms;
- //Set the timer type flags
- f = fuEvent.TIME_CALLBACK_FUNCTION(repeat ? fuEvent.TIME_PERIODIC : fuEvent.TIME_ONESHOT);
- //Tell OS that we are about to need a precision timer.
- PrecisionTimer.timeBeginPeriod(1);
- }
- ///
- /// Stop the current timer instance
- /// VERY IMPORTANT TO CALL
- ///
- public void Stop()
- {
- lock (this)
- {
- if (id != 0)
- {
- timeKillEvent(id);
- Trace.WriteLine("Timer " + id.ToString() + " stopped " + DateTime.Now.ToString("HH:mm:ss.ffff"));
- id = 0;
- }
- }
- //Tell OS that we are done using the precision timer and that it can continue back to normal.
- PrecisionTimer.timeEndPeriod(1);
- }
- ///
- /// Start a timer instance
- ///
- /// Timer interval in milliseconds
- /// If true sets a repetitive event, otherwise sets a one-shot
- public void Start()
- {
- //Kill any existing timer
- //Stop();
- lock (this)
- {
- id = timeSetEvent(ms, 0, thisCB, UIntPtr.Zero, (uint)f);
- if (id == 0)
- throw new Exception("timeSetEvent error");
- Trace.WriteLine("Timer " + id.ToString() + " started " + DateTime.Now.ToString("HH:mm:ss.ffff"));
- }
- }
- void CBFunc(uint uTimerID, uint uMsg, UIntPtr dwUser, UIntPtr dw1, UIntPtr dw2)
- {
- //Callback from the PrecisionTimer API that fires the Timer event. Note we are in a different thread here
- OnTimer(new EventArgs());
- }
- }
An example of calling the class:
PrecisionTimer timer = new PrecisionTimer(8500, false); //will time for 8.5 seconds before triggering an event
timer.Timer += new EventHandler(timer_Timer);
timer.Start();
//Do Stuff or something until event
My event handler
void timer_Timer(object sender, EventArgs e){
timer.Stop();
DoStuff();
}
Monday, January 11, 2010
List Creator
You just need to make sure that anything that is return through the query is defined in the object as a write property.
So here is the List Creator code, which does the creation via reflections:
- public class ListCreator where T : new()
- {
- public List FindAll(IDataReader iDataReader)
- {
- List returnList = new List();
- try
- {
- //need a Type and PropertyInfo object to set properties via reflection
- Type tType = new T().GetType();
- PropertyInfo pInfo;
- //x will hold the instance of until it is added to the list
- T x;
- //use reader to populate list of objects
- while (iDataReader.Read())
- {
- x = new T();
- //set property values
- //for this to work, command’s column names must match property names in object
- for (int i = 0; i < iDataReader.FieldCount; i++)
- {
- pInfo = tType.GetProperty(iDataReader.GetName(i));
- pInfo.SetValue(x, (iDataReader[i] == DBNull.Value? null:iDataReader[i]), null);
- }
- //once instance of is populated, add to list
- returnList.Add(x);
- }
- }
- catch (Exception ex)
- {
- Logging.Logging.Error(ex.ToString());
- }
- return returnList;
- }
- }
- public List<PartsOrdering> GetAllPartsOrdering()
- {
- List<PartsOrdering> li = new List<PartsOrdering>();
- try
- {
- using (SqlConnection cn = new SqlConnection(connString))
- {
- ListCreator<PartsOrdering> PartsOrders = new ListCreator<PartsOrdering>();
- SqlCommand cmd = new SqlCommand("dbo.selPartsOrdering", cn);
- cmd.CommandType = CommandType.StoredProcedure;
- cn.Open();
- li = PartsOrders.FindAll(ExecuteReader(cmd));
- }
- }
- catch (Exception ex)
- {
- Logging.Error(ex.ToString());
- }
- return li;
- }
- [Serializable]
- public class PartsOrdering : IFormattable
- {
- #region Properties
- [DefaultValue(-1)]
- public int PartsOrderingID
- {
- get;
- set;
- }
- [DefaultValue("")]
- public string ToolID
- {
- get;
- set;
- }
- [DefaultValue("")]
- public string PartNumber
- {
- get;
- set;
- }
- [DefaultValue("")]
- public string Description
- {
- get;
- set;
- }
- [DefaultValue(0)]
- public int Quantity
- {
- get;
- set;
- }
- [DefaultValue(1)]
- public int PartsOrderingLocationID
- {
- get;
- set;
- }
- [DefaultValue(1)]
- public int PartsOrderingUrgencyID
- {
- get;
- set;
- }
- [DefaultValue("")]
- public string Comment
- {
- get;
- set;
- }
- public DateTime RequestedDateTime
- {
- get;
- set;
- }
- [DefaultValue("")]
- public string RequestedUser
- {
- get;
- set;
- }
- public string LocationName
- {
- get
- {
- List<PartsOrderingLocation> locations = new PartsOrderingLocation().List();
- return locations.Where(n => n.PartsOrderingLocationID.Equals(this.PartsOrderingLocationID)).Select(n => n.Name).First();
- }
- set
- {
- List<PartsOrderingLocation> locations = new PartsOrderingLocation().List();
- this.PartsOrderingLocationID = locations.Where(n => n.Name.Equals(value)).Select(n => n.PartsOrderingLocationID).First();
- }
- }
- public string UrgencyName
- {
- get
- {
- List<PartsOrderingUrgency> locations = new PartsOrderingUrgency().List();
- return locations.Where(n => n.PartsOrderingUrgencyID.Equals(this.PartsOrderingUrgencyID)).Select(n => n.Name).First();
- }
- set
- {
- List<PartsOrderingUrgency> locations = new PartsOrderingUrgency().List();
- this.PartsOrderingUrgencyID = locations.Where(n => n.Name.Equals(value)).Select(n => n.PartsOrderingUrgencyID).First();
- }
- }
- public int UrgencyRank
- {
- get
- {
- List<PartsOrderingUrgency> locations = new PartsOrderingUrgency().List();
- return locations.Where(n => n.PartsOrderingUrgencyID.Equals(this.PartsOrderingUrgencyID)).Select(n => n.Rank).First();
- }
- }
- [DefaultValue(-1)]
- public int PartsOrderingGroupID
- {
- get;
- set;
- }
- [DefaultValue(true)]
- public bool IsActive
- {
- get;
- set;
- }
- }
Friday, January 08, 2010
How to update large count of rows without locking them
http://blogs.msdn.com/sqlpfe/archive/2010/01/06/tsql-coding-patterns-i.aspx
So instead of the usual:
Try:
Tuesday, December 15, 2009
Converting XPS to Bitmap
So, first thing I did was print the pdf to a XPS file. Now I just need to take that XPS file and convert it to some type of usable image that SSRS can recognize.
Found a solution on one of the MSDN message boards; however, I had to make some updates to it:
- static public void SaveXpsPageToBitmap(string xpsFileName)
- {
- DirectoryInfo di = new DirectoryInfo(xpsFileName);
- XpsDocument xpsDoc = new XpsDocument(xpsFileName, System.IO.FileAccess.Read);
- FixedDocumentSequence docSeq = xpsDoc.GetFixedDocumentSequence();
- //DocumentReferenceCollection drc = docSeq.References;
- for (int i = 0; i < docSeq.DocumentPaginator.PageCount; i++)
- {
- DocumentPage docPage = docSeq.DocumentPaginator.GetPage(i);
- BitmapImage bitmap = new BitmapImage();
- RenderTargetBitmap renderTarget = new RenderTargetBitmap((int)docPage.Size.Width, (int)docPage.Size.Height, 96, 96, PixelFormats.Default);
- renderTarget.Render(docPage.Visual);
- BitmapEncoder encoder = new BmpBitmapEncoder();
- encoder.Frames.Add(BitmapFrame.Create(renderTarget));
- FileStream pageOutStream = new FileStream(di.FullName.Substring(0,di.FullName.Length - 4) + "_Page_" + (i+1).ToString() + ".bmp", FileMode.Create, FileAccess.Write);
- encoder.Save(pageOutStream);
- pageOutStream.Close();
- }
- }
So this program works nicely, only problem I currently have is that I have to convert these large size bitmaps (~5MB per page) to something more reasonable. Especially, since the SSRS deployment gives me a SOAP error because of the size. I'll probably just end up converting them to Jpeg later on.
Tuesday, December 01, 2009
Creating an Psion Tecklogix Scan Gun App
Step 1: Getting Psion Resources
You will have to register to get access to the page.
(https://teknet.psionteklogix.com/ptxCMS/Teknet.aspx?s=us&p=DevKits)
Download and install their "Mobile Devices SDK"
Step 2: Create Project
If you go to the "Smart Device" section, you will notices a "Psion Teklogix Device Project". This add the Psion TeklogixNet reference and also the PtxSdkCommon.dll in the root directory, which is a dependency when running your app on device.

Step 3: Add Items to the Toolbar, if needed
Right-click on the Toolbar, and select Choose Items.
Browse to C:\Program Files\Psion Teklogix\Mobile Devices SDK V3.1\DotNet2 and select the PsionTeklogixNet.dll
This should add:

If not already done so, drag each of those items onto the form.

The default app gives a good example, it uses the ScanCompleteEvent; which send in a string value representation of the barcode and places it text into a textbox for the user to see.
- /* $Revision 1.1.1.1 */
- /* Copyright Psion Teklogix Inc. 2007 */
- /*
- * File: Form1.cs
- *
- * Description:
- * Implementation file for Form1 class in PtxApp1
- *
- */
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Drawing;
- using System.Runtime.InteropServices;
- using System.Text;
- using System.Windows.Forms;
- using PsionTeklogix.Barcode;
- using PsionTeklogix.Barcode.ScannerServices;
- /*! <summary>
- Contains the class and functions related to PtxApp1 scanner application
- </summary>
- */
- namespace PtxApp1
- {
- /*
- * Form1
- *
- */
- /// <summary>
- /// The Form1 class generates the Graphical User Interface for PtxApp1.
- /// </summary>
- public partial class Form1 : Form
- {
- /*
- * InitializeScanner
- *
- */
- /// <summary>
- /// Initialize components for PtxApp1.
- /// </summary>
- private void InitializeScanner()
- {
- scanner.Driver = scannerServicesDriver;
- scanner.ScanCompleteEvent += new ScanCompleteEventHandler(scanner_ScanCompleteEvent);
- }
- /*
- * Form1
- *
- */
- /// <summary>
- /// Required for Windows Form designer support.
- /// </summary>
- public Form1()
- {
- try
- {
- InitializeComponent();
- InitializeScanner();
- }
- catch (Exception ex)
- {
- MessageBox.Show("Failed to initialize component: " + ex.ToString());
- this.Close();
- }
- }
- /*
- * Button1_Click
- *
- */
- /// <summary>
- /// This method is called when the scan button is clicked and will scan the
- /// barcode.
- /// </summary>b
- /// <param name="sender">
- /// The calling object that represents the user that sent the message.
- /// </param>
- /// <param name="e">
- /// Contains event data of the Scan Button control
- /// </param>
- private void button1_Click(object sender, EventArgs e)
- {
- try
- {
- scanner.Scan();
- }
- catch (Exception ex)
- {
- MessageBox.Show("Scan error: " + ex.ToString());
- this.Close();
- }
- }
- /*
- * Scanner1_ScanCompleteEvent
- *
- */
- /// <summary>
- /// This method is called when the scan complete event occurs. The method is
- /// called by its respective handler and then displays a text representation
- /// of the barcode on the display.
- /// </summary>
- /// <param name="sender">
- /// The calling object that represents the user that sent the message.
- /// </param>
- /// <param name="e">
- /// Contains event data once the scan is complete
- /// </param>
- delegate void scanner_ScanCompleteDelegate(object sender, ScanCompleteEventArgs e);
- private void scanner_ScanCompleteEvent(object sender, ScanCompleteEventArgs e)
- {
- if (!InvokeRequired)
- {
- textBox1.Text = e.Text;
- }
- else
- {
- Invoke(new scanner_ScanCompleteDelegate(scanner_ScanCompleteEvent),
- new object[] { sender, e });
- }
- }
- }
- }
P.S.
The size of the form shouldn't be bigger than 245 x 300
Friday, July 31, 2009
Project Euler Problem 1 (C# vs F#)
If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
Find the sum of all the multiples of 3 or 5 below 1000.
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace Euler
{
class Program
{
//Add all the natural numbers below one thousand that are multiples of 3 or 5.
//0.001 seconds
static void Main(string[] args)
{
Stopwatch sw = new Stopwatch();
sw.Start();
int total = 0;
for (int i = 1; i <>
{
if (i % 3 == 0 || i % 5 == 0)
{
total += i;
}
}
Console.WriteLine("Answer: " + total);
sw.Stop();
Console.WriteLine(sw.Elapsed);
Console.WriteLine(sw.ElapsedTicks);
Console.ReadLine();
}
}
}
The F# solution:
F#
#light
//0096404 milliseconds
open System.Diagnostics
let stopWatch = new Stopwatch()
stopWatch.Start()
printfn "%A" (List.sum(List.filter (fun n-> (n % 3) = 0 or (n % 5) = 0) [1 .. 999]))
stopWatch.Stop();
printfn "%A"stopWatch.Elapsed
printfn "%A"stopWatch.ElapsedTicks
open System
Console.ReadKey(true)
C# does it in
0.001 seconds
3606 ticks
F# does it in
0.0096404 seconds
24380 ticks
There is probably some more efficient way of doing the F#, this was however my first attempt at learning the language. Will have to see how it can do the more tougher problems.
Thursday, May 21, 2009
Link Server limited return row size from Informix
So I ran into the following error while trying to run a query.
"Maximum output rowsize (32767) exceeded"
The problem with this, was that I was using a SQL SERVER link server, called ETS, to an Informix database. The problem was that I could not limit the size on the "Notes", so basically I ended up using an exec and open query just to accomplish this task.
declare @Query varchar(max)
set @Query = N'select
wrkhdr.region_no,
notif.atten,
notif.dba,
notif.owner,
notif.addr1,
notif.addr2,
notif.city,
notif.state,
notif.country,
notif.postal_code,
notif.notif_id,
notif.notif_date,
notif.start_date,
notif.end_date,
wrkhdr.create_user,
wrkhdr.rid,
wrkhdr.license_no,
notif.license_status,
rtrim(CAST(notes.note as nvarchar(250)))
FROM
notification notif
join workq_hdr wrkhdr ON wrkhdr.workq_id = notif.workq_id
left join document_header ON document_header.filing_id = wrkhdr.filing_id
left join document_header_notes ON document_header.recno = document_header_notes.document_header_notes_id
left join notes ON document_header_notes.notes = notes.note_id
where notif.notif_id = ' + convert(varchar(30),@Parameter1)
set @Query = N'select * from openquery(ets2, ''' + REPLACE(@Query, '''', '''''') + ''')'
exec (@Query)
