2008-05-08

Capture a key in a control or form

Last days I was in another trouble. I need to execute a function when i stroke a key (in this example Enter Key).
My primary option was asociate a button as default, then when I press Enter, i will be like I click in the button. But my form don't allow a button in this place (it doesn't look well).
But C# 2.0 has a simple solution to this case: IMessageFilter

First the controls needs implement IMessageFilter interface and overrides the PreFilterMessage method

public class MyControl: UserControl, IMessageFilter
{
...........
public bool PreFilterMessage(ref Message msg)
{
........
}
...........
}

In the PreFilterMessage method you can capture all messages and allow not enter key stroke messages pass.
const int WM_KEYDOWN = 0x100;
public bool PreFilterMessage(ref Message msg)
{
Keys keyCode = (Keys)(int)msg.WParam & Keys.KeyCode;
if (msg.Msg == WM_KEYDOWN && keyCode == Keys.Return)
{
DoSomeAction();
return true;
}
return false;
}


If you implement this code in a UserControl or in a Form, it will seem don't to work. I'ts because you need activate the filter:

Application.AddMessageFilter(MyControlInstance);

As you can imagine, using this workaround reduces your application perfomance.

2008-03-24

CompositeKey<T,K>

Last week a generic Dictionary was needed to organize a list of values.
The normal use was Dictionary<T,M> where T is the type of the key class, and M is the type of object.
But I was looking a more complex key: not a Guid, it was 2 Guid fields.
THe trouble was:
I had a List<MYCompositeClass>()
MYCompositeClass had 3 values (two of them was the key (KeyA,KeyB)).
To solve this, I created a CompositeKeY(T,K) (T y K is the types of KeyA and KeyB) and I created a Dictionary<CompositeKey<T,K>,List<M>>
If you use ContainsKey with a object generated based on MyCompositeClass, then it didn't work because the seach in the Dictionary<> was by HashCode, not by values of fields.
We need to overrides Equals and GetHashCode (well, this is the shortest method, see below for the longest )

public class CompositeKeY<T, K>
{

private T keyA;
private K keyB;

public T KeyA
{
get{ return keyA;}
set{keyA = value;}
}
public K KeyB
{
get{return keyB;}
set{keyB = value;}
}

public CompositeKeY(T keyA, K keyB)
{
this.keyA = keyA;
this.keyB = keyB;
}

public override bool Equals(object obj)
{
CompositeKeY<T, K> objCompare = obj as CompositeKeY<T, K>;
bool equal = false;
if (objCompare != null)
{
if (this.KeyA.Equals(objCompare.KeyA) && this.KeyB.Equals(objCompare.KeyB))
{
equal = true;
}
}
return equal;
}
public override int GetHashCode()
{
return KeyA.GetHashCode() ^ KeyB.GetHashCode();
}

}

Now we can create
class MYCompositeClass
{
public Guid KeyA;
public Guid KeyB;
public String Value;
}

Dictionary<CompositeKey<Guid,Guid>,List<String>> myDictionary = new Dictionary<CompositeKey<Guid,Guid>,List<String>>();
foreach (MyCompositeClass element in ListOfMyCompositeClass)
{
CompositeKey<Guid,Guid> key = new CompositeKey<Guid,Guid>(element.KeyA,element.KeyB);
if(myDictionary.ContainsKey(key))
{
myDictionary.Add(key,new List<String>);
}
myDictionary[key].Add(element.Value);
}

And It's works. If you removes the override of GetHashCode(), then it doesn't work.

Another method to do the same was using a IEqualityComparer class.

public class CompositeKeyEqualityComparer<T, K> : IEqualityComparer<CompositeKey<T, K>>
{
public bool Equals(CompositeKey<T, K> x, CompositeKey<T, K> y)
{
return Equals(x.KeyA, y.KeyA) && Equals(x.KeyB, y.KeyB);
}

public int GetHashCode(CompositeKey<T, K> obj)
{
return obj.KeyA.GetHashCode() ^ obj.KeyB.GetHashCode();
}

}

Then you can removes el overrides, but you must say to the Dictionary's constructor the IEqualityComparer must use.

Dictionary<CompositeKey<Guid,Guid>,List<String>> myDictionary = new Dictionary<CompositeKey<Guid,Guid>,List<String>>(new CompositeKeyEqualityComparer<Guid,Guid>());
foreach (MyCompositeClass element in ListOfMyCompositeClass)
{
CompositeKey<Guid,Guid> key = new CompositeKey<Guid,Guid>(element.KeyA,element.KeyB);
if(myDictionary.ContainsKey(key))
{
myDictionary.Add(key,new List<String>);
}
myDictionary[key].Add(element.Value);
}

There was at least two solucions to my problem (I didn't more to my work).

2007-10-05

Create object code snippet

This is another Code Snippet very usefull to me.
You can see in a previous post how to implement it in Visual Studio 2005.


<?xml version="1.0" encoding="utf-8"?>

<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">

  <CodeSnippet Format="1.0.0">

    <Header>

      <Title>vnew</Title>

      <Shortcut>vnew</Shortcut>

      <Description>Code snippet for create a basic new variable</Description>

      <Author>Red Line .Net</Author>

    </Header>

    <Snippet>

      <Declarations>

        <Literal Editable="true">

          <ID>type</ID>

          <ToolTip>Variable type</ToolTip>

          <Default>Object</Default>

          <Function>

          </Function>

        </Literal>

        <Literal Editable="true">

          <ID>variable</ID>

          <ToolTip>The variable backing this property</ToolTip>

          <Default>myVar</Default>

          <Function>

          </Function>

        </Literal>

        <Literal Editable="true">

          <ID>parameters</ID>

          <ToolTip>Parameters Constructor</ToolTip>

          <Default>

          </Default>

          <Function>

          </Function>

        </Literal>

      </Declarations>

      <Code Language="csharp"><![CDATA[$type$ $variable$ = new $type$($parameters$);]]></Code>

    </Snippet>

  </CodeSnippet>

  <CodeSnippet Format="1.0.0">

    <Header>

      <Title>

      </Title>

      <Shortcut>

      </Shortcut>

      <Description>

      </Description>

      <Author>

      </Author>

    </Header>

    <Snippet>

      <Code Language=""><![CDATA[]]></Code>

    </Snippet>

  </CodeSnippet>

</CodeSnippets>

2007-10-02

NotImplemented Code Snippet

IntelliSense Code Snippets are reusable, task-oriented blocks of code.
This is a example to see

You must create a new file throwNotImplemented.snippet in the \Visual Studio 2005\Code Snippets\Visual C#\My Code Snippets folder in your My Documents folder.
The snippet can be used from VS2005 IDE without another configuration.

Code to write:

<?xml version="1.0" encoding="UTF-8"?>

<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">

  <CodeSnippet Format="1.0.0">

    <Header>

      <Title>throw NotImplementedException</Title>

      <Author>Red Line .Net</Author>

      <Description>Create a line to throw a NotImplementionException</Description>

      <Keywords>

        <Keyword>"Exception"</Keyword>        

      </Keywords>

      <Shortcut>throwNotImplemented</Shortcut>

    </Header>

    <Snippet>

      <Declarations>

        <Literal>

          <ID>message</ID>

          <Type>String</Type>

          <ToolTip>Reason for not implementation</ToolTip>

          <Default>The method or operation is not implemented.</Default>

        </Literal>

      </Declarations>

      <Code Language="csharp" Kind="method body">

        <![CDATA[throw new NotImplementedException("$message$");

        $selected$$end$]]>

      </Code>

    </Snippet>

  </CodeSnippet>

</CodeSnippets>


To use:
In a class code, write throwNotImplemented, press tab key and the IDE writes a line.

throw new Exception("The method or operation is not implemented.");

You can use the shortcuts using ctrl+K,X and you search the snippet in My Code Snippets folder

More info in http://msdn2.microsoft.com/en-us/library/ms165392(VS.80).aspx

2007-09-04

Let the computer works instead you (I)

System.CodeCom namespace can help you to avoid a lot of waste of time typing property declarations in classes when you did the work in a database or excel or xml file. And when you need to edit the class (to add a new property or field), you must edit the definition template (database, excel, xml) and go to source code to edit class file too. Microsoft give us tools to automate this class generation, lets see this tools.
Fists we see how to create a class on runtime. We use System.CodeCom namespace to create a CodeCompileUnit object. With this CodeCompileUnit you can write it to a text file to use.

Let's see a bit of code

CodeCompileUnit code = new CodeCompileUnit();

//Create a namespace
CodeNamespace unitNamespace = new CodeNamespace("MyNameSpace");
code.Namespaces.Add(unitNamespace);

//Create a class
CodeTypeDeclaration unitClass = new CodeTypeDeclaration("ClassName");
unitClass.IsPartial = true; // You can define a partial class, very useful to avoid write code in the automated file.

CodeCommentStatement coment = new CodeCommentStatement("This class 'ClassName' do nothing");
unitClass.Comments.Add(coment);


unitNamespace.Types.Add(unitClass);

//Create a field and a property related
string fieldName = "myField";
string propertyName = "MyProperty";

CodeMemberField codeField = new CodeMemberField("System.String", fieldName);
unitClass.Members.Add( codeField );

CodeMemberProperty unitProperty = new CodeMemberProperty();
unitProperty.Name = propertyName;
unitProperty.Type = new CodeTypeReference("System.String");
unitProperty.Attributes = MemberAttributes.Public;

unitProperty.GetStatements.Add( new CodeMethodReturnStatement( new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName) ) );
unitProperty.SetStatements.Add( new CodeAssignStatement( new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), new CodePropertySetValueReferenceExpression() ) );

unitClass.Members.Add(unitProperty);


This simple code creates a CodeCompileUnit object.

Now you can traslate this object to a file.


CodeDomProvider provider = new Microsoft.CSharp.CSharpCodeProvider(); //Of course, you can select a Microsoft.VisualBasic.VBCodeProvider

// Create an IndentedTextWriter, constructed with a StreamWriter to the source file.
IndentedTextWriter tw = new IndentedTextWriter(new StreamWriter(filename, false), " ");
// Generate source code using the code generator.
provider.GenerateCodeFromCompileUnit(compileunit, tw, new CodeGeneratorOptions());
// Close the output file.
tw.Close();


To compile this code you need using that namespaces:
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.IO;
using Microsoft.CSharp;


Of course a more complete reference is in MSDN pages (http://msdn2.microsoft.com/es-es/library/system.codedom.codecompileunit(VS.80).aspx)