Apr 29, 2013

Object cloning

Object cloning

How to perform shallow copy of complex type?
http://msdn.microsoft.com/en-us/library/system.object.memberwiseclone.aspx

Apr 11, 2013

Windows forms data binding tutorial

http://www.codeproject.com/Articles/24656/A-Detailed-Data-Binding-Tutorial

Windows Forms / DataGridView / End row edit upon value change

I've created DataGridView in windows forms app with check box and text box.
When I click on checkbox I want some action executed - another button clicked.
I require that editing of row in which click occurred finishes its editing session  so that underlying BindingSource is updated to latest change.
So if my click unchecked or checked given field I wish to have it in my underlying DataTable so some other logic can immediately use it.
This proved to bit a bit tricky.
Note that in this scenario Checkboxcolumn in question is expected exactly at columnindex 0.


public EditorGUI()
        {
            InitializeComponent();

gridTables.CellContentClick += ( o, e ) =>
{
if ( e.ColumnIndex > 0 ) return;
gridTables.CommitEdit( DataGridViewDataErrorContexts.Commit );
};
gridTables.CellValueChanged += ( o, e ) => btnQuicksrch_Click( this, null );
        }

Here is second scenario. There are two grids - gridColumns & gridSelFields.
They both reflect same data but filter it in different way.
When user clicks and checks/unchecks checkbox in gridColumns second grid should immediately get  updated since they reflect same data.

private delegate void AfterDirtyStateChangedDelegate();
private void  MyForm()
...


       gridColumns.CurrentCellDirtyStateChanged += gridColumns_CurrentCellDirtyStateChanged;
       gridColumns.CellValueChanged += gridColumns_CellValueChanged;


First event to detect click and SPACE on checkbox is CurrentCellDirtyStateChanged:


void gridColumns_CurrentCellDirtyStateChanged(object sender, EventArgs e)
        {
            if ((sender as DataGridView).IsCurrentCellDirty)
            {
                BeginInvoke(new AfterDirtyStateChangedDelegate(DirtyStateChanged));
            }
        }


If cell "dirty" (change occured) async invoke committing of changes to underlying data list.


private void DirtyStateChanged()
        {
            Debug.Print("DirtyStateChanged()");
            gridColumns.CommitEdit(DataGridViewDataErrorContexts.Commit);
            gridColumns.EndEdit();
            bindSrcColumns.EndEdit();
        }


In DirtyStateChanged all three commits must execute to make sure our change is commited.
Above committing will finally raise CellValueChanged event.


void gridColumns_CellValueChanged(object sender, DataGridViewCellEventArgs e)
        {        
            UpdateIsSelectedToSelFields(e);
            GridColumnsFieldSelection(e.RowIndex);
        }

Only at this point we are sure that binding source of Columns is updated so we can use it.

...

var selField_SelRow_IsSelected = (bindSrcSelFields.DataSource as DataTable).Rows.Find(col_SelRow[EDITORCOLUMN_FIELDNAME])[EDITORCOLUMN_ISSELECTED];          
            (bindSrcSelFields.DataSource as DataTable).Rows.Find(col_SelRow[EDITORCOLUMN_FIELDNAME])[EDITORCOLUMN_ISSELECTED] = !Convert.ToBoolean(selField_SelRow_IsSelected);

...

Watch it ! In above this DOES NOT work :

selField_SelRow_IsSelected = !Convert.ToBoolean(selField_SelRow_IsSelected);

There is at the moment still active bug with DataGridView. When you press SPACE on checkbox field and handle CellContentClick you get exception.
Here it is explained:

http://connect.microsoft.com/VisualStudio/feedback/details/780347/nullreferenceexception-in-notifymassclient-after-checking-unchecking-a-checkbox-in-datagridview-with-spacebar#details

That's where I got above delegate thing.

There is only one misbehavior with above scenario. When you use SPACE key you cannot check and immediately uncheck same record. After first SPACE leave current row and come back.

This is due to fact that after first SPACE we ended EditMode and only by leaving and getting focus again will enter Edit mode again.

Furthermore consider  DataGridView.EditMode

http://msdn.microsoft.com/query/dev11.query?appId=Dev11IDEF1&l=EN-US&k=k(System.Windows.Forms.DataGridView.CellContentClick);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5);k(DevLang-csharp)&rd=true

Apr 8, 2013

Custom type comparing

You have custom type EditorColumn:

public class EditorColumns
{

        public bool IsSelected { get; set; }
        public string FieldName { get; set; }
        public int? OrderId { get; set; }

}

I want to do lambda expression that checks whether MyCollection that is collection of EditorColumn's contains seleted MyEditorColumn of type EditorColumns.
LINQ lambda for this is:   Contains

What is equality criteria for this?
How do you define that MyEditiorColumn has its matches in collection?
Since this is not value type by default .NET will use type reference.

Let's say that two EditorColumn's are equal if their fieldname's exactly match.

This has to be  designed into our EditorColumn like this:


internal class FieldNameComparer : IEqualityComparer<EditorColumn>
        {
            public bool Equals(EditorColumn x, EditorColumn y)
            {
                return x.FieldName.ToLowerInvariant() == y.FieldName.ToLowerInvariant();
            }

            public int GetHashCode(EditorColumn obj)
            {
                return 0;
            }
        }

There is no rule but I suggest that above class is placed inside EditorColumn type def.

Now we can write something like this:

var optionalDefs = defaultColumns.Where(dc => !userDefEdCols.Contains<EditorColumn>(dc,new EditorColumn.FieldNameComparer())).ToList<EditorColumn>();


Here is some more info:

http://www.code-magazine.com/Article.aspx?quickid=100083

Mar 20, 2013

Proper configuring Log4Net in ASP.NET in separate file

There is a lot on net about Log4Net but still this fantastic tool falls in category "setup initially and forget".
This means that after you once configure it you forget is there for months and just use its fluent logging capabilities.
Hence on project kick off I tend to always spend time looking for proper way to configure it and sometimes run into questions.

Here goes recipe for configuring Log4Net for ASP.NET 4.5 MVC hosted in Visual Studio Development server.

  1. First use NuGet and install Log4Net for your project.
  2. For Web only! This is not necessary for Desktop when you execute 8.!
    In your web.config configsections register Log4Net section:
<configSections>
...

    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, 
log4net"/>
...

  </configSections>

3. For Web applications create log4net.xml and reference it as in 4. as standalone Log4Net config file.
For desktop create log4net.Config and reference it using assembly reference (see 8.)  Here is simple example:

<?xml version="1.0"?>
<log4net>
  <appender name="FileAppender" type="log4net.Appender.RollingFileAppender">
    <param name="File" value="log4net_applog.log" />
    <param name="DatePattern" value="yyyy.MM.dd" />
    <param name="RollingStyle" value="Size" />
    <param name="maxSizeRollBackups" value="10" />
    <param name="maximumFileSize" value="100KB" />
    <layout type="log4net.Layout.PatternLayout">
      <param name="ConversionPattern" value="
%date{yyyy-MM-dd HH:mm:ss ffff} [%-3t] %-5p %logger{1}.%method: %message%newline" />
    </layout>
  </appender>
  <root>
    <!-- OFF, FATAL, ERROR, WARN, DEBUG, INFO, ALL -->
    <level value="DEBUG" />
    <appender-ref ref="FileAppender" />
  </root>
</log4net>

4. Use this for WEB. In web.config  AppSettings section add reference to above Log4Net.xml:

  <appSettings>
...

  <add key="log4net.Config" value="log4net.xml" />   

...

5. Give write rights to folder in which log resides. This depends on your OS version. For Win 7 it is NETWORK SERVICE account. If you are using Local IIS then check in your IIS under which account runs your ASP.NET application pool.

6. In Global.asax.cs configure Log4Net instance as external:


 protected void Application_Start()
        {
            log4net.Config.XmlConfigurator.Configure();
...



7. In every class you wish to use log4net register log instance:



   private readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);


8. Use this for Desktop. Add this to your Assembly Properties file:
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "Log4Net.config", Watch = true)]
Attribute watch enables to change configuration on the fly without restarting app.

9. If working in Desktop app go to properties of log4net.config and select "Copy If Newer".



Mar 19, 2013

ASP.NET 4.5 WEB API with Fiddler2

During my learning ASP.NET 4.5 WEB API I've spent too much time on one glich.
Here is my API controller action:

public DummyWrapper Post(DummyWrapper dummy)
        {            
            return "success";
        }



I was trying using Fiddler2 to emulate POST request:


User-Agent: Fiddler

Content-Type: application/json; charset=utf-8

Host: localhost:1600

Content-Length: 40


 {"dummy":{"value":"This is the title"}}

Initial version of my POCO on WEB API service


 public class DummyEntity
            {
                public string value { get; set; }
            }


I could not make Action model binder to deserialize above json object to dummy POCO.
After some time it hit me.
The catch was to wrap up entity in wrapper class.



 public class DummyWrapper
        {
            public DummyEntity dummy { get; set; }
           public class DummyEntity
            {
                public string value { get; set; }
            }
        }  

.NET HttpClient synchronous POST & GET

How to consume  ASP.NET REST-full WEB API in .NET Windows forms client app?
Visual Studio 2012 has introduced async methods and Tasks etc.
Hence it took me some effort to Google-out and figure out correct and cleanest way to POST & GET some POCO to ASP.NET WEB API and return it.

Class System.Net.Http HttpClient has gone through revamping and is ASYNC ready. Therefore it was hard to figure out simple stuff.

Bellow are two generic methods that perform converting POCO to Json using Json.NET framework and POST it to ASP.NET WEB API service. There is also simple GET variant.

Magic word  when using ASYNC methods is property Result.
It instructs method basically to switch to SYNCHRONOUS mode dropping all Task, await etc. stuff.

Yes, this approach negates whole async model of these methods.


using System.Net.Http;
using Newtonsoft.Json;

    public TResponse Post<TResponse, TRequest>(TRequest postdata, string postUrl)            
        {
            using (HttpClient client = new HttpClient())
            {
                var postdataJson = JsonConvert.SerializeObject(postdata);
                var postdataString = new StringContent(postdataJson, new UTF8Encoding(), "application/json");
                var responseMessage = client.PostAsync(postUrl, postdataString).Result;
                var responseString = responseMessage.Content.ReadAsStringAsync().Result;
                return JsonConvert.DeserializeObject<TResponse>(responseString);
            }
        }

        /// <summary>
        /// Perform HTTP Get the specified post ASP.NET WEB API URL.
        /// Try to convert response to TResponse
        /// </summary>
        /// <typeparam name="TResponse">The type of the response.</typeparam>
        /// <param name="postUrl">The post URL.</param>
        /// <returns></returns>
        public TResponse Get<TResponse>(string postUrl)
        {
            using (HttpClient client = new HttpClient())
            {
                var responseMessage = client.GetStringAsync(postUrl).Result;
                return JsonConvert.DeserializeObject<TResponse>(responseMessage);
            }
        }




Links:
http://forums.asp.net/t/1773007.aspx/1/10
http://blogs.msdn.com/b/webdev/archive/2012/08/26/asp-net-web-api-and-httpclient-samples.aspx