Blog

posts tagged with framework

Integrate NDoc HTML Help in VS.NET

0 Comments
By Fons Sonnemans, 30-dec-2003

Download NDocSample.zip

Introduction

NDoc 1.2 generates class libraries documentation from .NET assemblies and the XML documentation files generated by the C# compiler. NDoc uses add-on documenters to generate documentation in several different formats, including the MSDN-style HTML Help format (.chm), the Visual Studio .NET Help format (HTML Help 2), and MSDN-online style web pages.

This article explains the four steps to take in order to integrate the HTML Help 2 file you generated into Visual Studio.NET.

  1. Comment the Library using XML documentation tags
  2. Create HTML Help 2 using NDoc
  3. Registering the Help File using H2Reg
  4. Include Help Collection to VSCC

Step 1: Comment the Library using XML documentation tags

I have created a sample library with two classes Employee and EmployeeCollection. These classes are documented using the XML documentation tags (///). The assembly is named ReflectionIT.NDocSample.

using System;

namespace ReflectionIT.NDocSample
{
    /// <summary>
    /// Employee class used to demonstrate NDoc and Visual Studio.NET integration.
    /// </summary>
    /// <example>
    /// Some sample:
    /// <code>
    /// Employee emp = new Employee("Jim", 4000);
    /// emp.RaiseSalary();
    /// Console.WriteLine(emp.GetYearSalary);</code>
    /// </example>
    public class Employee
    {
        privatestring_name;
        privateint_salary;

        /// <summary>
        /// Initializes a new instance of the Employee class with a name and salary.
        /// </summary>
        /// <param name="name">The name of the employee</param>
        /// <param name="salary">The salary of the employee</param>
        public Employee(stringname, intsalary)
        {
            this.Name = name;
            this.Salary = salary;
        }

        /// <summary>
        /// Gets or sets the name of the employee.
        /// </summary>
        publicstring Name {
            get{return this._name;}
            set{this._name= value;}
        }

        /// <summary>
        /// Gets or sets the salary of the employee.
        /// </summary>
        publicint Salary {
            get{return this._salary;}
            set{this._salary= value;}
        }

        /// <summary>
        /// Returns the year salary for the employee using 12 months
        /// </summary>
        /// <returns>The year salary</returns>
        publicvirtualint GetYearSalary() {
            return Salary *12;
        }

        /// <summary>
        /// Raise the salary with 10%
        /// </summary>
        publicvirtualvoid RaiseSalary() {
            Salary += Salary *10/100;
        }
    }
}

The assembly is named ReflectionIT.NDocSample. I have set the name of the xml file in which the documentation comments are processed to ReflectionIT.NDocSample.xml. Make sure this is the same name as the assembly otherwise IntelliSense will not work correctly. Don't forget to compile the assembly; this will create the xml file.

Step 2: Create HTML Help 2 using NDoc

NDoc comes with a GUI front end (NDocGUI.exe) and a console application (NDocConsole.exe). I have used the NDocGui to create a NDoc project for my NDocSample library. You can easily do this using the 'New from Visual Studio Solution...' menu option.


Next you have to set the Documentation Type to 'HtmlHelp2'. I have also changed some extra properties like: CopyrightHref, CopyrightText, HtmlHelpName and Title.

Then you are ready to build the help file. Before you can do this you have to install Microsoft Html Workshop and Microsoft Visual Studio .NET Help Integration Kit (VSHIK2003). You build the help file using the Documentation, Build menu option (Ctrl+Shift+B). The result is an ReflectionIT.NDocSample.chm help file which NDoc converted to compiled Html Help.

Step 3: Registering the Help File using H2Reg

I have used H2Reg to register the NDocSample help file. H2Reg.exe is a small utility (177K), that allows you to register MS Help 2.x Collections: Namespaces, Titles, Plug-ins, and Filters without using MSI (Microsoft Installer).

To use H2Ref you have to create an ini file in which you describe what you want to register. Below you see the H2Reg_cmd.ini file I used to register the NDocSample help file.


; - - - - - - - Register -r switch

[Reg_Namespace]
;<nsName>|<nsColfile>|<nsDesc>
ReflectionIT.NDocSample|ReflectionIT.NDocSample.HxC|ReflectionIT NDocSample

[Reg_Title]
;<nsName>|<TitleID>|<LangId>|<HxS_HelpFile>|<HxI_IndexFile>|<HxQ_QueryFile>|
;<HxR_AttrQueryFile>|<HxsMediaLoc>|<HxqMediaLoc>|<HxrMediaLoc>|<SampleMediaLoc>
ReflectionIT.NDocSample|ReflectionIT.NDocSample|1033|ReflectionIT.NDocSample.HxS|||||||

;------- UnRegister -u switch

[UnReg_Namespace]
;<nsName>
ReflectionIT.NDocSample

[UnReg_Title]
;<nsName>|<TitleID>|<LangId>
ReflectionIT.NDocSample|ReflectionIT.NDocSample|1033


I have placed this ini file together with the ReflectionIT.NDocSample.HxC and ReflectionIT.NDocSample.HxC files into a folder called Help. In this folder you have to start H2Reg with the -R switch which I have automated using the following register.bat batch file.

copy . .\Ndoc\doc\*.hxS
copy..\Ndoc\doc\*.hxC
"C:\Program Files\Helpware\H2Reg\H2Reg.exe" -r cmdfile="C:\My Documents\Visual Studio Projects\NDocSample\Help\H2Reg_cmd.ini"

Step 4: Include Help Collection to VSCC

The last step is to include the help collection in Visual Studio .NET 2003 Combined Help Collection (VSCC). Therefore you have to open the 'Visual Studio .NET Combined Help Collection Manager' page using the following hyperlink: ms-help://MS.VSCC.2003/VSCCCommon/cm/CollectionManager.htm. In this page you have to select the 'ReflectionIT.NDocSample' checkbox and click the 'Update VSCC' button.


You can change the Title attribute of HelpCollection node in the .HxC file when you want a more descriptive name.

Result

The result off all these steps is great. You will see the help file integrated into Visual Studio.NET. It is context sensitive (F1) and will also appear in the Dynamic Help.



Conclusion

The Xml documentation comments of C# are great. With NDoc and H2Reg you can generate an Html Help 2 file and integrate it in Visual Studio.NET. This will make your code libraries really (re)usable.

I haven't figured out how to add Filters and Contents (TOC). I hope to update this article soon with these options. When you find this all to difficult you should look at DocumentX which is a lot easier to use, but not for free.

Any suggestions and feedback for improving this Add-In is most welcome. Send your suggestions and feedback to Fons.Sonnemans@reflectionit.nl

READ MORE

.NET Quick Reference Card

0 Comments
By Fons Sonnemans, 07-nov-2003

Download DotNetRefCard.pdf

I have created a small quick reference card for Microsoft .NET with some hard to remember details. My printer supports two pages per side and double sided printing. This makes it possible to print the 4 pages on a single sheet which makes it easier to use.

Contents:

  • C# String escape sequence
  • C# Numeric & Char Literals
  • String Formatting
  • Standard Numeric Format Strings
  • Custom Numeric Format Strings
  • Standard DateTime Format Strings
  • Custom DateTime Format Strings
  • Regular Expressions

I hope you will find it useful.

Any suggestions and feedback for improving this reference card is most welcome. Send your suggestions and feedback to Fons.Sonnemans@reflectionit.nl

READ MORE

LocalizedTextProvider Component

1 Comments
By Fons Sonnemans, 10-aug-2003

Download LocalizedText.zip

Introduction

I have read that the .NET framework has great features to localize your application. I had to use them recently to create an English, Dutch, French and German "version" of one of my applications. I found out they worked well but it was very time consuming to set all the text properties of all controls for each language. Even worse you have to translate common terms like: OK, Cancel, New, Open, Close, etc. on every form if you want to use the designer. You can, of course, write code to fetch them all from one resource file. But I don't like to write code, especially if you don't have to.

I came up with a solution for this problem, called the LocalizedTextProvider component. This component is actually an extender which, adds a LocalizedText property to a Control or MenuItem. When you add this component to a WinForm it will allow you to set the LocalizedText property ('Misc' category) of all controls on your form. You can select a Key from a predefined list. This list contains all keys from the string resources in the component. I have filled the list using the International Word List from the 'Microsoft Official Guidelines for User Interface Developers and Designers' website.



Example: the LocalizedText property for the Cancel button is set to Microsoft.Cancel

The Text for the Cancel button will automatically be translated to 'Annuleren' for Dutch users, 'Abbrechen' for German users and 'Annuler' for French users. You can test this by setting the CurrentUICulture property of the CurrentThread to a Dutch, German or French CultureInfo.

[STAThread]
staticvoid Main()
{
    Thread.CurrentThread.CurrentUICulture =new System.Globalization.CultureInfo("nl");
    //Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("fr");
    //Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("de");

    Application.Run(new Form1());
}

LocalizedTextProvider class

The LocalizedTextProvider class is derived from System.ComponentModel.Component and implements the System.ComponentModel.IExtenderProvider interface. This makes it an extender. The ProvideProperty attribute on the class specifies the name of the property (LocalizedText) that it offers to other components. The CanExtend method specifies whether this object can provide its extender properties to the specified object. In this case a Control or MenuItem. The GetLocalizedText() and SetLocalizedText() methods retrieve and store the resource key into a Hashtable. The UpdateText() method uses this key to retrieve the string from the resource file.

[ProvideProperty("LocalizedText", typeof(Component))]
publicclass LocalizedTextProvider : System.ComponentModel.Component, System.ComponentModel.IExtenderProvider, ISupportInitialize {
    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.Container components=null;

    private Hashtable _localizedTextList=new Hashtable();
    internal conststring ResourceKey ="ReflectionIT.Windows.Forms.Resources.Microsoft";
    internal readonly ResourceManager ResourceManager =new ResourceManager( ResourceKey, System.Reflection.Assembly.GetExecutingAssembly());
    internal conststring DefaultText ="(None)";

    /// <summary>
    /// Required for Windows.Forms Class Composition Designer support
    /// </summary>
    public LocalizedTextProvider(System.ComponentModel.IContainer container){
        container.Add(this);
        InitializeComponent();
    }

    /// <summary>
    /// Required for Windows.Forms Class Composition Designer support
    /// </summary>
    public LocalizedTextProvider() {
        InitializeComponent();
    }

    #region Component Designer generated code
    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent(){
        components=new System.ComponentModel.Container();
    }
    #endregion

    /// <summary>
    /// Specifies whether this object can provide its extender properties to the specified object.
    /// </summary>
    /// <param name="target">The Object to receive the extender properties.</param>
    /// <returns>true if this object can provide extender properties to the specified object; otherwise, false</returns>
    bool IExtenderProvider.CanExtend(objecttarget) {
        return(target is Control)|(target is MenuItem);
    }

    /// <summary>
    /// Get the key of the LocalizedText for the given component
    /// </summary>
    /// <param name="component">Control or MenuItem</param>
    /// <returns>Key of the LocalizedText</returns>
    [Category("Misc")]
    [Editor(typeof(LocalizedTextEditor), typeof(System.Drawing.Design.UITypeEditor))]
    [DefaultValue(LocalizedTextProvider.DefaultText)]
    [Description("The LocalizedText for this control")]
    publicstring GetLocalizedText(Component component){
        if(_localizedTextList.ContainsKey(component)) {
            if(_localizedTextList[component]!= null){
                return(string)(_localizedTextList[component]);
            }
        }

        return LocalizedTextProvider.DefaultText;
    }
    
    /// <summary>
    /// Set the LocalizedText for the given component
    /// </summary>
    /// <param name="component">Control or MenuItem</param>
    /// <param name="value">Key of the LocalizedText</param>
    public void SetLocalizedText(Component component,stringvalue) {
        _localizedTextList[component]=value;

        if(value !=null&&value!= LocalizedTextProvider.DefaultText){
            UpdateText(component);
        }        
    }

    /// <summary>
    /// Update the text for the component
    /// </summary>
    /// <param name="component">Control or MenuItem</param>
    private void UpdateText(Component component){
        string s = GetLocalizedText(component);

        if(s !=null&& s.Length > 0){
            if(componentis Control) {
                ((Control)component).Text = ResourceManager.GetString(s);
            }else{
                if(componentis MenuItem){
                    ((MenuItem)component).Text = ResourceManager.GetString(s);
                }
            }
        }
    }

    #region Implementation of ISupportInitialize

    publicvoid BeginInit(){
        // ignore
    }

    /// <summary>
    /// Update the Text for the all components, overwrites the manually set Text
    /// </summary>
    public void EndInit(){
        foreach(Component componentin_localizedTextList.Keys) {
            UpdateText(component);
        }
    }

    #endregion

}

The GetLocalizedText() method has a Editor attribute which specifies the LocalizedTextEditor used to change a property. The LocalizedTextEditor class is derived from PeterBlum.UITypeEditorClasses.BaseDropDownListTypeEditor. This base class makes it very easy to create your own editor. You only have to override the method FillInList() and fill the listbox. I have downloaded it from Peter's website, thanks Peter.

/// <summary>
/// TypeEditor for LocalizedText used by the LocalizedTextProvider
/// </summary>
public class LocalizedTextEditor : PeterBlum.UITypeEditorClasses.BaseDropDownListTypeEditor {

    privatestatic ArrayList _keys;

    /// <summary>
    /// Static Constructor: Create a list of all Keys from the ResourceFile
    /// </summary>
    static LocalizedTextEditor() {
        _keys=new ArrayList();
        Assembly a = Assembly.GetExecutingAssembly();
        System.IO.Stream rs = a.GetManifestResourceStream(LocalizedTextProvider.ResourceKey +".resources");
        if(rs !=null){
            ResourceReader rr= new ResourceReader(rs);

            foreach(DictionaryEntry d inrr) {
                if(d.Value isstring){
                    _keys.Add((string)d.Key);
                }
            }
        }
    }

    /// <summary>
    /// Add all keys to the ListBox
    /// </summary>
    /// <param name="pContext"></param>
    /// <param name="pProvider"></param>
    /// <param name="pListBox"></param>
    protected overridevoid FillInList(ITypeDescriptorContext pContext, IServiceProvider pProvider, ListBox pListBox){
        pListBox.Items.Add(LocalizedTextProvider.DefaultText);
        foreach(string keyin_keys){
            pListBox.Items.Add(key);
        }
        pListBox.Sorted = true;
    }

}

Conclusion

Localization isn't hard but it is a lot of work. I hope my LocalizedTextProvider component makes it easier for you. You will probably have to extend the resource files with extra keys and or languages. Have a try.

Any suggestions and feedback for improving this article is most welcome. Send your suggestions and feedback to Fons.Sonnemans@reflectionit.nl


READ MORE

Assembly Comparison Tool (WinChurn)

33 Comments
By Fons Sonnemans, 11-jul-2003

I have found a great tool on the GotDotNet user samples website

WinChurn allows you to compare two versions of the same managed assembly. This is extremely helpful when tracking changes in your assembly. The tool comes with a help system, as well as some examples to get you started. Optionally, the tool allows you to generate a policy file, however caution is urged when using this feature. While the tool is extremely robust and accurate at detecting public API changes, it cannot detect internal changes. The tool uses Reflection to generate the results.

Assembly Comparison Tool (WinChurn)

READ MORE

C# InputBox

1 Comments
By Fons Sonnemans, 30-apr-2003

Download InputBoxSample.zip

Introduction

Visual Basic 6.0 has an InputBox() function, Visual Basic.NET has one but in C# you don't. You can solve this easily by adding a reference to 'Microsoft.VisualBasic.dll' and use the static method Microsoft.VisualBasic.Interaction.InputBox().

The VB implementation has some shortcomings which I solved in my improved InputBox class. You never know whether the user entered a empty text or clicked the Cancel button. It is also impossible to have validation on the text.


Example: InputBox

InputBox class

The InputBox class is a public class with a private constructor. You use it by calling the static Show method. This method instantiates a new instance of the class, sets it's properties and returns the result. The result is not a string but a InputBoxResult object. This object has two properties: OK and Text. OK is a boolean indicating that the user clicked the OK button and not the Cancel button. The Text contains the string the user entered.

public static InputBoxResult Show(string prompt,stringtitle, stringdefaultResponse,
                                 InputBoxValidatingHandler validator,intxpos,intypos){
    using (InputBox form=new InputBox()) {
        form.labelPrompt.Text =prompt;
        form.Text = title;
        form.textBoxText.Text =defaultResponse;
        if(xpos >=0 &&ypos >=0){
            form.StartPosition = FormStartPosition.Manual;
            form.Left = xpos;
            form.Top = ypos;
        }
        form.Validator = validator;

        DialogResult result=form.ShowDialog();

        InputBoxResult retval=new InputBoxResult();
        if(result == DialogResult.OK){
            retval.Text = form.textBoxText.Text;
            retval.OK = true;
        }
        returnretval;
    }
}

publicstatic InputBoxResult Show(string prompt,stringtitle, stringdefaultText,
                                 InputBoxValidatingHandler validator){
    return Show(prompt, title,defaultText,validator,-1,-1);
}

Usage

You activate the InputBox by calling the static Show() method. It has 4 required and 2 optional arguments (using overloading).

private void buttonTest_Click ( object sender , System.EventArgs e) {
    InputBoxResult result= InputBox.Show("Test prompt:","Some title","Default text", null);
    if(result.OK){
        textBox1.Text = result.Text;
    }
}


Validation

You can add validation logic using the validator argument. The validator is a InputBoxValidatingHandler delegate which you can use to validate the Text. The following sample checks whether the Text is not empty. If so it sets Cancel to true and the Message to 'Required'.

private void buttonTest_Click ( object sender , System.EventArgs e) {
    InputBoxResult result= InputBox.Show("Test prompt:","Some title","Default text",
                                             new InputBoxValidatingHandler(inputBox_Validating));
    if(result.OK){
        textBox1.Text = result.Text;
    }
}

privatevoid inputBox_Validating(objectsender, InputBoxValidatingArgs e){
    if(e.Text.Trim().Length ==0){
        e.Cancel =true;
        e.Message ="Required";
    }
}


Example: Required text

Conclusion

The ImputBox class is just a simple class which you can use in your Windows Forms application to prompt for a text. It can also be used from Visual Basic.NET when you compile it in a Library and reference this Library from you VB project.

Any suggestions and feedback for improving this article is most welcome. Send your suggestions and feedback to Fons.Sonnemans@reflectionit.nl

READ MORE

Free VS.NET Add-In: Spelly.NET

0 Comments
By Fons Sonnemans, 01-apr-2003

Spelly is a Visual Studio addin that allows you to easily spell-check source code and/or comments. Spelly is smart enough to understand identifiers with under_scores and MixedCase.

Spelly is available for both Visual Studio .NET and Visual C++ 5/6.

READ MORE

Reliably and quickly improve your C# code with Xtreme Simplicity's C# Refactory

0 Comments
By Fons Sonnemans, 04-feb-2003

I have found a promising C# tool which fully integrates with Visual Studio.NET. I'm going to test it soon (I hope).

Have a look yourself at http://www.xtreme-simplicity.net

READ MORE

Save your UserSettings in an Isolated Store

0 Comments
By Fons Sonnemans, 01-feb-2003

Download UserSettings.zip

Introduction

I like applications who remember my settings the next time I use it. This is an easy feature which is often forgotten. Most of the time because it is quite some work. With this article I want to help you with this and saving you a lot off work.

Then there is always the question 'where do we store the settings?'. I see a lot of applications using the Registry, ini-files or xml-files. All these solutions are causing a security risk. Especially when you want your application to be downloaded from the web using the 'no touch' deployment features of .NET.

Microsoft has solved this problem for me by introducing an isolated stores. With these stores, you can read and write data that less trusted code cannot access and prevent the exposure of sensitive information that can be saved elsewhere on the file system. Data is stored in compartments that are isolated by the current user and by the assembly in which the code exists.

The downloadable zipfile contains a UserSettingLibrary which can be used to store user settings into an isolated store.

IsolatedUserSettings class

The IsolatedUserSettings class can be used to store and retrieve user settings in and from an IsolatedStorageFile. The RegisterForm() method can be used to register a form. The location, size and windowstate of the form will be saved and restored. The SaveSetting() method saves an serializable object which can be restored using the GetSetting() method.


using System;
using System.IO;
using System.IO.IsolatedStorage;
using System.Collections;
using System.Windows.Forms;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Reflection;

namespace ReflectionIT.UserSettings
{
    /// <summary>
    /// The IsolatedUserSettings class can be used to store and retrieve user settings
    /// in and from an IsolatedStorageFile.
    /// </summary>
    public class IsolatedUserSettings : IDisposable
    {
        private Hashtable _settings;
        privatebool_closed =false;
        privatestring_fileName;

        /// <summary>
        /// Constructor
        /// </summary>
        public IsolatedUserSettings(stringfileName)
        {
            _fileName=fileName;
            IsolatedStorageFile isoFile= GetStore();
            string[] files=isoFile.GetFileNames(fileName);
            if(files.Length == 1){
                // file exists -> Deserialize
                using(IsolatedStorageFileStream stream= new IsolatedStorageFileStream(
                         fileName, FileMode.Open, isoFile)){
                    BinaryFormatter formatter=new BinaryFormatter();
                    try{
                        _settings= (Hashtable)formatter.Deserialize(stream);
                    }
                    catch(SerializationException){
                        _settings= new Hashtable();
                    }
                }
            }else{
                // start fresh
                _settings= new Hashtable();
            }
        }

        /// <summary>
        /// ND Destructor
        /// </summary>
        ~IsolatedUserSettings() {
            Dispose(false);
        }

        /// <summary>
        /// Close the IsolatedUserSettings
        /// </summary>
        publicvoid Dispose(){
            Dispose(true);
            System.GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Close the IsolatedUserSettings
        /// </summary>
        /// <param key="disposing"></param>
        protectedvirtualvoid Dispose(bool disposing){
            this.Close();
        }

        /// <summary>
        /// Write the UserSettings to the IsolatedStorage
        /// </summary>
        publicvirtualvoid Close() {
            if(!_closed){
                IsolatedStorageFile isoFile= GetStore();

                using(IsolatedStorageFileStream stream= new IsolatedStorageFileStream(
                         FileName, FileMode.Create,isoFile)){
                    BinaryFormatter formatter=new BinaryFormatter();
                    formatter.Serialize(stream,_settings);
                }
                _closed= true;
            }
        }

        /// <summary>
        /// Get the fileName
        /// </summary>
        publicstring FileName {
            get{return _fileName;}
        }

        /// <summary>
        /// Return the Store
        /// </summary>
        /// <returns></returns>
        private IsolatedStorageFile GetStore(){
            return IsolatedStorageFile.GetStore(IsolatedStorageScope.Assembly |
                IsolatedStorageScope.User | IsolatedStorageScope.Domain |
                IsolatedStorageScope.Roaming, null,null);
        }

        /// <summary>
        /// Register a Form
        /// </summary>
        /// <param key="key"></param>
        /// <param key="form"></param>
        publicvirtualvoid RegisterForm(string key, Form form){
            FormSetting f;
            if(_settings.ContainsKey(key)) {
                f =(FormSetting)_settings[key];
                f.Restore(form);
            }else{
                f =new FormSetting(key);
                f.Backup(form);
                _settings.Add(key, f);
            }
            form.SizeChanged +=new EventHandler(f.FormMovedOrResized);
            form.LocationChanged +=new EventHandler(f.FormMovedOrResized);
        }

        /// <summary>
        /// UnRegister a Form
        /// </summary>
        /// <param key="key">Name of the Form. A forms with an existing key will
        /// automatically get the stored settings.</param>
        /// <param key="form"></param>
        publicvirtualvoid UnRegisterForm(string key, Form form){
            if(_settings.ContainsKey(key)) {
                FormSetting f =(FormSetting)_settings[key];
                f.Backup(form);
                form.SizeChanged -= new EventHandler(f.FormMovedOrResized);
                form.LocationChanged -= new EventHandler(f.FormMovedOrResized);
            }
        }

        /// <summary>
        /// Save a setting with a given key
        /// </summary>
        /// <param key="key">Name of the setting by which it is saved an can be
        /// restored using GetSetting().</param>
        /// <param key="setting"></param>
        publicvoid SaveSetting(stringkey,objectsetting){

            // Check Serializable attribute
            TypeAttributes attributes=setting.GetType().Attributes;
            if((attributes& TypeAttributes.Serializable)==0) {
                thrownew ArgumentException("Setting argument must be marked Serializable.");
            }

            // Add or Overwrite setting
            if(_settings.ContainsKey(key)) {
                _settings[key]= setting;
            }else{
                _settings.Add(key, setting);
            }
        }

        /// <summary>
        /// Return the setting with the given key
        /// </summary>
        /// <param key="key"></param>
        /// <returns>
        /// The setting object or null when the the given key does not exists
        /// </returns>
        publicobject GetSetting(stringkey){
            return GetSetting(key,null);
        }

        /// <summary>
        /// Return the setting with the given key
        /// </summary>
        /// <param key="key"></param>
        /// <returns></returns>
        publicobject GetSetting(stringkey,objectdefaultValue){
            if(_settings.ContainsKey(key)) {
                return_settings[key];
            }
            returndefaultValue;
        }
    }
}

The FormSetting class is used to store a form's location, size and windowstate in a serializable object. The next time you register a form with the same key the location, size and windowstate is restored to the saved values. So if you resize your form, close it and open it again the size will be the same as it was when you last closed it.

using System;
using System.Drawing;
using System.Windows.Forms;

namespace ReflectionIT.UserSettings
{
    /// <summary>
    /// Summary description for FormSetting.
    /// </summary>
    [Serializable]
    publicclass FormSetting
    {
        private Point _location;
        privatestring_name;
        private Size _size;
        private FormWindowState _windowState;

        public FormSetting(stringname)
        {
            this._name =name;
        }

        /// <summary>
        /// Get the name used for this FormSetting
        /// </summary>
        publicstring Name {
            get{return (this._name);}
        }

        /// <summary>
        /// Backup the Size, Location and WindowState (only when not Minimized)
        /// </summary>
        /// <param name="form"></param>
        publicvirtualvoid Backup(Form form){
            if(form.WindowState != FormWindowState.Minimized) {
                // Only when not Maximized
                if(form.WindowState != FormWindowState.Maximized){
                    this._location=form.Location;
                    this._size=form.Size;
                }
                this._windowState=form.WindowState;
            }
        }

        /// <summary>
        /// Eventhandler
        /// </summary>
        /// <param name="sender">Form which is Moved or Resized</param>
        /// <param name="e"></param>
        publicvirtualvoid FormMovedOrResized(object sender, EventArgs e){
            Backup((Form)sender);
        }

        /// <summary>
        /// Restore the Size, Location and WindowState for the given form
        /// </summary>
        /// <param name="form"></param>
        publicvirtualvoid Restore(Form form){
            form.Location = this._location;
            form.Size = this._size;
            form.WindowState =this._windowState;
        }
    }
}

Sample

The included sample uses the IsolatedUserSettings class to store and restore the user settings. In this case the Form (Location, Size and WindowState), the Checked property of a CheckBox and a MostRecentlyUsedList object. The settings are restored in the constructor and saved in the Closing event handler, the Form properties are saved automatically by the internally used FormSetting class. You must set the StartPosition property of the form to 'Manual', otherwise the location will not be restored.

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using ReflectionIT.UserSettings;

namespace Test
{
    /// <summary>
    /// Summary description for Form1.
    /// </summary>
    public class Form1: System.Windows.Forms.Form
    {
        private System.Windows.Forms.GroupBox groupBox1;
        private System.Windows.Forms.Button buttonMRUAdd;
        private System.Windows.Forms.TextBox textBoxMRU;
        private System.Windows.Forms.ListBox listBoxMRU;

        private MostRecentlyUsedList _mru;
        private IsolatedUserSettings _settings =new IsolatedUserSettings("settings.dat");
        private System.Windows.Forms.CheckBox checkBox1;

        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.Container components=null;

        public Form1()
        {
            //
            // Required for Windows Form Designer support
            //
            InitializeComponent();

            // Get UserSettings
            _settings.RegisterForm(this.Name,this);
            _mru=(MostRecentlyUsedList)_settings.GetSetting("mru",new MostRecentlyUsedList(5));
            checkBox1.Checked = (bool)_settings.GetSetting("checked",true);
            listBoxMRU.DataSource = _mru;
        }

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        protectedoverridevoid Dispose(bool disposing)
        {
            if(disposing )
            {
                if(components!= null)
                {
                    components.Dispose();
                }
            }
            base.Dispose( disposing);
        }

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        staticvoid Main()
        {
            Application.Run(new Form1());
        }

        /// <summary>
        /// Add the Text to the MRU and bind it to the datasource
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        privatevoidbuttonMRUAdd_Click(object sender, System.EventArgs e){
            _mru.Add(textBoxMRU.Text);
            textBoxMRU.Clear();
            textBoxMRU.Focus();

            listBoxMRU.DataSource = null;// force update
            listBoxMRU.DataSource =_mru;
        }

        /// <summary>
        /// Save the MRU and checkBox1.Checked to the UserSettings
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        privatevoid Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e){
            _settings.SaveSetting("mru",_mru);
            _settings.SaveSetting("checked",checkBox1.Checked);
            _settings.Close();
        }
    }
}

The MostRecentlyUsedList class has a Serializable attribute which makes it possible to store it in an IsolatedUserSetting.

 

using System;
using System.Collections;

namespace Test
{
   &nbsp/// <summary>
   &nbsp/// Summary description for MostRecentlyUsedList.
   &nbsp/// </summary>
   &nbsp[Serializable]
   &nbsppublicclass MostRecentlyUsedList : IList
   &nbsp{
   &nbsp   &nbspprivateint_capacity;
   &nbsp   &nbspprivate ArrayList _list;

   &nbsp   &nbsp/// <summary>
   &nbsp   &nbsp/// Initializes a new instance of the MostRecentlyUsedList class
   &nbsp   &nbsp/// that is empty and has the specified capacity
   &nbsp   &nbsp/// </summary>
   &nbsp   &nbsp/// <param name="capacity">The number of elements that the new
   &nbsp   &nbsp/// MostRecentlyUsedList is capable of storing. </param>
   &nbsp   &nbsppublic MostRecentlyUsedList(intcapacity)
   &nbsp   &nbsp{
   &nbsp   &nbsp   &nbsp_capacity=capacity;
   &nbsp   &nbsp   &nbsp_list=new ArrayList(capacity);
   &nbsp   &nbsp}

   &nbsp   &nbsp/// <summary>
   &nbsp   &nbsp/// Add a object to the beginning of the list. If the value already
   &nbsp   &nbsp/// exists it is removed first. The length of the list will never
   &nbsp   &nbsp/// exceed the capacity.
   &nbsp   &nbsp/// </summary>
   &nbsp   &nbsp/// <param name="value">The Object to be added to the beginning
   &nbsp   &nbsp/// of the MostRecentlyUsedList</param>
   &nbsp   &nbsppublicvoid Add(objectvalue)
   &nbsp   &nbsp{
   &nbsp   &nbsp   &nbspif(_list.Contains(value)){
   &nbsp   &nbsp   &nbsp   &nbsp_list.Remove(value);
   &nbsp   &nbsp   &nbsp}else{
   &nbsp   &nbsp   &nbsp   &nbspif(this.Count ==this.Capacity){
   &nbsp   &nbsp   &nbsp   &nbsp   &nbsp_list.RemoveAt(this.Count -1);
   &nbsp   &nbsp   &nbsp   &nbsp}
   &nbsp   &nbsp   &nbsp}
   &nbsp   &nbsp   &nbsp_list.Insert(0,value);
   &nbsp   &nbsp}

   &nbsp   &nbsp/// <summary>
   &nbsp   &nbsp/// Get or sets the number of elements that the MostRecentlyUsedList
   &nbsp   &nbsp/// is capable of storing.
   &nbsp   &nbsp/// </summary>
   &nbsp   &nbsppublicint Capacity {
   &nbsp   &nbsp   &nbspget{return (this._capacity);   &nbsp}
   &nbsp   &nbsp   &nbspset{
   &nbsp   &nbsp   &nbsp   &nbspthis._capacity=value;
   &nbsp   &nbsp   &nbsp   &nbspwhile(this.Count >=this.Capacity){
   &nbsp   &nbsp   &nbsp   &nbsp   &nbsp_list.RemoveAt(this.Count -1);
   &nbsp   &nbsp   &nbsp   &nbsp}
   &nbsp   &nbsp   &nbsp}
   &nbsp   &nbsp}
   &nbsp   &nbsp....

Conclusion

The IsolatedUserSettings is an simple class which uses an isolated store to serialize and deserialize objects. With it you can enhance your applications without much effort.

Any suggestions and feedback for improving this article is most welcome. Send your suggestions and feedback to Fons.Sonnemans@reflectionit.nl

READ MORE

WinForms AcceptButton Extender

0 Comments
By Fons Sonnemans, 01-dec-2002

Download AcceptButton.zip

Introduction

The System.Windows.Forms.Form class has an AcceptButton property which can be used to set the button on the form that is clicked when the user presses the ENTER key. The accept (default) button should be the button that represents the action that the user is most likely to perform if that action isn't potentially dangerous. This button has a dark border to let the user know that it is the accept button.

This feature works great only when you have one accept button. Have a look at the following diaglog.

The OK button is in this dialog the accept button. This is the correct behavior when the textbox 'Name' and datepicker 'Date' have the focus. There should not be an accept button when the (multiline) textbox 'Description' has the focus. When listbox 'Avialiable' has the focus the '> Add >' button must be the accept button. And when listbox 'Assigned' has the focus the '> Add >' button must be the accept button.

This can be accomplished by implementing the following Enter and Leave event handlers.


private void textBoxDescription_Enter ( object sender , System.EventArgs e) {
    this.AcceptButton =null;
}

privatevoid textBoxDescription_Leave(objectsender, System.EventArgs e) {
    this.AcceptButton =buttonOK;
}

privatevoid listBoxAvailable_Enter(objectsender, System.EventArgs e) {
    this.AcceptButton =buttonAdd;
}

privatevoid listBoxAvailable_Leave(objectsender, System.EventArgs e) {
    this.AcceptButton =buttonOK;
}

privatevoid listBoxAssigned_Enter(objectsender, System.EventArgs e) {
    this.AcceptButton =buttonRemove;
}

privatevoid listBoxAssigned_Leave(objectsender, System.EventArgs e) {
    this.AcceptButton =buttonOK;
}

But why write code when the same can be accomplished by setting some properties in the designer. The AcceptButton extender class can be used to eliminate this code.

AcceptButton Extender Provider

An extender provider is a component that provides properties to other components. The AcceptButton class implements the IExtenderProvider interface making it an Extender Provider. When you add an AcceptButton control to a Form, all other controls (accept buttons) on the form have the UseDefaultAcceptButton and the AcceptButton property added to their list of properties.

You now only have to set the two properties for each control:

Control UseDefaultAcceptButton property AcceptButton property
textBoxName True (none)
textBoxDescription False (none)
dateTimePickerDate True (none)
listBoxAvailable False buttonAdd
listBoxAssigned False buttonRemove

Conclusion

The AcceptButton class is good example for which you can use Extender Providers. Extender Providers are somewhat strange to write but very powerful. I use them a lot.

Any suggestions and feedback for improving this article is most welcome. Send your suggestions and feedback to Fons.Sonnemans@reflectionit.nl

READ MORE

Janus System has released some great .NET controls

0 Comments
By Fons Sonnemans, 22-okt-2002

I have used the Janus GridEX control in many of my VB6 projects. It worked perfectly and it is very fast.

I have tested the .NET Trial version, it is even better. This is a component that everybody who is building WinForms application should buy.

Have a look at Janus GridEX Control for .NET and try it yourself.

READ MORE

All postings/content on this blog are provided "AS IS" with no warranties, and confer no rights. All entries in this blog are my opinion and don't necessarily reflect the opinion of my employer or sponsors. The content on this site is licensed under a Creative Commons Attribution By license.