Silverlight Zooming and Panning a Canvas

12. May 2009

Zooming and Panning seems to be in great demand for Silverlight projects. I myself searched around through numerous articles trying to figure out good ways to do it. I came up with a solution that hopefully can satisfy some of the demand here on the internets.

First thing I did was make a control that is the zooming and panning canvas. I called it DesignRender as it applied to the particular project I was developing.

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    x:Class="SilverlightApplication2.DesignRender">
    
    <ScrollViewer x:Name="scrollRoot" VerticalScrollBarVisibility="Disabled" 
                  HorizontalScrollBarVisibility="Disabled">
        <Canvas x:Name="canvasRoot">
            <Canvas Background="#FF00FBF2" x:Name="canvasContainer"
                    Width="1000" Height="800">
                <!-- This is dummy stuff for zooming/panning -->
                <Rectangle Width="90" Height="90" Fill="#FFCA1F1F"
                           Canvas.Left="0" Canvas.Top="0" 
                           RadiusX="2" RadiusY="2" />
                <Canvas.RenderTransform>
                    <ScaleTransform x:Name="scaler" 
                                    CenterX="0" CenterY="0" 
                                    ScaleX="1" ScaleY="1" />
                </Canvas.RenderTransform>
            </Canvas>
        </Canvas>
    </ScrollViewer>
</UserControl>

I am using a scrollviewer so I can place the UserControl effectively in a parent control. You can change this as desired. The canvasRoot acts as your background and the canvasContainer is the actual content residing in your background. If this was photoshop, the canvasRoot would be the greyed out background and the canvasContainer would be your actual document. Hence the static sizing of the canvasContainer. Now for the code behind.

public partial class DesignRender : UserControl
{
    private bool isDragging = false;
    private Point offset;
    
    public DesignRender()
    {
        InitializeComponent();

        // Handle the mouse click events within the control
        // for panning.
        this.canvasContainer.MouseLeftButtonUp += 
            new MouseButtonEventHandler(canvasContainer_MouseLeftButtonUp);
        this.canvasContainer.MouseLeftButtonDown +=
            new MouseButtonEventHandler(canvasContainer_MouseLeftButtonDown);
        this.canvasContainer.MouseMove += 
            new MouseEventHandler(canvasContainer_MouseMove);
        
        // Handle the scroll wheel events. They are different
        // per browser so we have to attach to a couple of events.     
        HtmlPage.Window.AttachEvent("DOMMouseScroll", OnMouseWheel);
        HtmlPage.Window.AttachEvent("onmousewheel", OnMouseWheel);
        HtmlPage.Document.AttachEvent("onmousewheel", OnMouseWheel);
    }

    /// <summary>
    /// Called when the mouse wheel is used. Zooms
    /// in our out depending.
    /// </summary>
    private void OnMouseWheel(object sender, HtmlEventArgs e)
    {
        double mouseDelta = 0;
        ScriptObject obj = e.EventObject;
        
        // Get what the mouseDelta was in
        // the particular browser being used
        if (obj.GetProperty("detail") != null)
            mouseDelta = ((double)obj.GetProperty("detail"));
        else if (obj.GetProperty("wheelDelta") != null)
            mouseDelta = ((double)obj.GetProperty("wheelDelta"));
        
        // Quick mouse translation
        mouseDelta = Math.Sign(mouseDelta);

        // calculate the new scale for the canvas
        double newScale = this.scaler.ScaleX + (mouseDelta * .25);

        // Don't allow scrolling too big or small
        if (newScale < 0.25)
            return;
        if (newScale > 10)
            return;

        // Set the zoom!
        this.scaler.ScaleX = newScale;
        this.scaler.ScaleY = newScale;
    }
    
    /// <summary>
    /// Called when user presses mouse down. Start
    /// panning.
    /// </summary>
    private void canvasContainer_MouseLeftButtonDown(
        object sender, MouseButtonEventArgs e)
    {
        // Say we are dragging
        this.isDragging = true;
        this.canvasContainer.CaptureMouse();
        // Calculate the place where they clicked
        offset = e.GetPosition(this.canvasContainer);
        offset.X *= this.scaler.ScaleX;offset.Y *= this.scaler.ScaleY;
    }
    
    /// <summary>
    /// Called when user releases mouse. End panning
    /// </summary>
    private void canvasContainer_MouseLeftButtonUp(
        object sender, MouseButtonEventArgs e)
    {
        if (this.isDragging)
        {
            // Say we are done dragging
            this.isDragging = false;
            this.canvasContainer.ReleaseMouseCapture();
        }
    }
    
    /// <summary>
    /// Called when the user moves there mouse. If
    /// they have the mouse button pressed then we
    /// pan.
    /// </summary>
    private void canvasContainer_MouseMove(
        object sender, MouseEventArgs e)
    {
        if (this.isDragging)
        {
            // Calculate the new drag distance
            Point newPosition = e.GetPosition(this.canvasRoot);
            Point newPoint = 
                new Point(newPosition.X - offset.X,newPosition.Y - offset.Y);
            
            // Set the values
            this.canvasContainer.SetValue(Canvas.LeftProperty,newPoint.X);
            this.canvasContainer.SetValue(Canvas.TopProperty,newPoint.Y);
        }
    }
}

The code itself should be pretty self explanatory. I use scaling to do the zooming and mouse down events for panning. Now just host this control in your Page.xaml whatever way you would like. This should get everyone started in the right direction. Hope this helps.



Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

C#, Silverlight , , ,

Serializing and Deserializing XML to files

7. April 2009

This is a simple class for serializing a type to a file and deserializing a type from a file. This code shouldn’t be considered production, but it will show you a very simple way to do it.

    /// <summary>
    /// Class for saving xml serializable types 
    /// to a file.
    /// </summary>
    /// <typeparam name="T">The generic serializable
    /// type that is going to be serialized or deserialized</typeparam>
    public class TypeSerializer<T>
    {
        /// <summary>
        /// Serializes and saves the specified item
        /// to a file specified.
        /// </summary>
        /// <param name="item">The item to serialize.</param>
        /// <param name="path">The path to the file.</param>
        public void Save(T item, string path)
        {
            // Create the serializer
            XmlSerializer s = new XmlSerializer(typeof(T));
            // Create the writer
            TextWriter w = new StreamWriter(path);
            // Do the work
            s.Serialize(w, item);
            // Clean up
            w.Close();
        }

        /// <summary>
        /// Loads the specified file and serializes it.
        /// </summary>
        /// <param name="path">The path to the item.</param>
        /// <returns>The serialized type.</returns>
        public T Load(string path)
        {
            // Create the serializer
            XmlSerializer s = new XmlSerializer(typeof(T));
            // The item we are returning
            T item;
            // The text reader that puts the text in the stream
            TextReader r = new StreamReader(path);
            // Deserialize the item and cast it
            item = (T)s.Deserialize(r);
            // Clean up
            r.Close();
            // return the item
            return item;
        }
    }
Simple to use. Just call new TypeSerializer<YourClass>().Save(item, path); to save it or TypeSerializer<YourClass>().Load(path); to load it. Thank you CodeProjectfor the step in the right direction. I just wanted a simpler example to start people out. I hope this helps.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

C# , ,

Excel Importer

17. March 2009

I wrote this little WPF application as the demonstration for my previous article on reading excel spreadsheets. I figured everyone should be able to test it and play around with it. Just a note: The code was written very fast and inefficiently so don’t consider it anything more than proof of concept. At any rate, here you go.

ExcelImporter.zip (18.51 kb)



Currently rated 4.0 by 1 people

  • Currently 4/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

C#, Excel , , ,

Reading Excel with C#

25. February 2009

Overview

This article explains how to read Excel documents with C#. After two or three projects dealing with copying and pasting data from sql server to excel, I decided that it would be a good time to write some code to help with the process. Here is the basic code for reading an excel spreadsheet.

Code

// Create the connection string
string connectionString = @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" +
    location + @";Extended Properties=""Excel 8.0;HDR=YES;""";

// Create a factory
DbProviderFactory factory =
       DbProviderFactories.GetFactory("System.Data.OleDb");

// Create an adapter
DbDataAdapter adapter = factory.CreateDataAdapter();

// Create a select command to get the dataset
DbCommand selectCommand = factory.CreateCommand();
selectCommand.CommandText = "SELECT * FROM [" + sheetName + "$]";

// Set up the connection and the connection string
DbConnection connection = factory.CreateConnection();
connection.ConnectionString = connectionString;
selectCommand.Connection = connection;

// Set up the command
adapter.SelectCommand = selectCommand;

// Create the dataset
DataSet ds = new DataSet();

// Load the results
adapter.Fill(ds);

return ds;
Basically, I am just passing in a location of the document and the sheet name for the particular excel spreadsheet. OLEDB supports Outlook so bringing it into a dataset isn’t that much work. I realize that the article is relatively slim, but I hope it gets viewers pointed in the right direction. Thank you Dave Hayden for my step.

Happy Coding



Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Excel, C#

C# and the G15 Display

14. October 2008

Overview

I just purchased the Logitech G15 Keyboard; being the geek that I am the first thing that I had to do with it today was develop something for the LCD Display. There are a lot of tutorials around on developing for the C++ SDK, but there were very few on developing with C#. This article explains how to do so.

Hello World (Of Course)

I found that there was a managed library for the LCD display, so I decided to use it. You can find it here. Note that you should probably finish the article because the library isn't perfectly straightforward. Once you have the library unzipped, you do the following steps:

  • Open up Visual Studio (I used 2008)
  • Create a new Windows Forms Application
  • Right click on the project file and click Add Reference
  • Click the browse tab
  • Look for the directory that you unzipped the managed library to. Navigate to the lgLcdClassLibrary/Release folder and ad the lgLCDNETWrapper.dll file.

This is the non-straightforward part. I did my development and found that my application was not working. You have to add another library to the bin directory of your application or it will not work. The reason is that the managed library is a wrapper around another unmanaged library and if you don't have both of the libraries the application will not work.

  • Open up Windows Explorer and navigate to the directory where you unzipped the managed SDK. 
  • Go into the CLDemo-CS/Release folder and copy the lgLcdLibWrapper.dll file. 
  • Paste this file into your bin/debug folder of your application.

Finally, we can get to coding. Open up the code behind file of your Form1.cs file and make it look like mine.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using lgLcdClassLibrary;
using System.Drawing.Imaging; 
namespace LogitechTest1
{    
    public partial class Form1 : Form    
    {        
        // The LCD interface class in the wrapper.        
        private LCDInterface lcd;        
        // A byte array we are going to convert a bitmap too        
        byte[] lcdTranslate;     
    
        // The text to write on the screen        
        string myText = "Hello World";        
        // The friendly applicaiton name        
        string appName = "Test LCD App";        
        // The font family used        
        string fontFamily = "Arial";
        
        // The font size of the text        
        float fontSize = 10.0f;        
        // The brush to write the font with        
        Brush myBrush;        
        // The font to use to write        
        Font myFont;
         
        // This is the width and height of the screen        
        int width = 160;        
        int height = 43;
        
        // Some offsets to position the text better        
        float col1Offset = 2;        
        float row1Offset = 1;
         
        // The bitmap placed on the screen        
        Bitmap bmp; 
       
        // The amazing GDI graphics object        
        Graphics g;  
       
        /// <summary>        
        /// Creates a new instance of the form        
        /// </summary>        
        public Form1()        
        {            
            // Visual Studio Generated            
            InitializeComponent(); 
            
            // Subscribe to the closing event for the form            
            // Note: It is better to do this in the designer            
            // but I wanted compileable code            
            this.FormClosing +=                 
                new FormClosingEventHandler(Form1_FormClosing);
             
            // Initialize the LCD            
            this.lcd = new LCDInterface();            
            // Open a connection to it            
            this.lcd.Open(this.appName, true);            
            // Initialize our byte array            
            this.lcdTranslate = new byte[this.width * this.height]; 
            
            // Initialize the font            
            this.myFont = new Font(this.fontFamily,                 
                this.fontSize, FontStyle.Regular);            
            // Initialize the brush            
            this.myBrush = new SolidBrush(Color.White);
             
            try            
            {                
                // Create the bitmap                
                this.bmp = new Bitmap(this.width, this.height,                     
                    PixelFormat.Format24bppRgb);
                 
                // Create a graphics object from the bitmap                
                this.g = Graphics.FromImage(this.bmp);
                 
                // Clear the graphics object               
                g.Clear(Color.Black);
                 
                // Draw our text onto the graphics object                
                g.DrawString(this.myText, this.myFont,                     
                    this.myBrush, this.col1Offset, this.row1Offset);
                 
                // Create temporary color and byte                
                Byte pixelValue;
                 
                // This is where we translate the bitmap to                
                // a byte array                
                for (int hIndex = 0; hIndex < this.height; ++hIndex)                
                {                    
                    for (int wIndex = 0; wIndex < this.width; ++wIndex)                    
                    {                        
                        // Get the green value of the current pixel                        
                        pixelValue = this.bmp.GetPixel(wIndex, hIndex).G;                        
                        // Place it in our translator                        
                        lcdTranslate[wIndex + (hIndex * this.width)] =                             
                            pixelValue;                    
                    }                
                }
                 
                // Send the lcd the translated information                
                this.lcd.DisplayBitmap(ref lcdTranslate[0],                     
                    LCDInterface.lglcd_PRIORITY_NORMAL);            
            }            
            catch (Exception e)            
            {                
                // Show any issues that arose                
                MessageBox.Show(e.Message);            
            }        
        }
         
        /// <summary>        
        /// Fired when the form closes. Closes the        
        /// connection to the LCD screen.        
        /// </summary>        
        private void Form1_FormClosing(           
            object sender, FormClosingEventArgs e)        
        {            
            // Close the connection to the screen            
            this.lcd.Close();        
        }    
    }
}

Now build and run the application. When the form pops up, press the black circle button to the left of the LCD until it says "Test LCD App" ... Amazing! Hello World should pop up on your screen.

Code Rundown

Basically, we are creating a bitmap that is the size in pixels of the Lcd screen. From there, we create a graphics object from the bitmap and draw text to it. The next part is a bit tricky. The managed library wants a byte array instead of the actual bitmap itself so we do a little translation of all of the pixels to get it into the correct format. Finally, we display the information.

Conclusion

I hope this article puts you in the right direction for developing amazing things with the Logitech G15.



Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

C#

Developing C# and Outlook 2007

26. September 2008

Overview

This article is going to be a part of a multi-part series on developing with C# and Outlook. The goal of this post is to connect to the Outlook API and create a task with categories. This article assumes you are using Visual Studio 2008 with the .NET Framework version 3.5. These articles are considered proofs-of-concept to aid you in developing better apps than I.

Create a Console Application

Open up visual studio and create a console application. We are creating a console application as apposed to an Outlook plugin to concentrate on the API itself allowing you to develop libraries, services, and plugins for Outlook. So I'm just going to put the basics out there and then explain what everything does.

  • Open up Visual Studio
  • Create a Console Application
  • Right click on the project and select Add Reference
  • Under the .NET tab click Microsoft.Interop.Outlook (the newest version)
  • Click ok

Use this code for the Program.cs file:

using System;
using Microsoft.Office.Interop.Outlook;

/// <summary>
/// This is the class acting as the entry
/// point for the application.
/// </summary>
class Program
{
    /// <summary>
    /// This is the entry point for the application
    /// </summary>
    /// <param name="args">Any arguments
    /// to pass into the application (not used).</param>
    static void Main(string[] args)
    {
        // Create an instance of the outlook application
        Application outlookApp = new ApplicationClass();
        // Get the namespace from the application
        NameSpace outlookNamespace = outlookApp.GetNamespace("MAPI");

        // Create a task
        TaskItem task = (TaskItem)outlookApp.CreateItem(OlItemType.olTaskItem);

        // Set up some properties
        task.Subject = "My New Automated Task";
        task.Categories = "@Computer,@Home";

        // Save the task
        task.Save();
    }
}

Explanation

Hopefully the commenting will help the explanation. The first part creates an outlook application and assigns it to the Application interface. This application interface has access to versioning information as well as helper methods. We then get the namespace (folder) in which we are going to add the task to. We are passing in the type (outlook uses MAPI).

The create task is an important one. This line of code can be modified to create mail items, journal items, calendar items, contact items, etc. The outlookApp.CreateItem is a factory method to which we pass in the outlook item type. Finally, we call the save method for the item which places it in to your outlook.

Conclusion

This is a good start to get you on your way. I think this code is simple enough to allow you to fiddle with it and see what you can do. In the near future I will post about manipulating the more complex features of Outlook.



Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

C#

Expanding / Collapsing the CollapsiblePanelExtender Programatically

6. February 2008

I ran into the problem of collapsing my CollapsiblePanelExtender programatically. This is a quick solution to that for those running into the same problem.

Here is the code:

this.cpeTest.ClientState = "true";
this.cpeTest.Collapsed = true;

And here is the control information:

<ajaxControlToolkit:CollapsiblePanelExtender
    id="cpeTest"
    runat="server"
    TargetControlID="pnlTarget"
    Collapsed="False"
    ExpandControlID="divExpand"
    CollapseControlID="divCollapse"
    AutoCollapse="False"
    ScrollContents="False"
    TextLabelID="lblExpand"
    CollapsedText="Expand"
    ExpandedText="Collapse"
    ExpandDirection="Vertical" />

I hope this helps :).



Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Asp.Net, C# ,

Calling C# Code From JavaScript

6. February 2008

Every once in a while people want to call their server-side code from JavaScript. I ran into this particular difficulty myself to find that many people online (including inside the msdn forums) say that this cannot be done. Well I hope it hasn't led too many people astray as I am going to demonstrate it here. So first thing is first:

  • Create a new Website (It doesn't even have to be an AJAX-Enabled Website)
  • Open the Default.aspx Page and change it to the following:
<%@ Page=""   Language="C#" AutoEventWireup="true"
         CodeFile="Default.aspx.cs" Inherits="_Default" %>

  <!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">
      Calling C# from JavaScript
      <script type="text/javascript">
        function RecieveServerData(arg, context)
        {
        Message.innerText = arg;
        }

        <form id="form1" runat="server">

          <input type="button" value="Callback"
             onclick="CallServer('Calling From Button', null)" />
          <span id="Message">

Now for the code-behind:

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public partial class _Default : System.Web.UI.Page,
   ICallbackEventHandler
{
    string fromClient;

    ///
    /// Fired when the page loads. Creates
    /// JavaScript to connect to the server.
    ///
    ///
    ///
    protected void Page_Load(
       object sender, EventArgs e)
    {
        // Get javascript function from the
        // server that is capible of connecting
        // to our server. RecieveServerData is
        // the javascript method we will call when
        // we are done processing the data
        string reference =
           this.ClientScript.GetCallbackEventReference(
           this, "arg", "RecieveServerData", "");

        // Generate the javascript that can connect
        // to the server
        string callBackScript =
           @"   function CallServer(arg, context)
            {" + reference + ";}";

        // Put the javascript on the page
        this.ClientScript.RegisterClientScriptBlock(
           this.GetType(), "CallServer",
           callBackScript, true);
    }

    ///
    /// This is the method called by the
    /// javascript. Here you can do some server
    /// processing to manage what you are going
    /// to give back to the client if anything.
    ///
    ///
    public void RaiseCallbackEvent(
       string eventArgument)
    {
        /* Im saving stuff here */
        this.fromClient = eventArgument;
    }

    ///
    /// This is the method that passes back
    /// to the javascript.
    ///
    ///
    public string GetCallbackResult()
    {
        return this.fromClient;
    }
}

And that is it. Run it and you will be sending information to the server, processing it, and sending the information back to the client. How does it work?  I ICallbackEventHander interface defines the contract allowing the javascript to connect to it. The registration in the Page_Load() generates javascript capable of connecting to the server, and the resit is in your server-side code. This functionality is not limited to pages as well. As long as you can access the page to do the script registration, you can create server-side code that can be connected to by client-side javascript.

This is very useful, but be aware of the security risks involved. Always make sure to validate the input. Hope this helps.



Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

C#, Asp.Net , , ,