tag:blogger.com,1999:blog-56737104499790804532023-11-15T08:35:51.953-05:00Happy NomadAdrian Alexander Pinterhttp://www.blogger.com/profile/03189159591463976343noreply@blogger.comBlogger19125tag:blogger.com,1999:blog-5673710449979080453.post-70166710765241307692013-01-31T18:29:00.001-05:002019-03-07T11:24:42.824-05:00Data-binding among complex Expressions in C#<p><strong><em>What could be more preferable than simply stating X = Y in one place then having the rest of the work transparently handled on your behalf?</em></strong></p> <p><a title="Expression Trees (C# and Visual Basic)" href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/" target="_blank"><em>Expression</em> trees</a> were one of the many language features <a title="Overview of C# 3.0 - Expression Trees" href="http://msdn.microsoft.com/en-us/library/bb308966.aspx#csharp3.0overview_topic20" target="_blank">introduced</a> with C# 3.0 way back in 2007.  Integration with the compiler allows us to nonchalantly ask it to emit <strong>data</strong> that represents a line of code instead of going about its usual business of readying the code for execution only.  This leads to a convenient convergence of the benefits of reflection and those of compiler-checked code.  An example that is <a title="Getting Information About Objects, Types, and Members with Expression Trees" href="http://blogs.msdn.com/b/csharpfaq/archive/2010/01/06/getting-information-about-objects-types-and-members-with-expression-trees.aspx" target="_blank">further described here</a> is that we no longer must use a string literal to meta-identify a property; instead, we can use a lambda expression like <code>o => o.SomeProperty</code>.</p> <p>It took me much of this month of January 2013 to apply expression trees in a way that goes far beyond that simple property example.  The result demonstrates not only the power of <strong>code as data</strong>, but also the applicability of the concept of <strong>data binding</strong> to coding in general instead of in the user-interface alone.</p> <p>The project arose from the need to manage numerous interdependent properties on view-model and domain model classes.  A property must be updated whenever one of the properties it depends on changes.  Done on an individual bases, this would lead to a lot of event handlers.  My undeviating aim to keep application code as succinct and declarative as possible led me to seek an alternative.  What could be more preferable than simply stating X = Y in one place then having the rest of the work transparently handled on your behalf?  Nothing, of course.  So I create the following public interface and then implemented the guts to make it work:</p> <div class="code"><span class="XmlComment">/// <summary></span> <br /><span class="XmlComment">/// Keeps a target's properties updated with values determined by corresponding expressions. </span><span class="XmlComment">Properties referenced within a value expression are monitored such that property-change</span><span class="XmlComment"> notifications cause the target properties to be updated.</span> <br /><span class="XmlComment">/// </summary></span> <br /><span class="XmlComment">/// <typeparam name="TTarget">The type of object on which properties will be updated</typeparam></span> <br /><span class="Modifier">public</span> <span class="ReferenceType">class</span> PropertiesUpdater<TTarget> : IDisposable <br />{ <br />    <span class="XmlComment">/// <summary></span> <br />    <span class="XmlComment">/// Constructs a PropertiesUpdater instance for properties of the specified target.</span> <br />    <span class="XmlComment">/// </summary></span> <br />    <span class="XmlComment">/// <param name="target">The object on which properties will be updated</param></span> <br />    <span class="Modifier">public</span> PropertiesUpdater( TTarget target ) <br />    { <br />        ... <br />    } <br /> <br />    <span class="XmlComment">/// <summary></span> <br />    <span class="XmlComment">/// Sets the specified property of target to the result of the specified value expression </span><span class="XmlComment">whenever the value of a property referenced within the value expression changes.</span> <br />    <span class="XmlComment">/// </summary></span> <br />    <span class="XmlComment">/// <typeparam name="TProperty">The type of the property on target to be set</typeparam></span> <br />    <span class="XmlComment">/// <param name="targetPropertyExp">The property on target to be set</param></span> <br />    <span class="XmlComment">/// <param name="valueExp">The expression to be monitored for changes and evaluated as </span><span class="XmlComment">the target property's value</param></span>  <br />    <span class="XmlComment">/// <returns>The instance on which this method was invoked, to allow multiple invocations </span><span class="XmlComment">to be chained together</returns></span> <br />    <span class="Modifier">public</span> PropertiesUpdater<TTarget> AddProperty<TProperty>( <br />        Expression<Func<TTarget, TProperty>> targetPropertyExp, <br />        Expression<Func<TProperty>> valueExp ) <br />    { <br />        ... <br />    } <br /> <br />    <span class="Modifier">public</span> PropertiesUpdater<TTarget> RemoveProperty<TProperty>( <br />        Expression<Func<TTarget, TProperty>> targetPropertyExp ) <br />    { <br />         ... <br />    } <br /> <br />    <span class="Modifier">public</span> <span class="ValueType">void</span> Dispose() <br />    { <br />        ... <br />    } <br />} </div> <p>I will present some usage examples to show the sort of advantage that PropertiesUpdater provides.  The restaurant POS system I am now working on includes a Table domain object that represents the sort of table you sit at.  Table’s OpenOrder property is bound in the user-interface and so its private setter raises a property-changed event for notifying the UI of changes.  Table ’s constructor contains the following code for determining OpenOrder’s value:</p> <div class="code"><span class="Keyword">new</span> PropertiesUpdater<Table>( <span class="Keyword">this</span> ) <br />    .AddProperty( <br />        t => t.OpenOrder, <br />        () => DineInParts.Select( p => p.Owner ).SingleOrDefault( o => o != <span class="Keyword">null</span> && o.IsOpen ) <br />    ); </div> <p>PropertiesUpdater recognizes most LINQ-to-Objects methods and can interpret the lambdas passed to them.  After calling AddProperty in the above example, each of the following causes the target Table.OpenOrder property to be updated on <span class="Keyword">this</span>: (1) changes to the Table.DineInParts collection property’s value on <span class="Keyword">this</span>, (2) adds/removes of the collection instance’s elements, and (3) changes to values of the Owner and IsOpen properties referenced via collection elements.  Under the covers, PropertiesUpdater relies on the referenced objects implementing INotifyPropertyChanged and the referenced collections implementing INotifyCollectionChanged.</p> <p>Next I will present two usages taken from the constructor of the order detail screen’s view-model in the restaurant POS system-</p> <div class="code"><span class="InlineComment">// Base the phone number and name on their sources in the domain model.</span> <br /><span class="InlineComment">// Initialize these without causing the contact to be overwritten.</span> <br /><span class="Keyword">new</span> PropertiesUpdater<OrderDetailsScreenPM>( <span class="Keyword">this</span> ) <br />    .AddProperty( <br />        pm => pm.PhoneNumber, <br />        () => domainItem.Contact <span class="Keyword">is</span> VerifiedCustomer ? ((VerifiedCustomer)domainItem.Contact).PhoneNumber : <span class="Keyword">null</span>  <br />    ).AddProperty( <br />        pm => pm.Name, <br />        () => domainItem.Contact != <span class="Keyword">null</span> ? domainItem.Contact.Name : <span class="Keyword">null</span>  <br />    ); <br /> <br /><span class="InlineComment">// Store a contact in the domain model that has the user-provided phone number and name.</span> <br /><span class="Keyword">new</span> PropertiesUpdater<Order>( domainItem ) <br />    .AddProperty( <br />        dm => dm.Contact, <br />        () => PhoneNumber != <span class="Keyword">null</span> <br />            <span class="InlineComment">// If a phone# is provided then lookup a verified contact; if not found then create one.</span> <br />            ? (DataAccess.Instance.FindCustomer( PhoneNumber ) ?? <span class="Keyword">new</span> VerifiedCustomer { PhoneNumber = PhoneNumber, Name = Name }) <br />            <span class="InlineComment">// If only a name is provided then create an unverified contact.</span> <br />            : Name != <span class="Keyword">null</span> ? <span class="Keyword">new</span> Customer { Name = Name } : <span class="Keyword">null</span> <br />    ); </div> <p>The first thing you may notice about this example is that the “one line” permitted for the value expression is actually spanning multiple lines.  The “one line” limit just means the expression must not contain a semicolon.  The expression can in fact be however complex is needed.</p> <p>In the value expressions for the first instantiated PropertiesUpdater, notice the presence of property paths that navigate across more than one property.  These are <code>domainItem.Contact.PhoneNumber</code> and <code>domainItem.Contact.Name</code>.  What if Contact's value changes?  The former value of Contact is no longer of interest (in particular, PhoneNumber's and Name’s values on the former contact), therefore the PropertiesUpdater stops observing the former contact.  This is a similar situation as when an item is removed from a collection in which case the removed element is no longer observed.  In the opposing case, PropertiesUpdater will begin observing a newly introduced value.</p> <p>The Wikipedia article on <a href="http://en.wikipedia.org/wiki/Reactive_programming" target="_blank">Reactive programming</a> provides a canonical example that is in fact PropertyUpdater’s primary purpose.  So, I decided to call the namespace for holding PropertiesUpdater and its supporting classes “Qnomad.Reactive”, at least until someone who knows better tells me that’s an inappropriate classification.  As for Microsoft’s <a href="http://msdn.microsoft.com/en-us/data/gg577609.aspx" target="_blank">Reactive Extensions</a> (Rx), I didn’t see its connection to what PropertiesUpdater provides until I came across an interesting article that fills in the gap: <a href="http://blogs.infosupport.com/data-binding-in-code-using-reactive-extensions/" target="_blank">Data binding in code using Reactive Extensions</a>.  When I first saw the title, I worried my month-long work on PropertiesUpdater may have been for naught.  The single usage example that the article builds up to, however, falls far short of what PropertiesUpdater can accomplish.  The explained functionality reminds me of the basic building block with which PropertiesUpdater observes referenced objects, which resembles Josh Smith’s <a title="One way to avoid messy PropertyChanged event handling (11 July 2009)" href="http://joshsmithonwpf.wordpress.com/2009/07/11/one-way-to-avoid-messy-propertychanged-event-handling/" target="_blank">PropertyObserver</a>.  Using Rx for this purpose, however, doesn’t seem necessary or even useful.  This leaves me wondering how “reactive” Rx really is; although, I suppose it may just be reactive in a different sense.  As with other CS concepts that become buzzwords, “reactive” is likely to go through a period of overuse before its actual meaning gets whittled down in the minds of the masses.  Instead of waiting for that to happen, though, I would love to hear some clarification sooner.</p> <p>Realizing the significant time expense of implementing PropertiesUpdater about a week into it, I checked if something similar already exists.  I came across the WhenAny method of the <a href="http://www.reactiveui.net/" target="_blank">ReactiveUI</a> project, but it pales in comparison to what I ended up accomplishing with PropertiesUpdater.  It wasn’t until after completing PropertiesUpdater and describing it in the above paragraphs that I finally found three projects — <a href="http://bindablelinq.codeplex.com/" target="_blank">Bindable LINQ</a>, <a href="http://obtics.codeplex.com/" target="_blank">Obtics</a>, and <a href="http://clinq.codeplex.com/" target="_blank">Continuous LINQ</a> — that appear to accomplish a similar feat.  My initial impression, however, is that PropertiesUpdater can more easily accomplish the same with its mere two public methods and comparatively tiny implementation.  Anyway, I’ll further explore these two alternatives later, if there is occasion.</p> Adrian Alexander Pinterhttp://www.blogger.com/profile/03189159591463976343noreply@blogger.com0tag:blogger.com,1999:blog-5673710449979080453.post-4062469651777023812012-12-05T19:15:00.001-05:002013-01-30T14:45:21.923-05:00Collection accessors language featureCollections are independent types from the domain objects on which they are used to define properties, yet they are usually closely tied to internal business logic of the single domain object that "owns" them.<br />
<br />
What do you do in each of the following scenarios:<br />
<ol>
<li>You have business logic that determines whether or not an item can be added to the collection.</li>
<li>You have some logic that needs to run before an item is added to the collection and another piece of logic that need run after the item was added.</li>
</ol>
These are the possible approaches that I can think of for tackling these scenarios:<br />
<ol type="a">
<li>Put that logic in the view-model. The logic will need to be repeated everywhere the collection is modified.</li>
<li>Expose the collection as IEnumerable then add Add/Remove methods directly onto the domain object where you put the check logic. This works but the public collection property can always be cast to ICollection or derivative and directly modified, thus violating the intended encapsulation.</li>
<li>Create a custom collection.</li>
</ol>
But I do not feel satisfied with any of these options. I always wished to see a language feature like the add/remove block that was introduced in C# for events but as being available for public collection properties as well.<br />
<br />
public collection IList<orderdetails> Details <br /> {<br /> add { if (isMaxReached) throw new Exception("max reached"); details.Add(value); }<br /> remove { details.Remove(value); } <br /> }
<br /><br />All modifications to the collection would pass through these accessors.</orderdetails>Adrian Alexander Pinterhttp://www.blogger.com/profile/03189159591463976343noreply@blogger.com0tag:blogger.com,1999:blog-5673710449979080453.post-37022908993670040612011-04-13T15:36:00.019-04:002013-02-01T08:35:09.424-05:00Reusable Tree Operations for the .NET FrameworkAn <a href="http://www.codeproject.com/KB/recipes/nonintrusive-trees-graphs.aspx">earlier article</a> of mine describes how a common <code>ITreeNode<T></code> interface can facilitate reusable tree operations thanks to the introduction of extension methods. That article discusses reusable tree operations with respect to domain-models. Trees, however, are a ubiquitous data structure and also occur throughout the .NET Framework, as discussed in <a href="http://www.codeproject.com/KB/linq/LinqToTree.aspx" target="_blank">[Eberhardt 2010]</a>. Ideally, the .NET Framework would include an <code>ITreeNode<T></code> interface that's consistently implemented within the Framework, but this is not so. [Eberhardt 2010] describes a way to provide tree operations to those Framework types by means of the adapter pattern and code generation. His approach, however, has drawbacks. Wrapping a node instance every time it is navigated is likely to impact performance, and relying on code generation complicates maintenance. Upon looking into the matter for myself, I came up with an approach that suffers neither of these drawbacks.<br /><br />Before continuing onto my solution, I want to note what tree operations, specifically, should be available. Leveraging the functionality already provided by LINQ to Objects reduces this list considerably. For example, instead of numerous methods that deal with ancestors, such as <code>IsAncestorOf</code> and <code>FindAncestorOfType<T></code>, a single <code>Ancestors</code> method can be provided with LINQ to Objects taking care of the rest.<pre>//item.IsAncestorOf( newParent )<br />newParent.Ancestors().Contains( item )<br /><br />//tvi = this.FindAncestorOfType<TreeViewItem>();<br />tvi = this.Ancestors().OfType<TreeViewItem>().First();</pre>LINQ to XML sets a good example by borrowing its "axis methods" — that query ancestors, siblings, descendants — from XPath. [Eberhardt 2010] does an excellent job of describing these.<br /><br />Now I will show my solution for adapting .NET Framework tree types in order to make common tree operations available on them. The code that I will be discussing is available as part of <a href="http://www.qnomad.com/wpf/corehelpers/">Qnomad's CoreHelpers library</a>. To create my solution, I applied a <em>variation</em> of the adapter pattern. Instead of instantiating a wrapper for every tree node, there's just one wrapper per adapted tree type, e.g. <code>DependencyObject</code>. The library also includes a singleton of this sort for the <code>ITreeNode<T></code> type discussed in my earlier article. The tree operations, then, use the singletons to navigate the hierarchy. The various pieces of this puzzle are outlined below.<ul><li><code>internal static class TreeOperations</code>: Contains an implementation for each tree operation. Each method signature is of the form:<pre>internal static IEnumerable<T> Operation<T>( T startNode, ITreeNodeAdapter<T> adapter )</pre></li><li><pre>internal interface ITreeNodeAdapter<T><br />{<br /> bool HasSubItems( T treeNode );<br /> IEnumerable<T> GetSubItems( T treeNode );<br /> T GetParent( T treeNode );<br />}</pre></li><li>Next come the implementations of <code>ITreeNodeAdapter<T></code>. The library contains these two:<ul><li><code>internal class WpfTreeNodeAdapter : ITreeNodeAdapter<DependencyObject></code></li><li><code>internal class ITreeNodeAdapterImpl<T> : ITreeNodeAdapter<ITreeNode<T>></code></li></ul></li></ul>You've undoubtedly noticed that everything so far has been declared as internal. The public methods are all on the corresponding "<code>Extensions</code>" classes. The library's <code>WpfExtentions</code> class, for example, contains this method:<pre> public static IEnumerable<DependencyObject> Ancestors( this DependencyObject startNode )<br /> {<br /> return TreeOperations.Ancestors( startNode, WpfTreeNodeAdapter.Instance );<br /> }</pre>Well, that pretty much wraps it up. So now you see a better way for exposing common tree operations on .NET Framework tree types.<p/>Adrian Alexander Pinterhttp://www.blogger.com/profile/03189159591463976343noreply@blogger.com1tag:blogger.com,1999:blog-5673710449979080453.post-27196869130872311522011-03-21T18:23:00.029-04:002011-03-22T12:05:53.677-04:00Advantages of the Domain ModelPrior to arriving at my previous consulting assignment, the company had already completed their first WPF project. The architecture they used on that earlier project included a rather unusual, in my opinion, hybrid object/relational in-memory representation of business objects. Some of the business objects were wrapped in View Models, while the View was directly referencing ADO.NET data rows of other business objects. I initially kept an open mind, programming their new app to that earlier architecture. But I eventually realized that it provided me no way to abstract away from the underlying relational structures. I then spent the weekend refactoring the new app, and building its Data Access layer. Having broken away from the status quo established by their first WPF project, however, my efforts were not greeted warmly. I had to spend a subsequent weekend producing the below “Advantages of the Domain Model”, as well as the <a href="/2011/01/microsoft-embraces-domain-model.html">Microsoft Embraces the Domain Model</a> article I posted earlier, for convincing them to go with a purely object-oriented in-memory representation of business objects.<br /><br /><h2>Advantages of the Domain Model</h2><br />Confining object/relational mapping logic to a clearly demarcated data access layer (DAL) within your application, facilitates the creation of a single, coherent, object-oriented Domain Model to represent your application’s business objects. Adherence to this practice leads to more robust, scalable, and maintainable software.<br /><br /><h3>Separation of concerns</h3><br /><table bgcolor="lightblue" cellspacing="4" cellpadding="4" border="2" style="float:right; margin-left:10px; margin-bottom:10px"><tr><td align="center">View</td></tr><tr><td align="center">View Model</td></tr><tr><td align="center">Domain Model</td></tr><tr><td align="center">Data Access</td></tr><tr><td align="center">Data Services</td></tr></table><b>Separation of concerns</b> is a fundamental principle within computer science. As applied to an application’s overall architecture, it provides for organizing a complex software system into separate, but interdependent, parts. The new app’s codebase is easier to understand and maintain than the old app, thanks to the separated Data Access, Domain Model, and View Model layers. The responsibilities of all three layers are lumped into one layer within the old app.<br /><ul><li><b>Conceptual model.</b> The Domain Model provides a concise representation of the domain of interest. This can aid in communication with experts in the problem domain who are not necessarily developers.</li><br /><li><b>Division of labor and organization of code.</b> Need to retrieve or persist data? Then call into the <code>DataAccess</code> singleton. Need to execute some business logic when a condition’s selected field changes? Then go to the Domain Model and put the logic into the <code>Condition.SelectedField</code> setter. Need to tell the View how to display itself differently? Then go to the View Model.</li><br /><li><b>Code Reuse.</b> Both the old and new apps contain object-relational mapping logic. The difference is that the new app’s Data Access layer, by way of generalized functions, eliminates the needless repetition found within the old app’s numerous Load and Save methods.</li><br /><li><b>Consistent interface to in-memory data.</b> Hiding the relational model specifics, as is accomplished by the new app’s Data Access layer, provides a consistent representation of business objects. The View layer in the old app, on the other hand, contains bindings that reference both .NET properties as well as database fields.</li><br /><li><b>Maintainability.</b> Because of the separation of responsibility, the new app can be modified more easily when business needs change. Changing a database field’s name, for example, requires a limited maintenance effort in the new app. Since data access is coded concisely in the Data Access layer, it involves altering just one string.</li></ul><br /><h3>Increased Productivity</h3><br />Coding directly to ADO.NET objects, as the old app does, means that you cannot easily navigate the relationships between business objects. The new app’s Data Access layer, on the other hand, shields upper layers from database specifics. This allows for more natural modeling of data, and simpler handling of complex relationships between entities.<br /><ul><li>The business objects are strongly typed, so we benefit from compile-time type-checking.</li><br /><li>Business objects’ members expose themselves through Intellisense, eliminating typos and problems with remembering property names.</li><br /><li>We can navigate the object-oriented Domain Model by using the OO-dot syntax, such as condition.SelectedField.Type.AvailableOperators. That is, we just choose an object instance to be our point-of-entry, then navigate from there. The ADO.NET objects in the old app, on the other hand, don’t allow you to easily navigate the data model.</li></ul>Adrian Alexander Pinterhttp://www.blogger.com/profile/03189159591463976343noreply@blogger.com0tag:blogger.com,1999:blog-5673710449979080453.post-90293067121107236172011-01-20T19:30:00.043-05:002013-02-01T08:33:57.231-05:00WPF Dialog Service via BindingView Model logic sometimes involves showing a modal dialog box. The dilemma has always been: how do you show the dialog while abiding by the rule that the View Model not reference the View? A common solution has become to create a dialog service class, then extract its interface for the View Model to consume. In my opinion, using the interface is only slightly better than using the actual class. It still looks to me like the View Model is referencing the View. Why attempt such a charade when the View Model's primary means of communication with the View is perfectly adequate?<br /><br />Although the View Model must not reference View classes, the View Model can still talk to the View. If there wasn't such a capability, then displaying the View Model's properties to the user would be impossible. The communication method I am referring to, of course, is data binding. Data binding is already the primary means of communication between the View and the View Model. We can use it for showing a dialog box as well.<br /><br />Showing a dialog box using the solution I have created involves two easy steps: <ol><li>Add the <code>MessageBoxHelper.Message</code> attached property into the View.</li><li>Within the View Model, set the property specified in the first step when you want the dialog box to be shown.</li></ol>The View Model's property mentioned in these steps is of type <code>DialogPM</code>. This class contains no View-specific logic. It's purpose, rather, is to transport parameters for the <code>MessageBox.Show(...</code> method call from the View Model to the View and, if that method's result is important, back from the View to the View Model.<br /><br />Download the <a href="http://www.qnomad.com/wpf/corehelpers/">Qnomad CoreHelpers library</a> to see the complete example.<p/>Adrian Alexander Pinterhttp://www.blogger.com/profile/03189159591463976343noreply@blogger.com0tag:blogger.com,1999:blog-5673710449979080453.post-82009577183791628432011-01-02T13:00:00.030-05:002013-02-01T08:34:26.095-05:00Microsoft Embraces the Domain ModelSoon after I introduced a Domain Model into the application I was creating for my employer, I found myself at a team meeting trying to explain to fellow programmers, and my manager at the time in particular, why such a change is beneficial. I started by describing how, by the nature of data flowing from the relational database to the object-oriented runtime environment and back, we have an inherent need to translate between two different data models. The manager stopped me in my tracks when she said that I sounded “too academic”. Although left speechless at the moment, in retrospect (and at the risk of sounding like a smart ass) I should have responded, “the law of gravity may sound academic when described by a physicist, but that alters neither its truth nor practical consequence.” Just as the laws of physics describe our physical world, the world our applications live in can be described in terms of data models. The problem stemming from the presence of two data models, instead of just one, is called the <a href="http://en.wikipedia.org/wiki/Object-relational_impedance_mismatch" target="_blank">Object-Relational Impedance Mismatch</a>.<br /><br />The Object-Relational Impedance Mismatch is an “academic” (to quote my former manager) sounding term for a practical problem facing software application development.<br /><blockquote>…work directly against the relational structure of database tables.<br /><br />This is all well and good for simple databases and simple applications. However, as a database gets richer and an application more sophisticated, we see increasing divergence between the way an application needs to look at the data—its “conceptual” or “object” model—and the way information is structured in the database—the “storage” or “relational” model.” A manufacturer's ordering system, for example, may store its order information in multiple related tables, yet the application programmer really wants to work with a single, conceptual “order” entity without having to do a complex JOIN in every order-related query. (This is a case of what’s called the “object-relational impedance mismatch,” if you’ve heard that term.)</blockquote>The above quote is taken from an article in the MSDN Library titled <a href="http://msdn.microsoft.com/en-us/library/ee730343.aspx" target="_blank">Microsoft Data Development Technologies: Past, Present, and Future</a> by Kraig Brockschmidt and Lorenz Prem, Program Managers at Microsoft.<br /><br />Microsoft released the ADO.NET Entity Framework in August 2008 as part of the .NET Framework version 3.5 SP1, and then an improved version in the .NET Framework 4 released in April 2010. What is the ADO.NET Entity Framework? From <a href="http://msdn.microsoft.com/en-us/library/bb399572.aspx" target="_blank">its overview</a>:<br /><blockquote>The ADO.NET Entity Framework enables developers to create data access applications by programming against a conceptual application model instead of programming directly against a relational storage schema. The goal is to decrease the amount of code and maintenance required for data-oriented applications.</blockquote>The “conceptual application model” is commonly known as the Domain Model.<br /><br />With the Entity Framework finally here, it is clear that Microsoft has at long last embraced the Domain Model pattern. This is significant given that, in the 12 year hiatus since Microsoft's last data-access paradigm shift when it released ADO in October 1996, they’ve been careful to find the new emblem of application architecture for years to come. In general, Microsoft is the most immune to “fads”. They tend to do things their own way, and avoid industry standards up until the standards become so engrained that the obvious choice becomes to embrace them or get left behind. Microsoft’s embrace of the Domain Model is evidence, in my eyes, that this has become an engrained industry standard.<br /><br />For those of us using the .NET Framework to build applications, Microsoft’s commitment to the Domain Model goes beyond confirming an industry standard. Having Visual Studio 2010 installed on our machines, while relying on it so much for development, we also have access to the extensive tooling support Microsoft has built around the pattern. From <a href="http://msdn.microsoft.com/en-us/library/ee730343.aspx" target="_blank">the MSDN library</a>: “conceptual and relational representations can be easily created using designers in Visual Studio, and the Entity Framework designer in Visual Studio will create a default mapping without any effort on your part.” It’s a safe bet that this tooling support will be continually improved with future Visual Studio releases.Adrian Alexander Pinterhttp://www.blogger.com/profile/03189159591463976343noreply@blogger.com0tag:blogger.com,1999:blog-5673710449979080453.post-73120492471642173352009-09-16T10:56:00.029-04:002009-09-18T13:12:44.916-04:00Listening to HTML Events from BHOCreating a plug-in for Internet Explorer was an interesting proposition. After years of .NET development, the first point I investigated was how to write my add-in using C#. I was surprised to find no official support from Microsoft for .NET development of IE add-ins. Thanks to COM Interop, however, it's still possible. More so, information and sample code abound for making it happen.<br /><br />A number of Code Project articles have been written about how to setup your add-in project using COM Interop. But I wanted to get straight to writing C# code and seeing its effects within IE. In other words, I wanted an existing library that did the COM Interop stuff for me, and which I could simply reference from my project. Two such solutions exist: one commercial and one open source. The commercial one is <a href="http://www.add-in-express.com/programming-internet-explorer/">Add-in Express for IE</a>. I was always suspicious of their tool since a free trial isn't available, so I never shelled out the $149 (USD). The free open-source alternative known as <a href="http://code.msdn.microsoft.com/SpicIE" title="Simple Plug-In Creator for Internet Explorer">SpicIE</a>, however, is good enough.<br /><br />My plug-in needs to be kept in sync with whatever text is currently selected within the browser window. Listening to HTML events raised on the browser's current page is necessary in order to do this. The <a href="http://code.msdn.microsoft.com/SpicIEContrib" title="SpicIE Contrib - Samples & Guidance">SpicIE Contrib companion website</a> includes a bare bones sample showing how to do this. This sample, however, hooks up the event handlers only after the entire page has loaded. I was looking for something a bit more interactive than that. In other words, I want to hook up the event handlers ideally as soon as the user is able to interact with page elements. This turned out to be a non-trivial task.<br /><br />In response to the above problem, I developed a reusable class called HtmlPageEventManager whose purpose is to subscribe to a given list of HTML events for each new webpage that's opened. A goal is to attach handlers as soon as the user can begin interacting with page elements, even before the page load is complete. Using this class is simple -- just call the constructor:<br /><pre>var evts = new List<HtmlEvent>() {<br /> HtmlEvent.onclick, HtmlEvent.ondblclick, HtmlEvent.onkeydown,<br /> HtmlEvent.onselectstart, HtmlEvent.onselectionchange,<br /> HtmlEvent.onfocusin<br />};<br />new HtmlPageEventManager( this, evts, this.HtmlEventHandler );</pre>You can <a href="http://happynomad121.googlepages.com/HtmlEvents.zip">download my solution here</a> to try it out. And feel free to use it in your own projects if you find it useful.<br /><br />I encountered some issues while creating HtmlPageEventManager.<br /><ul><li>As mentioned above, I want to add the HTML event handlers as soon as possible, i.e. even before the page load is complete. Doing so in the NavigateComplete handler seemed to work great, until I tested opening a hyperlink in a new tab or window. The new tab or window often times didn't have the HTML event handlers functioning correctly. I could see from debugging that IHTMLDocument3.attachEvent was indeed called on the correct document or element instance and returned 'true' indicating handlers were attached, but the handlers were thereafter never invoked. It seemed that the document/element wasn't "ready" to have events attached, although that didn't concur with the 'true' return value.<br />Via the debugger, I discovered that from within OnNavigateComplete, I can detect the problem situation by looking at the IWebBrowser2.Busy property, and adjust my strategy concerning when to add the handlers. So now the registrations occur in OnNavigateComplete only when browser.Busy is true. And if they haven't been registered by the time OnDocumentComplete is reached, then they get registered there instead.</li><br /><li>When you "Refresh" a webpage, the event registrations were always being lost. That is, they weren't being reconnected on the new page elements. I found out that the NavigateComplete and DocumentComplete handles aren’t called on a "Refresh". When I tested it earlier, it seemed like they were called. But what I observed must have been those two handlers called for frames within the main document – not the main document itself. I overcame this issue by subscribing to the BHO's DownloadComplete event, where I again call my "RegisterEventHandlers" helper method that attaches the HTML event handlers. I incorporated the idea from <a href="http://code.google.com/p/pricegoblin/wiki/DemoButton">this code</a> of a "normalPageLoad" member variable to conditionally call RegisterEventHandlers.</li></ul>Adrian Alexander Pinterhttp://www.blogger.com/profile/03189159591463976343noreply@blogger.com0tag:blogger.com,1999:blog-5673710449979080453.post-67397765546676909552009-07-01T21:00:00.031-04:002013-02-01T08:35:39.524-05:00DragDropManager for WPF TreeViews and ListViews<b>Update:</b> Please note that the download for this project <a href="http://www.qnomad.com/wpf/dragdrop/">has been moved here</a>.<br /><br />My new drag-and-drop manager for WPF, which handles both TreeViews and ListViews, is ready for download. Here's a list of features it includes:<br /><ul><li>Supports drag-drop operations among any number of TreeViews and/or ListViews.</li><li>Shows drag adorner while dragging among different TreeViews/ListViews.</li><li>Explicitly supports two types of drop events: ProcessMove for moving an item, and ProcessLink for linking one (<a href="#demo">see demo</a>).</li><li>User controls where, relative to the item under the drag cursor, the item will be dropped: above, below, or (for TreeViews) within.</li><li>Spiffy animations for grabs, drops, and cancelled operations.</li><li>Auto-expand item if hovering over it during drag operation.</li><li>Auto-scroll TreeView or ListView if near top or bottom edge.</li><li>Works on the standard TreeView and ListView controls. No third-party TreeView or ListView control is required.</li><li>Not dependent upon a particular presentation-model (aka ViewModel) implementation. The pattern isn't even necessary in order to use the DragDropManager.</li></ul><a name="demo"></a><h2>Demonstration Application</h2>The demo consists of a TreeView that holds categories, and a ListView populated with the selected category's items. These two controls together have a total of three types of drag-drop operations setup on them.<br /><ol><li>Move a category within the TreeView</li><li>Move an item within the ListView</li><li>Link an item with another category</li></ol>The demo is <a href="http://www.qnomad.com/wpf/dragdrop/#demo">available for download here</a>. The drag-and-drop setup code in the demo looks like this:<br /><code><br />dragMgr.AddControl<Category>( categoriesTreeView );<br />dragMgr.AddControl<Item>( itemsListView );<br />dragMgr.AddHandler<Category>( categoriesTreeView, dragMgr_ProcessMove );<br />dragMgr.AddHandler<Item>( itemsListView, dragMgr_ProcessMove );<br />dragMgr.AddHandler<Item, Category>( categoriesTreeView, dragMgr_ProcessLink );<br />dragMgr.GetLinkEffect += dragMgr_GetLinkEffect;<br /></code><br />The first two lines indicate which controls will have their dragging managed. The following three lines add the event handlers whose job is to actually move or link the domain-model objects. The last line adds the custom link animations you will see in the demo.<br /><br />The body of your ProcessMove and ProcessLink event handlers will depend on how you've architected your presentation-model. The demo app doesn't even use one, so the event handlers simply look like this:<br /><code><br />void dragMgr_ProcessMove( object sender, MoveArgs<Category> moveArgs )<br />{<br /> moveArgs.oldPosition.parent.SubCategories.Remove( moveArgs.itemToMove );<br /> moveArgs.newPosition.parent.SubCategories.Insert(<br /> moveArgs.newPosition.childIndex, moveArgs.itemToMove );<br />}<br /><br />void dragMgr_ProcessMove( object sender, FlatMoveArgs<Item> moveArgs )<br />{<br /> var category = (Category)categoriesTreeView.SelectedItem;<br /> category.Items.RemoveAt( moveArgs.oldIndex );<br /> category.Items.Insert( moveArgs.newIndex, moveArgs.itemToMove );<br />}<br /><br />void dragMgr_ProcessLink( object sender, LinkArgs<Item, Category> e )<br />{<br /> e.TargetLoc.Items.Add( e.ItemToLink );<br />}</code>Adrian Alexander Pinterhttp://www.blogger.com/profile/03189159591463976343noreply@blogger.com3tag:blogger.com,1999:blog-5673710449979080453.post-65198047206887289542009-07-01T17:00:00.012-04:002013-02-01T08:36:14.645-05:00Obfuscation for .NET<b>Update (1 November 2012):</b><br/>
I recently tried upgrading to the latest Smart Assembly version (now owned by Red Gate) while in the process of upgrading my project to .NET Framework 4.5. The latest Smart Assembly version seemed to work okay at first, but then I discovered a bug that caused it to crash. I sent a bug report to Red Gate, along with a repro, then waited and bugged them until I finally received a reply that said: "I'm hopeful that a release may be available with a couple of weeks, however there's no full time development team currently working on Smartassembly; the majority are deployed on other tools." This was especially infuriating since I had purchased "1 year Support & Upgrades". What kind of support is this? None at all, I think. My conclusion is that development on Smart Assembly is stagnant. Therefore, it's best to consider the product dead.<br/><br/>
<b>Original Post:</b><br/>
I recently had to fulfill my need for an obfuscation utility for .NET. More specifically, I needed a solution that works with WPF. I learned that WPF poses a particular challenge concerning obfuscation, since the BAML format has not been made public by Microsoft.<br /><br />My first attempt was using <strong>Dotfuscator Community Edition</strong> which comes bundled with Visual Studio. After spending a significant amount of time picking certain properties and methods to exclude, I still couldn't get the obfuscated version of my app to run. So I finally gave up and instead turned to Google to help me find an alternative tool.<br /><br />The only tool I found, with specific mention of WPF support, was <strong><a href="http://www.smartassembly.com/" target="_blank">{smartassembly}</a></strong>. As long as I exclude those types and (attached) properties used in XAML, this tool works like a charm.<br /><br />{smartassembly} projects are stored in an XML format, so you can edit them using either your favorite text editor or {smartassembly}'s designated user-interface. The UI follows a rather interesting concept. It's a gigantic vertically scrollable area, consisting of a collapsible pane for each settings group. It reminds me a lot of a word-processor. This may seem strange at first, but after a couple hours of usage, you'll likely see the design concept's merit points.<br /><br />{smartassembly} has security and optimization features in addition to simple obfuscation. One of particular interest is error reporting. When your program crashes, error reports can be collected by a server and made available for your viewing. That feature, however, is only available in the Professional and Enterprise editions. The Standard edition will run you 500 USD, and the other editions move up from there. For more information, see the <a href="http://www.smartassembly.com/" target="_blank">{smartassembly} website</a>.Adrian Alexander Pinterhttp://www.blogger.com/profile/03189159591463976343noreply@blogger.com0tag:blogger.com,1999:blog-5673710449979080453.post-62363118011303436112009-04-22T18:26:00.007-04:002013-02-01T08:36:36.920-05:00Non-Intrusive Tree & Graph TypesI just submitted a second article to Code Project. This one is called <a href="http://www.codeproject.com/KB/recipes/nonintrusive-trees-graphs.aspx" target="_blank">Non-Intrusive Tree & Graph Types</a>. It's an original subject for me, totally different from my first CP article, so I don't know what to expect. I'm looking forward to see what feedback I'll be getting.Adrian Alexander Pinterhttp://www.blogger.com/profile/03189159591463976343noreply@blogger.com0tag:blogger.com,1999:blog-5673710449979080453.post-14123648485620604902009-01-31T19:30:00.136-05:002009-07-05T20:28:22.335-04:00Drag-and-Drop in a WPF TreeView<script type="text/javascript">if ( window.location.href == "http://happynomad121.blogspot.com/2009/01/drag-and-drop-in-wpf-treeview.html" ) window.location.href = "http://happynomad121.blogspot.com/2009/07/drag-drop-in-wpf-treeview-listview.html"</script><br /><a href="http://happynomad121.blogspot.com/2009/07/drag-drop-in-wpf-treeview-listview.html">Update: Get the latest version of my DragDropManger by clicking here.</a><br /><br />When it came to adding drag-and-drop support to my application, the unfortunate dearth of an easy-to-use solution for WPF <code>TreeView</code>s became apparent. The closest thing available was <a href="http://www.codeproject.com/KB/WPF/ListViewDragDropManager.aspx" target="_blank">drag-and-drop support for ListViews</a> provided by our beloved WPF guru, Josh Smith. So, with Josh's blessing, I set out to adapt his <code>ListViewDragDropManager</code> into a <code>TreeViewDragDropManager</code>. It was a lengthy learning process, but I'm happy to say that the port is complete and <a href="http://happynomad121.googlepages.com/PresentationModel.zip">a robust solution for TreeView DnD</a> is now available. To demonstrate this functionality, the download contains a modified version of "Demo 2" from <a href="http://www.codeproject.com/KB/WPF/WPF-Presentation-Models.aspx" target="_blank">my Code Project article</a>.<br /><br />Although many of the private helpers and event handlers in my port differ significantly from the original, the public interface remains nearly identical. So you can read Josh's article, the "Using the code" section in particular, to get started. But do note the differences. First, my class takes two type parameters (instead of one), which you can best understand in the context of <a href="http://www.codeproject.com/KB/WPF/WPF-Presentation-Models.aspx" target="_blank">my Code Project article</a>. Second, whereas Josh's <code>ProcessDrop</code> event is optional, my class requires you to subscribe in order to actually move a dropped item.<br /><br />The drag-and-drop setup code in the demo look like this:<br /><div style="overflow-x:auto; overflow-y:hidden"><table><tr><td><pre>var m = new TreeViewDragDropManager<SampleTreeNode, SampleTreeNodeView>( sampleTreeView );<br />m.ProcessDrop += delegate( object sender, ProcessDropEventArgs<SampleTreeNode> e ) {<br />	e.DroppedItem.ParentNode = null; //move the selected item<br />	e.NewParent.SubNodes.Insert( e.NewIndex, e.DroppedItem );<br />};</pre></td></tr></table></div>Adrian Alexander Pinterhttp://www.blogger.com/profile/03189159591463976343noreply@blogger.com0tag:blogger.com,1999:blog-5673710449979080453.post-9566326514634691252008-09-18T11:00:00.025-04:002009-07-03T15:00:12.718-04:00Visa run by bicycleA fact of life for many foreigners staying long term in Thailand, and in other countries probably, is the <b>visa run</b>. My visa is valid for one year, but I'm only allow to stay continuously for a maximum of ninety days. Before exceeding that limit I must leave Thailand, even if for just five minutes before returning again.<br /><br />So today it was visa run time again! Well, my ninety days wasn't up until Monday, but I decide to get it over with a few days early. I arrived yesterday in this town on the Burmese border, called Mae Sot. I came from Lampang where I was visiting some American friends. I didn't depart there until 1pm, however, so I arrived in Mae Sot at 5pm. I headed straight to the border. When I got there at 5:11pm I found out the Burmese side closes at 5pm. Oops.<br /><br />I headed into town where I eventually found the decent and affordable <b>Green Guest House</b>. It's located on a side-street behind the police station, just past the holding facility (just a big cage really) for illegal Burmese immigrants. After checking in, I went to eat dinner at the restaurant Krua Canadian (Canadian Kitchen) which (surprise!) is owned and operated by a Canadian guy with a Thai wife. It was good tasting Mexican food, better than what I had in Chiang Mai. I assume the Canadian owner spent some time living in Mexico before coming to Thailand.<br /><br />This morning I rented one of the guest house's bicycles, then rode the five kilometers or so to the border. I took a bunch of photos, with the idea in mind to write this blog entry tonight. So here the photos are below.<br /><center><br /><table style="width:auto;"><tr><td><a target="nomadphotos" href="http://picasaweb.google.com/lh/photo/E3ry_DQJBf2sjyizv1lEFQ"><img src="http://lh4.ggpht.com/happynomad121/SNKJjQ4kKgI/AAAAAAAAAHU/oOIyByMR_yc/s400/IMG_1540-1.jpg" /></a></td></tr><tr><td style="font-family:arial,sans-serif; font-size:11px; text-align:center">The day's mode of transporation, in front of the guest house</td></tr></table><br /><br /><table style="width:auto;"><tr><td><a target="nomadphotos" href="http://picasaweb.google.com/lh/photo/Ny_kSVb4BBqbRG2Ky9Tm5w"><img src="http://lh3.ggpht.com/happynomad121/SNKJ3YlXn1I/AAAAAAAAAHk/84y0Flre4OM/s400/IMG_1545-1.jpg" /></a></td></tr><tr><td style="font-family:arial,sans-serif; font-size:11px; text-align:center">The road ahead</td></tr></table><br /><br /><table style="width:auto;"><tr><td><a target="nomadphotos" href="http://picasaweb.google.com/lh/photo/5vTF-oIF1uYl-sPyrkFHWw"><img src="http://lh6.ggpht.com/happynomad121/SNKJph1hvwI/AAAAAAAAAHc/Tao4YQbnbVY/s400/IMG_1548-1.jpg" /></a></td></tr><tr><td style="font-family:arial,sans-serif; font-size:11px; text-align:center">The road ahead becomes a four-lane highway</td></tr></table><br /><br /><table style="width:auto;"><tr><td><a target="nomadphotos" href="http://picasaweb.google.com/lh/photo/ou2gNRcQ2he5B0TJV40CcQ"><img src="http://lh6.ggpht.com/happynomad121/SNKJ-viNANI/AAAAAAAAAHs/a6KmsLhnbL8/s400/IMG_1549-1.jpg" /></a></td></tr><tr><td style="font-family:arial,sans-serif; font-size:11px; text-align:center">Border checkpoint in sight</td></tr></table><br /><br /><table style="width:auto;"><tr><td><a target="nomadphotos" href="http://picasaweb.google.com/lh/photo/GED5N2IlTbzxg2ilcPTISA"><img src="http://lh4.ggpht.com/happynomad121/SNKKNL0x6kI/AAAAAAAAAH0/Vg2KSBAgBT0/s400/IMG_1550-1.jpg" /></a></td></tr><tr><td style="font-family:arial,sans-serif; font-size:11px; text-align:center">For some, this is the way home</td></tr></table><br /><br /><table style="width:auto;"><tr><td><a target="nomadphotos" href="http://picasaweb.google.com/lh/photo/DDPs2mON0y-1_2LcL2SHcg"><img src="http://lh6.ggpht.com/happynomad121/SNKKV05MgZI/AAAAAAAAAH8/UCxZ5SKidNI/s400/IMG_1552-1.jpg" /></a></td></tr><tr><td style="font-family:arial,sans-serif; font-size:11px; text-align:center">Crossing the bridge into Burma</td></tr></table><br /><br /><table style="width:auto;"><tr><td><a target="nomadphotos" href="http://picasaweb.google.com/lh/photo/3aj7DE2R8YumxEKRcvhd7Q"><img src="http://lh3.ggpht.com/happynomad121/SNKKmuI2NbI/AAAAAAAAAIE/_tiynUU3NFg/s400/IMG_1559-1.jpg" /></a></td></tr><tr><td style="font-family:arial,sans-serif; font-size:11px text-align:center;">Some Burmese don't need a bridge, or travel documents, to get into Thailand</td></tr></table><br /><br /><table style="width:auto;"><tr><td><a target="nomadphotos" href="http://picasaweb.google.com/lh/photo/OOJVwuEdSbuA2FMzDBVvkQ"><img src="http://lh4.ggpht.com/happynomad121/SNKKum1TInI/AAAAAAAAAIM/PC4VF9enG1w/s400/IMG_1560-1.jpg" /></a></td></tr><tr><td style="font-family:arial,sans-serif; font-size:11px; text-align:center">The Burmese end of the bridge is in sight</td></tr></table><br /><br /><table style="width:auto;"><tr><td><a target="nomadphotos" href="http://picasaweb.google.com/lh/photo/xE3uGHi-1hM1DKo8zpfVAg"><img src="http://lh5.ggpht.com/happynomad121/SNKK2C1OvtI/AAAAAAAAAIs/hRp4lBvEvyI/s400/IMG_1561-1.jpg" /></a></td></tr><tr><td style="font-family:arial,sans-serif; font-size:11px; text-align:center">I spent a couple minutes walking down the street</td></tr></table><br /><br /><table style="width:auto;"><tr><td><a target="nomadphotos" href="http://picasaweb.google.com/lh/photo/NLwfSBwJqOv8-nu11lmXSQ"><img src="http://lh6.ggpht.com/happynomad121/SNKLIJ4DsJI/AAAAAAAAAI0/LZMlMBm76-8/s400/IMG_1563-1.jpg" /></a></td></tr><tr><td style="font-family:arial,sans-serif; font-size:11px; text-align:center">Then into some market... (then I headed back to Thailand)</td></tr></table><br /></center>Adrian Alexander Pinterhttp://www.blogger.com/profile/03189159591463976343noreply@blogger.com0tag:blogger.com,1999:blog-5673710449979080453.post-18937479714231304472008-08-24T23:00:00.006-04:002013-02-01T08:37:06.125-05:00Item Presentation Models for WPFAfter two weeks of work (and some play), I've finally posted my <a target="_blank" href="http://www.codeproject.com/KB/WPF/WPF-Presentation-Models.aspx">Item Presentation Models for WPF</a> article to Code Project. This is my first Code Project article ever, so I'm excited about it. I hope readers find it useful and I look forward to their feedback.Adrian Alexander Pinterhttp://www.blogger.com/profile/03189159591463976343noreply@blogger.com0tag:blogger.com,1999:blog-5673710449979080453.post-90618141364963402872008-08-19T14:00:00.016-04:002013-02-01T08:37:34.785-05:00Naming domain-specific ItemPresentationModel objectsI've decided to follow a rather unconventional naming pattern for domain-specific presentation-model classes — suffix them with "View". I am aware that this naming breaks away from the conventional terminology of MVC and its derivatives, in which "View" refers to the UI layer. So I devote this blog entry to digressing my reasons.<br /><br />Outside the scope of MVC-related design patterns, the word "view" has a more general meaning that's applied in varying contexts. SQL, for example, uses it to mean a derived <i>view</i> of base data. Closer to the present topic, WPF includes the <code>CollectionView</code> class which is a sort of presentation-model. People who prefer that "view" strictly refer to the UI probably hate this choice of class name. However, appending "ViewModel" or the even longer "PresentationModel" to all my class names is verbose in my opinion.<br /><br />For the above reasons I instead use the suffix "View" as an acceptably short alternative for my domain-specific Presentation Model classes. By doing so, I intend to invoke the word's general meaning of the sense that the presentation-model provides a derived <i>view</i> of the domain object. Another interpretation is that the presentation-model provides an interface for the domain object that makes it fit for presentation; the presentation-model is therefore an abstract <i>view</i> (presentation) object. This latter interpretation is useful since it emphasises the presentation-model's actual purpose, which is that it facilitates visual display (in that sense, Josh Smith's original "Presenter" suffix doesn't sound half bad).<br /><br />I am aware that this naming breaks away from every quasi-standard in the literature of MVC and its derivatives. Even the WPF library doesn't use it consistently. That is, they use the word "View" in its more traditional MVC sense as well, as in <code>ListView</code> and <code>TreeView</code>. If instead WPF named those controls "ListControl" and "TreeControl" — which would be consistent with many other controls in their library such as <code>ContentControl</code>, <code>ItemsControl</code>, <code>UserControl</code>, and <code>TabControl</code> — then I would find their choice of "CollectionView" more acceptable. In any case, I still find their suffix "View" to be so much more aesthetically pleasing than the alternatives, so that's what I choose to use in my own projects. Such a choice, however, has implications only within the scope of individual projects or organisations. Therefore, others can use whatever naming convention they find to be the easiest to live with.Adrian Alexander Pinterhttp://www.blogger.com/profile/03189159591463976343noreply@blogger.com0tag:blogger.com,1999:blog-5673710449979080453.post-37888601919268985542008-05-10T15:00:00.021-04:002013-02-01T08:38:06.030-05:00Revisiting bidirectional association helpersThe one-to-many and many-to-many helper classes in my <a href="http://happynomad121.blogspot.com/2007/12/collections-for-wpf-and-nhibernate.html">ObservableCollections</a> project make it easy to maintain in-memory the semantics of bidirectional associations, by keeping an association's references in sync in both directions. The way these helper classes are now, they work only in situations where the many-side is constrained as unique and is non-indexed, i.e. the corresponding collection is an <code>ISet</code>. However, I've encountered a situation in my main project where an association's many-side is indexed, i.e. the corresponding collection is an <code>IList</code>. So I'm revisiting the <a href="http://happynomad121.blogspot.com/2007/12/collections-for-wpf-and-nhibernate.html">ObservableCollections</a> project in order to accommodate this need.<br /><br />Looking back at the bidirectional association helper classes after so much time, I realised how confusing the class and method names have been. So the first step was to rename them. For starters, I renamed the <code>SetContainerToEntityAssocSync</code> class to <code>OneToManyAssocSync</code> to reflect the more general (<span class="footnote">albeit incomplete <small>Although I will add support to the newly named <code>OneToManyAssocSync</code> class for indexed collections, this more general name isn't completely applicable since I won't be adding support for non-unique collections as well.</small></span>) desired applicability. And I renamed the <code>SetContainerToSetContainerAssocSync</code> class to <code>ManyToManyAssocSync</code>. After that, in the <code>OneToManyAssocSync</code> class, I changed other occurrences of "setContainer" to say "one" or "oneSide" since it has a multiplicity of one in the relationship. And I changed "entity" to say "many" or "manySide" since it has a multiplicity of n in the relationship. Finally, I renamed some miscellaneous variable names so that they, to, will be consistent with this new terminology.<br /><br />The next step was to modify the <code>OneToManyAssocSync</code> class's <code>UpdateOneSide</code> method, along with the <code>ManyToManyAssocSync</code> class's <code>UpdateOtherSide</code> method, as follows. The other side's collection must be cast not to <code>ISet</code>, but to the ancestor type <code>ICollection</code> which is common to both <code>ISet</code> and <code>IList</code>. I actually ended up using the generic <code>ICollection<T></code> since the nongeneric version strangely lacks methods for adding, removing, and checking for containment of items. The code now works in cases the collection type is an <code>IList</code>; however, it only works when the uniqueness constraint is being applied to the <code>IList</code>, which is fine in the case of my main project. It will not, however, work for bags or lists with multiple occurrences of the same item. I included checks for this boundary condition.Adrian Alexander Pinterhttp://www.blogger.com/profile/03189159591463976343noreply@blogger.com0tag:blogger.com,1999:blog-5673710449979080453.post-24949832429436946472008-01-18T09:30:00.002-05:002013-02-01T08:38:49.794-05:00Closable TabItem in WPFHaving finally reached the user-interface implementation step in my current programming project, I must first lay the foundation for the program's <a target="_blank" href="http://en.wikipedia.org/wiki/Tabbed_document_interface">tabbed document interface</a> (TDI). It's a TDI of the style seen in Firefox and Internet Explorer, where each tab includes it's own close button. Additionally, in my program the set of tabs is fixed and the user can reopen a tab (via a menu) after it's been closed, which makes it appear at its original position. WPF's standard <code>TabItem</code> control doesn't have a close button, so in order to include such a tab in your own program you must find or create a custom control that does the job. You can <a href="http://happynomad121.googlepages.com/TabItemTest.zip">download the result</a> of my effort—a Visual Studio 2005 solution—here <i>(updated 2008-Feb-11)</i>.<br /><br />The included <code>ClosableTabItem</code> derives from <code>TabItem</code> and provides the functionality you need to easily add close buttons to your program's tabs. The sample application additionally includes for each tab a corresponding item on the 'Tabs' menu, with a check mark indicating the tab's current visibility. After a tab has been closed, a check mark no longer appears on it's menu item. This provides a means for the user to reopen a previously closed tab by selecting a menu item with no check mark. If the user selects a menu item that's currently checked, the item gets unchecked and the tab is closed. But I am not sure this latter behaviour is the most natural/useful. Perhaps it would be better if, rather than closing the tab, it gets selected and it's contents are brought to the front. In that case, I would rename the menu from 'Tabs' to 'Switch Tabs' similar to the 'Switch Windows' menu in Microsoft Word 2007. I am not sure which behaviour would make for a better user experience, so please send me your feedback.<br /><br />Soon after I started creating the custom control, I came across <a target="_blank" href="http://geekswithblogs.net/kobush/archive/2007/04/08/CloseableTabItem.aspx">a blog post</a> which looked like it already did the work for me. Upon looking at the solution's source code, however, I became disappointed with it's architecture. Rather than reusing the <code>Style</code> and <code>Template</code> of it's parent <code>TabItem</code> type by deriving from it, it completely replaces them. This flows contrary to the object-oriented principles of code reuse which I have internalised. So I decided to continue creating my own custom control. At first I thought that after adding the <code>BasedOn</code> attribute onto my custom control's <code>Style</code> element, I'd just need to write a few lines of code then be done with it. But it actually took more work than that, involving WPF features such as data templating, property bindings, and routed commands. The resulting custom style code is a lot shorter than the competition's, though—less than 90 lines as opposed to about 170—so I consider it an improvement.Adrian Alexander Pinterhttp://www.blogger.com/profile/03189159591463976343noreply@blogger.com2tag:blogger.com,1999:blog-5673710449979080453.post-17016526494192475612007-12-11T10:00:00.000-05:002008-01-13T08:09:47.934-05:00Camping trip to Doi InthanonI can't believe the extent of our travels in just four days. We went shopping in Bangkok on Friday afternoon, where I bought a new <a target="_blank" href="http://en.wikipedia.org/wiki/Torchiere">torchiere</a> (floor lamp) for my home office. Much to our own surprise, we managed to make it to Chiang Mai by Saturday morning. We bought our new camping gear and then had an excellent camping trip with our friends, David and Jeab. We were back in Bangkok by Sunday night, and then on our home island by Monday evening. Whew.<br /><br />When we departed our home Thursday evening, we had the intention of heading all the way to Chiang Mai. Even before reaching Bangkok, however, we decided it wasn't worth the additional eleven hour bus ride. We slept a night at my girlfriend's parent's house in Bangkok, and went shopping the next day. That evening, however, my girlfriend's mother phoned from work saying her niece (my girlfriend's cousin) will be driving with her boyfriend to Chiang Mai that very evening. We scrambled to hitch a ride, and arrived in Chiang Mai the next morning.<br /><br />The highlight of the trip, without a doubt, was ascending to the peak of Doi Inthanon in Chiang Mai on Sunday morning at 6 a.m. Being the highest point in Thailand, it's a popular tourist destination among Thais. Now is the country's "cold" season, and that peak is the coolest place. Everyone goes there at 6 a.m. because it's the coldest time of day. It's for the thrill (?) of feeling freezing cold, as if in some foreign country.<br /><div style="text-align: center;"><a target="nomadphotos" href="http://picasaweb.google.com/happynomad121/200712DoiInthanonChiangMai/photo#5143496758551917090"><img src="http://lh6.google.com/happynomad121/R2FfDOzcMiI/AAAAAAAAAAg/xXD5RyHBTw0/s400/SUC54863.JPG" /></a><br /><br /><a target="nomadphotos" href="http://picasaweb.google.com/happynomad121/200712DoiInthanonChiangMai/photo#5143533532061905490"><img src="http://lh4.google.com/happynomad121/R2GAfuzcMlI/AAAAAAAAAB4/YUi4iV13bkc/s400/SUC54865%20edit1.JPG" /></a><br /><br /><a target="nomadphotos" href="http://picasaweb.google.com/happynomad121/200712DoiInthanonChiangMai/photo#5143533755400204898"><img src="http://lh4.google.com/happynomad121/R2GAsuzcMmI/AAAAAAAAACA/whrua1UcHc4/s400/SUC54866%20edit1.JPG" /></a><br /><br /><a target="nomadphotos" href="http://picasaweb.google.com/happynomad121/200712DoiInthanonChiangMai/photo#5143533965853602418"><img src="http://lh5.google.com/happynomad121/R2GA4-zcMnI/AAAAAAAAACI/QuXQR02o_qY/s400/SUC54868%20edit1.JPG" /></a><br /><br /><a target="nomadphotos" href="http://picasaweb.google.com/happynomad121/200712DoiInthanonChiangMai/photo#5143534184896934530"><img src="http://lh4.google.com/happynomad121/R2GBFuzcMoI/AAAAAAAAACQ/u5CDsTRV71k/s400/SUC54874%20edit1.JPG" /></a><br /></div>Adrian Alexander Pinterhttp://www.blogger.com/profile/03189159591463976343noreply@blogger.com0tag:blogger.com,1999:blog-5673710449979080453.post-27755386724257946302007-12-06T05:00:00.011-05:002013-02-01T08:39:17.446-05:00Collections for WPF and NHibernateMy current programming project is on the Microsoft .NET platform, and I'm writing code in the C# language. I'm using NHibernate — a popular object/relational mapping tool — in the data access layer, and Windows Presentation Foundation (WPF) for the user interface. One of the features making WPF so attractive is data binding. Although I was initially disappointed when I found out that NHibernate's collections don't work correctly out-of-the-box with WPF data binding, I soon found <a href="http://analog-man.blogspot.com/2007/01/bridge-gap-between-your-nhibernate.html">Gary DeReese's blog entry</a> which provides a (partial) solution. Gary's "NotifyingCollectionDemo" shows how a custom collection type can be built to rectify NHibernate's shortcoming. After studying how his <code>ObservableList</code> (which is actually an observable bag) was created relative to the NHibernate API, I began work on my own "ObservableCollections" project which includes three custom collection types: observable bag, observable list, and observable set.<br /><br />I've finished creating these custom collection types, so you can <a href="http://happynomad121.googlepages.com/ObservableCollections.zip">download the result now</a> <i>(updated 2008-Dec-10)</i> to use in your own projects. The zip archive contains a Visual Studio 2005 solution that includes three projects. The "ObservableCollections" project is the library containing the custom collections. The "ObservableCollections Demo" project is a WPF program that you can run to play with the contents of a sample set and sample list collection. And the "CoreHelpers" project contains some miscellaneous helper classes that I use in various projects. Before running the demo project, however, you'll need to do the following:<br /><ul><li>Add references to the library and demo projects, pointing to the following NHibernate assemblies on your computer (unless already in the GAC): NHibernate, Castle.DynamicProxy, Iesi.Collections, and log4net.</li><br /><li style="padding-bottom:0">Change the absolute path of the database in the hibernate.cfg.xml file to match its location on your computer.</li></ul>The "CoreHelpers" project includes a <code>OneToManyAssocSync</code> helper class that makes it easy to maintain in-memory the semantics of a one-to-many bidirectional association. The demo project shows how to use it. There is also a <code>ManyToManyAssocSync</code> class for easily maintaining many-to-many bidirectional associations. (See <a href="http://happynomad121.blogspot.com/2008/05/revisiting-bidirectional-assoc-helpers.html">this blog entry</a> for a note about the renaming of these classes from their former names.)Adrian Alexander Pinterhttp://www.blogger.com/profile/03189159591463976343noreply@blogger.com18tag:blogger.com,1999:blog-5673710449979080453.post-62478853265629959042000-01-30T23:28:00.004-05:002009-03-05T12:38:04.744-05:00Senior Year at Lewisburg Area High School: List of Laments<UL><br /><LI> I had been continuously in search of somewhere quiet to work in the school building, and no such place exists. Study halls, especially in the cafeteria, are filled with people talking. Even the library is typically noise-filled, usually a teacher talking to a single student.<br /><br /><LI> Senior year study halls in the cafeteria never provided silence in which to concentrate on work. The library, however, was usually considerably quieter. Nonetheless, in my study hall, when students asked the study hall teacher for a pass to the library (or other places such as to the guidance office), he would demand that the student wait until role was called. Role usually took, while this teacher demanded silence only while it was being called, around 10-15 minutes, or ¼ the period.<br /><br /><LI> The guidance office did not provide me with a quiet place to take my AP exam. I was the only one taking that particular exam. During the first segment, there were multiple announcements over the intercom that interrupted at least 20 minutes of test time. Those interruptions also greatly threw off my concentration for periods of time in between. The College Board did not weigh my exam grade in consideration of this.<br /><br /><LI> Teachers typically waste a great deal of time at the beginning and at the end of class periods. The content of most classes is largely review and not new material. There are very few AP courses offered, in a very limited range of subjects.<br /><br /><LI> My senior year Trigonometry teacher would spend a considerable amount of time at the beginning of each class talking about professional sports.<br /><br /><LI> Both classes I've had with the Health teacher have been a pathetic demonstration of the immense expanse of uselessness which extends across most of the school day. This teacher often spends half a class period talking to the entire class about such thing as school basketball teams, or to certain students about somewhat personal issues. The Health class I had with him sophomore year had only one class period which I feel I actually learned something. That day we watched a National Geographic special. Eighty percent of the class time for that class sophomore year, was not even spent talking about course material.<br /><br /><LI> The Anatomy class I took with the above teacher senior year, in the time we were actually talking about the course material, was a waste as well. The class consisted simply of memorizing names of bones, muscles, and processes but with no intent of this teacher on giving us an understanding of these. I and my former classmates may now recognize the name of a muscle, bone, or process if we heard it spoken, but any conceptualization of it would escape us.<br /><br /><LI> When I mentioned to my Junior year English teacher that I saw fault in the fact that through-out high school we only focused on British and American (predominantly white American) Literature, he did not express any reciprocal concern or desire to change this fact.<br /><br /><LI> The only book in all of high school not written by a European or white American that I was assigned to read, was in my senior year AP English class, a class which not the majority of students are opt to taking. The discussion of this novel, lead by the instructor, was incompetent, insufficient, and rushed. I did my end-of-year research paper on this book and read things I was amazed were not mentioned in class by the instructor. From the instructor, I was introduced to such racism as "dark black men" intimidate her because of their blackness. In this particular case, I confronted her in front of the class. She immediately grew defensive and left the room in a furry.<br /><br /><LI> Computer equipment and supplies are plentiful. This does not mean, however, that they are used effectively. The majority of the time, printers in the library or Tech-Ed lab do not work properly. I've wasted a large amount of time simply trying to print a word processor document. Student "administrators" are largely depended on for maintaining the equipment. This provides a scapegoat for the responsible faculty members, but is not effective enough in maintaining the equipment (printers, scanners, CPUs, etc.). In addition, the faculty does not have the training or knowledge in rendering the capabilities of the expensive computer equipment. Much of the technology, purchased with taxpayer's money, goes to waste.<br /><br /><LI> Most attempts at individuality are discouraged by the repressive atmosphere maintained by the administration and faculty. This is reflected in the curriculum as well, in which diversity in English literature presented to the students is non-existent.<br /><br /><LI> The Junior year english class consisted mainly of filling out "study guides" while we read classic novels. The tests on these novels consisted of questions taken directly from the study guides. The study guides consisted largely of specific details from the novels, most of which were completely irrelevant to the underlying meaning. I'll pick our reading of Edith Wharton's <I>Ethan Frome</I> as an example. One item on the study guild was the color of the character Zeena's rocking chair. For the exam, we also had to recognize a photograph the teacher took of a hill, which is the supposed basis of the fictional hill the characters Mattie and Ethan took their fateful sled ride on. We were not at any point asked to uncover our own thought and interpretation of the novel. We were simply <I>told</I> what to "think" of the novel. The teacher's entire course was built on making us trivia wizards in the subjects we covered. But being able to name the color of Zeena's rocking chair from <I>Ethan Frame</I>, does not do much in the way of making us brighter people.<br /><br /><LI> Full day detentions, known in Lewisburg High School as "TRAC," are frequently assigned to students for an extremely wide range of offensives. The first instance in the school year a student is assigned TRAC, he/she is kept one day in the "TRAC room." If there are additional "offenses" that school year, the student is kept three consecutive days, during which he/she is not allowed to attend any class other than gym. The concept of being given an entire day, or three days, to catch up on work is very appealing to many students, and indeed if one is lucky enough to have TRAC on a quiet day, it can be quite productive. However, for most individuals a typical day in the TRAC room can be very unproductive. The TRAC "supervisor" is also a Special Education teacher. He is more often than not tutoring students in front of the room, speaking load and clear so everyone in the room hears him. In addition to this continuous interruption, this Special Education teacher often holds conversations with other staff members, also loud and clear and in front of the room.<br /><br /><LI> This Special Education teacher is often in a bad mood, the unpleasant result of which falls on whoever has TRAC that day. Such days, which are most days, result in students who have TRAC that day, having TRAC one or more extra days. This perpetuates the situation of many who are consistently in TRAC. This teacher takes more of an initiative in tormenting individuals who are consistently in TRAC, who also happen to be individuals with parents less concerned than most about such injustices. In one particular instance which I witnessed first hand, a student who was a frequent roomer in TRAC, was "upgraded" from one day remaining of TRAC. This was a direct result of the teacher's bad mood that day.<br /><br /><LI> The only especially good English teacher I had in the high school, was not a faculty member, but a student teacher from Bucknell University. She had a creative and energetic approach to teaching, from which I feel I and my classmates benefited greatly. I also feel the spanish teacher has a creative and energetic approach to her Spanish courses. Honors Chemistry, which was taught by a teacher now retired, was also a very benefiting and useful course. However, these are only exceptions in this item. They make up an extremely small fraction of the classes offered at the high school.<br /><br />Overall, I should summarize the productiveness within the school. The atmosphere of the high school is NOT one of productiveness, as some of the above examples illustrate. For the most part, it is not one of learning either. There are only a few exceptions to this, which are shown in the previous item. As a whole, I can confidently say that the content of most courses is a waste of time, from which I have not drawn any benefit. In class time which is actually being used, 95% of the focus is on discipline and being told what to "think," and 5% of the focus is on academics and actual thinking and understanding. Upon my graduation, not one Ph.D. in any course subject was teaching at the high school. This list of laments could continue on for miles with specific deficiencies noted in my senior year and in the proceeding years, but I hope I have provided a sufficient sampling.<br /></UL>Adrian Alexander Pinterhttp://www.blogger.com/profile/03189159591463976343noreply@blogger.com0