Thursday, June 11, 2009

Retrieving domain users in a Silverlight application using a WCF Service

When you need to get the domain users in a Silverlight application the best option is to go behind a WCF service.Create your WCF service and expose the end points . The proxy created in the Silverlight application can get the details of the domain users using the methods exposed in the WCF.

WCF Class

[ServiceContract]
public class UserDomain
{
[OperationContract]
public List<DomainUser> getDomainUsers()
{
DirectorySearcher Search = new DirectorySearcher();
Search.SearchRoot = new DirectoryEntry("LDAP://dc=abc,dc=com");//here abc represents the domain name
Search.Filter = "(&(objectclass=user)(objectcategory=person))";
Search.SearchScope = SearchScope.Subtree;
Search.PropertiesToLoad.Add("userPrincipalName");

SearchResultCollection colQueryResults = Search.FindAll();
List<DomainUser> users = new List<DomainUser>();
foreach (SearchResult Result in colQueryResults)
{
if (Result.Properties["userPrincipalName"] != null)
{
if (Result.Properties["userPrincipalName"].Count > 0)
{
users.Add(new DomainUser() { ID = Result.Properties["userPrincipalName"][0].ToString(),
DisplayName = Result.Properties["name"][0].ToString() });
}
}
}
return users;
}
}
[DataContract]
public class DomainUser
{
[DataMember]
public string ID { get; set; }
[DataMember]
public string DisplayName { get; set; }
}


Silverlight Implementation

Create the Silverlight application and add a service reference to the above created WCF service.Create the client proxy with the added service and call the WCF method to retrieve the domain users.


Code

public partial class MainPage : UserControl
{
public MainPage()
{
try
{
InitializeComponent();
UserDomainClient Proxy = new UserDomainClient();
Proxy.getDomainUsersCompleted += new EventHandler<getDomainUsersCompletedEventArgs>(Proxy_getDomainUsersCompleted);
Proxy.getDomainUsersAsync();
}
catch (Exception ex)
{

MessageBox.Show(ex.Message);
}
}

void Proxy_getDomainUsersCompleted(object sender, getDomainUsersCompletedEventArgs e)
{
List<DirectoryServiceExt.DomainUser> lstUsers=e.Result;
}
}



Make sure that you use basicHttpbinding while deploying the WCF service

Getting Started with NSIS(Nullsoft Scriptable Install System)

NSIS (Nullsoft Scriptable Install System) is a tool that allows programmers to create such installers for Windows.It is released under an open source license and is completely free for any use.

NSIS creates installers that are capable of installing, uninstalling, setting system settings, extracting files, etc. Because it's based on script files, you can fully control every part of your installers. The script language supports variables, functions, string manipulation, just like a normal programming language - but designed for the creation of installers. Even with all these features, NSIS is still the smallest installer system available. With the default options, it has an overhead of only 34 KB. Let's now try a very simple installer with the NSIS as below

1. Download latest NSIS and install

2. Open any text editor and copy the following snippet. Save this file as .nsi

;-------------------------------------------------------------------------
; Installer script for My Installer 1.0
;-------------------------------------------------------------------------
;--------------------------------------------
; General definitions
!define PRODUCT_NAME "My Installer"
!define PRODUCT_VERSION_MAJOR 1
!define PRODUCT_VERSION_MINOR 0
!define PRODUCT_DISPLAY_VERSION "1.0"
!define PRODUCT_PUBLISHER "XYZ, Inc."
!define PRODUCT_WEB_SITE "http://www.XYZ.com"
!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME} 1.0"
!define PRODUCT_UNINST_ROOT_KEY "HKLM"
!define PRODUCT_INSTALL_DIR "$PROGRAMFILES\XYZ\${PRODUCT_NAME} 1.0"

;--------------------------------------------
; Maximum compression
SetCompressor /SOLID lzma

;--------------------------------------------
; Modern UI (MUI 1.67 compatible) definitions
!include "MUI2.nsh"

; MUI Settings
!define MUI_ABORTWARNING
!define MUI_ICON "resources\icon.ico"
!define MUI_UNICON "resources\icon.ico"
!define MUI_HEADERIMAGE
!define MUI_HEADERIMAGE_BITMAP "resources\header.bmp"
!define MUI_HEADERIMAGE_RIGHT
!define MUI_HEADER_TRANSPARENT_TEXT
!define MUI_WELCOMEFINISHPAGE_BITMAP "resources\welcome.bmp"
!define MUI_WELCOMEFINISHPAGE_BITMAP_NOSTRETCH
;!define MUI_FINISHPAGE_NOAUTOCLOSE

; Welcome page
!insertmacro MUI_PAGE_WELCOME

; License page
!define MUI_LICENSEPAGE_CHECKBOX
!insertmacro MUI_PAGE_LICENSE "resources\license.rtf"

; Instfiles page
!insertmacro MUI_PAGE_INSTFILES

; Finish page
!define MUI_FINISHPAGE_SHOWREADME "$INSTDIR\README.txt"
!define MUI_FINISHPAGE_NOREBOOTSUPPORT
!insertmacro MUI_PAGE_FINISH

; Uninstaller pages
!insertmacro MUI_UNPAGE_INSTFILES

; Language files
!insertmacro MUI_LANGUAGE "English"

!include "nsDialogs.nsh"
!include "LogicLib.nsh"
!include "StrFunc.nsh"
${StrLoc}

;--------------------------------------------
; Installer Settings
Name "${PRODUCT_NAME} v1.0"
OutFile "XYZ_1.0.exe"
InstallDir "${PRODUCT_INSTALL_DIR}"
ShowInstDetails show
ShowUnInstDetails show
BrandingText "${PRODUCT_PUBLISHER}"
RequestExecutionLevel admin

;-------------------------------------------------------------------------
; Initialize Function
Function .onInit

; Determine if installed
ReadRegStr $R0 ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString"
StrCmp $R0 "" Proceed EqualMinor

EqualMinor:
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION \
"${PRODUCT_NAME} is already installed. $\n$\nClick `OK` to remove the \
previous installation or `Cancel` to cancel." \
IDOK UninstallPrevious
Abort

;--------------------------------------------
; Run the uninstaller
UninstallPrevious:
HideWindow
ClearErrors
ExecWait '$R0 _?=$INSTDIR' ;Do not copy the uninstaller to a temp file
Delete '$R0'
BringToFront
Goto Proceed

Proceed:

FunctionEnd

;-------------------------------------------------------------------------
; Main Install Section
Section "MainSection" MainSection

DetailPrint "Installing Components..."
SetOutPath "${PRODUCT_INSTALL_DIR}"
SetOverwrite ifnewer
File "/oname=abc.png" "Build\abc.png"
File "/oname=icon.ico" "resources\blendables1.ico"

SectionEnd

;-------------------------------------------------------------------------
; Installer Finished callbacks
Function .onInstSuccess
FunctionEnd

Function .onInstFailed
FunctionEnd

;-------------------------------------------------------------------------
; Post Install
Section -Post
WriteUninstaller "$INSTDIR\uninst.exe"

;--------------------------------------------
; Add/Remove registry settings
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\icon.ico"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_DISPLAY_VERSION}"
WriteRegDWORD ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "VersionMajor" "${PRODUCT_VERSION_MAJOR}"
WriteRegDWORD ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "VersionMinor" "${PRODUCT_VERSION_MINOR}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
SectionEnd

;-------------------------------------------------------------------------
; Uninstaller
Function un.onUninstSuccess
HideWindow
MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) was successfully removed from your computer."
FunctionEnd

Function un.onInit
MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "Are you sure you want to completely remove $(^Name) and all of its components?" IDYES +2
Abort
FunctionEnd

Section Uninstall

;--------------------------------------------
; Registry
DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
Delete "$INSTDIR\icon.ico"
Delete "$INSTDIR\uninst.exe"
Delete "${PRODUCT_INSTALL_DIR}\abc.png"
RMDir /r "${PRODUCT_INSTALL_DIR}"
SetAutoClose True

SectionEnd



3. Create a folder named Resources and copy/create the files like .ico and .rtf as mentioned in the script. This folder should be created in the same location where the .nsi file is saved



4. Right click the ".nsi" file and select "Compile NSIS Script"



5. A compilation window will pop up and will list the errors with line numbers if the compilation failed. I would recommend using Notepad++ as the editor for nsi script as it is very easy to navigate to the line number mentioned in the compiler window (for errors in the script) . If the compilation went with out any errors the installer will be ready and available at the same location as the .nsi file.

Wednesday, June 10, 2009

Design time Experience for your WPF/Silverlight controls

It is always a good practice to ship your WPF/Silverlight controls with its design time experience. Design time experience can be extended with the help of design assemblies supplies by Microsoft

Steps :

Create your WPF/Silverlight control

Create a visual studio class library project and name it MyControls

Subclass a control of your choice and add one dependency property. I did that for a button control in my sample. I used this control to demonstrate the property Editor in design time extensibility

public class MyButton : Button
{

public double MyHeight
{
get { return (double)GetValue(MyHeightProperty); }
set { SetValue(MyHeightProperty, value); }
}

// Using a DependencyProperty as the backing store for MyHeight. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyHeightProperty =
DependencyProperty.Register("MyHeight", typeof(double), typeof(MyButton), new UIPropertyMetadata(0d));


}



Create a second class as above to demonstrate the category Editor concept in design time Extensibility . My second class is as follows




public class MyTextBox : TextBox
{
public double MyWidth
{
get { return (double)GetValue(MyWidthProperty); }
set { SetValue(MyWidthProperty, value); }
}

// Using a DependencyProperty as the backing store for MyWidth. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyWidthProperty =
DependencyProperty.Register("MyWidth", typeof(double), typeof(MyTextBox), new UIPropertyMetadata(20d));
}



Create the Design time project for the control



Create a class library project named MyControls.design and add reference to the following assemblies



Microsoft.Windows.Design.dll



MyControls.dll



Create a class which inherit from the IRegisterMetadata interface and write necessary code to update the metadatstore (a container of custom design-time attributes).




public class DesignMetaDataMain : IRegisterMetadata
{
#region IRegisterMetadata Members
public void Register()
{
AttributeTableBuilder builder = new AttributeTableBuilder();
new MyButtonMetadata().AddMetadata(builder);
new MyTextBoxMetadata().AddMetadata(builder);
MetadataStore.AddAttributeTable(builder.CreateTable());
}
#endregion
}




public class MyButtonMetadata
{
public void AddMetadata(AttributeTableBuilder tableBuilder)
{
//Property Editor
tableBuilder.AddCustomAttributes(
typeof(MyButton),
MyButton.MyHeightProperty,
new MyButtonCategoryAttribute(),
new EditorAttribute(typeof(TextExtendedEditor),
typeof(TextExtendedEditor)));
}
}

internal class MyButtonCategoryAttribute : CategoryAttribute
{
protected override string GetLocalizedString(string value)
{
return "MyButton";
}
}




public class MyTextBoxMetadata
{
public void AddMetadata(AttributeTableBuilder tableBuilder)
{
tableBuilder.AddCustomAttributes(typeof(MyTextBox), MyTextBox.MyWidthProperty, new MyTextBoxCategoryAttribute());
tableBuilder.AddCustomAttributes(typeof(MyTextBox), new EditorAttribute(typeof(MyTextBoxCategoryEditor), typeof(MyTextBoxCategoryEditor)));
}
}
internal class MyTextBoxCategoryAttribute : CategoryAttribute
{
protected override string GetLocalizedString(string value)
{
return "MyTextBox";
}
}



Category Editor definition




public class MyTextBoxCategoryEditor : CategoryEditor
{

public override bool ConsumesProperty(PropertyEntry property)
{
return true;
}

public override System.Windows.DataTemplate EditorTemplate
{
get
{
try
{
Resources myresourcedictionary = new Resources();
return myresourcedictionary["myTextBoxEditorTemplate"] as DataTemplate;

}
catch (Exception ex)
{

MessageBox.Show(ex.Message);
}
return null;
}
}

public override object GetImage(System.Windows.Size desiredSize)
{
return null;
}

public override string TargetCategory
{
get
{
return "MyTextBox";
}
}
}



Property Editor definition




public class TextExtendedEditor : ExtendedPropertyValueEditor
{
private Resources res = new Resources();

public TextExtendedEditor()
{
this.InlineEditorTemplate = res["myButtonEditorTemplate"] as DataTemplate;
}

}



Resource Dictionary code




<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:PropertyEditing="clr-namespace:Microsoft.Windows.Design.PropertyEditing;assembly=Microsoft.Windows.Design"
x:Class="MyControls.Design.Resources" >
<DataTemplate x:Key="myButtonEditorTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
Content="{Binding StringValue}" Click="Button_Click"/>
<TextBox Text="{Binding StringValue}"
Grid.Column="1" />
<!--<PropertyEditing:EditModeSwitchButton Grid.Column="1" />-->
</Grid>
</DataTemplate>
<DataTemplate x:Key="myTextBoxEditorTemplate">
<Expander>
<ItemsControl ItemsSource="{Binding Path=Properties}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Left"
Width="0.4*"
MinWidth="100"
MaxWidth="180" />
<ColumnDefinition SharedSizeGroup="Middle"
Width="0.6*" />
<ColumnDefinition SharedSizeGroup="Right"
Width="12" />
</Grid.ColumnDefinitions>
<PropertyEditing:PropertyContainer Grid.ColumnSpan="3"
PropertyEntry="{Binding}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Expander>
</DataTemplate>
</ResourceDictionary>



Build this project and copy the assembly to the bin\debug\design folder of the MyControls project. The design folder will not be there by default..you need to create a folder with that name. For deployment scenarios you must create a design folder on the location of the mycontrols.dll and place the design dll there. 



Consume the control in a project



Create a WPF application and use the two controls created in project MyControls




<Window x:Class="WPFHost.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyControls;assembly=MyControls"
Title="Window1" Height="300" Width="300">
<StackPanel>
<local:MyButton MyHeight="100" Content="MyButton"/>
<local:MyTextBox MyWidth="200"
Text="MyTextBox"
/>
</StackPanel>
</Window>



You are done and you should be able to see a custom property editor in case of the myTextBox control and a custom property editor for myButton as shown below(Note that the dependency property we added for the controls are shown in a separate bucket in the property pages


Design time in Cider











Design time in Blend