Dailycode.info

Short solution for short problems

Creating a service for your class library

During development, I always use a small WPF client to test the program, when test are going ok I create the service.

This week I used this tutorial twice, it only took me 1 hour to set it up correctly and needly (with event logging etc).

http://msdn.microsoft.com/en-us/library/zt39148a(v=vs.80).aspx

Logging I do with the aidf of a simple extra class (code found at the end of this post)

EventLogWriter.Instance.WriteToLog("Service Stopped", EventLogEntryType.Information);

I use this simple singleton class to help with the event logging:

 

/// <summary>

    /// Class enabling writing to the eventlog

    /// </summary>

    public class EventLogWriter

    {

        private System.Diagnostics.EventLog eventWriter;

 

        #region Singleton Pattern

 

        // Singleton.

        private static EventLogWriter _instance;

 

        /// <summary>

        /// Singleton Pattern.

        /// </summary>

        public static EventLogWriter Instance

        {

            get

            {

                if (EventLogWriter._instance == null)

                {

                    EventLogWriter._instance = new EventLogWriter();

                }

 

                return EventLogWriter._instance;

            }

        }

 

        #endregion

 

        /// <summary>

        /// Constructor

        /// </summary>

        public EventLogWriter()

        {

            // 

            // init eventWriter

            // 

            eventWriter = new EventLog();

            eventWriter.Log = "Application";

            eventWriter.Source = "SFIAutoMeasurementService";

        }

 

        /// <summary>

        /// Writes an entry to the eventlog

        /// </summary>

        /// <param name="msg">Message to write</param>

        /// <param name="entryType">Eventlog entry type</param>

        public void WriteToLog(string msg, EventLogEntryType entryType)

        {

 

            try

            {

                if (System.Diagnostics.EventLog.SourceExists("SFIAutoMeasurementService"))

                {

                    eventWriter.WriteEntry(msg, entryType);

                }

            }

            catch { }

 

        }

    }

How to get a user controls parent form. (Windows forms)

I wanted to pass the owner form to all my ShowDialog calls. But lots of times I call this method from a user control. So I found a good solution on stackoverflow (it’s a recursive function that will loop over all parents and check if it’s a windows form): http://stackoverflow.com/questions/371464/get-components-parent-form

I changed it a little to fit my needs, also this project was VB.Net:

   

''' <summary>
    ''' Returns the parent System.Windows.form of the control
    ''' </summary>
    ''' <param name="parent"></param>
    ''' <returns>First parent form or null if no parent found</returns>
    ''' <remarks></remarks>
    Public Shared Function GetParentForm(ByVal parent As Control) As Form
        Dim form As Form = TryCast(parent, Form)
        If form IsNot Nothing Then
            Return form
        End If
        If parent IsNot Nothing Then
            ' Walk up the control hierarchy
            Return GetParentForm(parent.Parent)
        End If
        Return Nothing
        ' Control is not on a Form
    End Function

 

 

I just call this method new on all windows and user controls where I have a show dialog call:

 

frm.ShowDialog(GP_Global.GlobalFunctions.GetParentForm(Me))

 


FileDialog default settings.

 

I was using several ‘Open’ and ‘Save’ file dialogs on my screen, all for importing or exporting xml files. Instead if setting the default parameters for each dialog over again, I made this easy function that helps to set the default parameters:

    Private Shared Sub DialogInit(ByRef dialog As FileDialog)

        dialog.InitialDirectory = Environment.SpecialFolder.Desktop

        dialog.DefaultExt = "xml"

        dialog.Filter = "XML|*.xml"

    End Sub

 

Then all file dialogs call this method to set the default parameters and all dialogs act the same:

Using dialog As New OpenFileDialog

'dialog.InitialDirectory = Environment.SpecialFolder.Desktop 

           'dialog.DefaultExt = "xml"

           'dialog.Filter = "XML|*.xml"

           DialogInit(dialog)

           If dialog.ShowDialog() <> DialogResult.OK Then Return

           xmlFile = dialog.FileName

End Using

 

Another example:

Using dialog As New SaveFileDialog()

DialogInit(dialog)

           If dialog.ShowDialog() <> DialogResult.OK Then Return

           xmlFile = dialog.FileName

End Using

 


How to kill a process on an other machine?

Found a good solution that does the trick! It takes only 9 line of code!

private void btnKillProcess_Click(object sender, EventArgs e)

{

ManagementScope scope = new ManagementScope(@"\\MyPCName\root\cimv2");

scope.Connect();

ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_Process WHERE Name = process.exe'");

ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);

ManagementObjectCollection objectCollection = searcher.Get();

foreach(ManagementObject managementObject in objectCollection)

{

managementObject.InvokeMethod("Terminate", null);

}

}

Source: http://weblogs.asp.net/stanleygu/archive/2010/03/31/tip-13-kill-a-process-from-local-to-remote.aspx


Track changes without binding controls

When you are working with windows forms .net without binding your controls to a data object, you still need to check if a control is changed or not. For example if a user closes a form without saving it, you might want to tell the user this and ask him to save or not.

This post will show you a very simple implementation that will easy the development effort. To use this, you will need to create control wrappers for every control that you want to auto implement the track changes functionality.

Let’s begin with creating or modifying an interface that is implemented by all controls that need change tracking. I will add the property IsDirty to the interface. The interface is called: IControlBase

 

#region Properties

 

bool IsDirty { get; set; }

 

#endregion

 

Next we implement the interface in our controls and set the IsDirty flag whenever a value is changed. I will give 2 examples:

Textbox:

#region Implementation of IControlBase

 

public bool IsDirty

{

    get;

    set;

}

#endregion

protected override void OnKeyPress(KeyPressEventArgs e)

{

    IsDirty = true;

    base.OnKeyPress(e);

}

 

Combobox:

#region Implementation of IControlBase

 

public bool IsDirty

{

    get;

    set;

}

 

 

protected override void OnTextChanged(EventArgs e)

{

    IsDirty = true;

    base.OnTextChanged(e);

}

 

So this will make sure that the events we want will set the IsDirty flag. For the combobox I had the problem that the event was raised when I filled it, so I created a function that will clear all IsDirty flags after loading the form:

 

public static void ClearIsDirty(Control currentControl)

{

 

    foreach (Control isDirtyctrl in currentControl.Controls)

    {

        if (isDirtyctrl.Controls.Count > 0)

        {

            ClearIsDirty(isDirtyctrl);

        }

        if (isDirtyctrl.Parent as IControlBase != null)

        {

            ((IControlBase)isDirtyctrl.Parent).IsDirty = false;

        }

    }

}

 

After the page is loaded and all controls are filled, I simply run this function and pass the parent control to it. So it will clear itself and all child controls. The control could be a panel or the form itself:

GlobalFunctions.ClearIsDirty(tpGeneral);

 

Then the last piece of code needs to be written when the user leaves the form without saving. So the onleave of your control is captured and then we call a second function that will check all controls of the form on their IsDirty flag. Here is the function that does the trick, put this function somewhere in your general functions namespace.

private static bool _isDirty=false;

 

public static bool CheckIsDirty(Control currentControl)

{

 

    if ((_isDirty == true))

    {

        return _isDirty;

    }

 

    foreach (Control isDirtyctrl in currentControl.Controls)

    {

        if (isDirtyctrl.Controls.Count > 0)

        {

            CheckIsDirty(isDirtyctrl);

        }

        else

        {

            if (isDirtyctrl.Parent as IControlBase != null)

            {

                if ((((IControlBase)isDirtyctrl.Parent).IsDirty == true))

                {

                    _isDirty = true;

                    return _isDirty;

                }

            }

        }

    }

    return _isDirty;

}

 

Here is the implementation of this function (I check 2 tabpages on the IsDirtyFlag, if the user wants to save I save the project. At the end I clear the IsDirty flags):

private void tbcontProject_Leave(object sender, EventArgs e)

{

    if (GlobalFunctions.CheckIsDirty(tpGeneral) || GlobalFunctions.CheckIsDirty(tpInfo))

    {

        DialogResult res = MessageBox.Show("Information has been changed, do you want to save?", "Information changed", MessageBoxButtons.YesNo);

        if (res == DialogResult.Yes)

        {

            SaveProject();

        }

        GlobalFunctions.ClearIsDirty(tpGeneral);

        GlobalFunctions.ClearIsDirty(tpInfo);

    }

 


No validation with the "Cancel" button

I was having a hard time with some forms that had control validation occuring. Especially when the "Cancel" button was pressed, the validation still occured. I was looking for a property on the button called autovalidate or something, but I looked over the CausesValidation property. If you set this to false, the validation will be ignored! Thank god! Found this here: http://stackoverflow.com/questions/1882523/how-to-skip-validating-after-clicking-on-a-forms-cancel-button

 


How to rebind a Select on a datatable?

If you have for example a data grid that binds to a dataset or data table and you perform a Select(“”) on the table, it can happen that you lose your bindings and the row is shown but the values not:
  
The code I used was like this:

grdProjectOverview.DataSource = dt.Select("ProjectEndDate > '" + DateTime.Now.ToShortDateString() + "'");

The system finds the correct number of rows and displays them, but the values are not in there. I solved it by using a dataview like this:

grdProjectOverview.DataSource = new DataView(dt, "ProjectEndDate > '" + DateTime.Now.ToShortDateString() + "'", String.Empty, DataViewRowState.CurrentRows);

Now the binding works and the values are shown. It  can really be a pain to get this one solved. Unfortunately as always I do not have time to dig into this deeper. So just the quick solution:
   
 

Opening a window with ShowDialog in a window that is opened with ShowDialog.

I had a simple form that opened a search form. From this search form it was possible to open a sub search form. What happened was when you opened the sub search form, and found a value, selected it and returned to the (parent)search form, it ended up in the close handler of this form and closed it. After some searching around, Ii found out that the Dialog Result of the sub search form was effecting the dialog result of the search form. The solution I found was to explicitly set the dialog result of the search form to none after the sub search form returned its result.

For example: Lets say my search form is called from a main form using the ShowDialog. In this search form we call a second search form of type SearchSubForm.

Dim frmSubSearchForm As SearchSubForm = new SearchSubForm()

If frmSubSearchForm.ShowDialog = DialogResult.OK Then

'Do something 

End If

 

This code could generate the problem. So to solve this, you can set the dialog result of the search form to none like this:

Dim frmSubSearchForm As SearchSubForm = new SearchSubForm()

If frmSubSearchForm.ShowDialog = DialogResult.OK Then

       Me.DialogResult = DialogResult.None

'Do something 

End If

This solves the problem. I did not had the time to look into this problem in dept. It could be that it has something to do with the main page state or some other problem, but unfortunately I will not have time to investigate it.


Setting the property of a control from a seperate thread using an extension method.

In this post you can find a solution that implements an extension method to change the properties of a Windows Forms Control. It’s a large improvement for my previous post: http://www.dailycode.info/Blog/post/2011/04/18/Multi-Threading-and-accessing-controls-from-seperate-threads-in-WPF.aspx

Now this extension will provide the functionality on any control of your form. This is the function that does the magic:

/// <summary>  

/// Executes the Action asynchronously on the UI thread, does not block execution on the calling thread.  

/// </summary>  

/// <param name="control"></param>  

/// <param name="code"></param>  

public static void UIThread(this Control @this, Action code)

{

    if (@this.InvokeRequired)

    {

        @this.BeginInvoke(code);

    }

    else

    {

        code.Invoke();

    }

}

 

Can it be more simpler? This can save a lot of developer time and is one of my latest high impact findings thanks to: http://stackoverflow.com/questions/661561/how-to-update-gui-from-another-thread-in-c

 

Here are some examples on  how to implement this:

Setting some text in a textbox from a separate thread:

this.UIThread(() => lblSubAction.Text = @"Reading Records...");

 

Changing the value of a progressbar:

 

this.UIThread(() => pgbSub.Value = pgbSub.Value + pgbSub.Step);

 

Setting a property of a third party control:

this.UIThread(() => numRecordsFound.EditValue = countForLambda);

 


Clear the controls from a .Net panel of container.

If you are working a lot with usercontrols and dynamlically adding them to the GUI, you might end up with the error saying that there are no more window handlers.

One of the reasons could be that you clear controls from your screen but they do not get discposed. Most of the time its just to many usercontrols, so add some paging or something.

But always try the following solution first. In stead of using the clear call to the panel, dispose each object in the panel. You can use this code:

    Private Shared Sub DisposeObjectsInPanel(ByVal pnl As Panel)

        For iCounter As Integer = 1 To pnl.Controls.Count Step 1

            If Not pnl.Controls(0) Is Nothing Then

                pnl.Controls(0).Dispose()

            End If

        Next

    End Sub