<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Ben Gribaudo</title>
	<atom:link href="http://bengribaudo.com/feed" rel="self" type="application/rss+xml" />
	<link>http://bengribaudo.com</link>
	<description>&#34;Come ye, and let us walk in the light of the LORD.&#34; Isaiah 2:5</description>
	<lastBuildDate>Mon, 26 Mar 2012 13:58:39 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>Saving &amp; Restoring WPF DataGrid Columns&#8217; Size, Sorting and Order</title>
		<link>http://bengribaudo.com/blog/2012/03/14/1942/saving-restoring-wpf-datagrid-columns-size-sorting-and-order</link>
		<comments>http://bengribaudo.com/blog/2012/03/14/1942/saving-restoring-wpf-datagrid-columns-size-sorting-and-order#comments</comments>
		<pubDate>Wed, 14 Mar 2012 16:22:36 +0000</pubDate>
		<dc:creator>Ben</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[DataGrid]]></category>
		<category><![CDATA[WPF]]></category>

		<guid isPermaLink="false">http://bengribaudo.com/?p=1942</guid>
		<description><![CDATA[A data grids usually allow users to reorder, resize, and sort its columns. A refined data grid implementation remembers the user&#8217;s changes to its columns&#8217; display, restoring the columns to match how they previously appeared each time the application is &#8230; <a href="http://bengribaudo.com/blog/2012/03/14/1942/saving-restoring-wpf-datagrid-columns-size-sorting-and-order">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>A data grids usually allow users to reorder, resize, and sort its columns. A refined data grid implementation remembers the user&#8217;s changes to its columns&#8217; display, restoring the columns to match how they previously appeared each time the application is launched. How do we implement such a feat if WPF&#8217;s <a href="http://msdn.microsoft.com/en-us/library/system.windows.controls.datagrid.aspx" target="_blank">DataGrid</a> is our control of choice?<span id="more-1942"></span></p>
<p>Analyzing the task, we find that it readily divides into two parts: provide a mechanism to capture/restore the DataGrid columns&#8217; state and set up the mechanics to persist this information across application executions. This article&#8217;s focus will be the first sub-task.</p>
<p>Let&#8217;s extend WPF&#8217;s DataGrid, adding a two-way bindable property named ColumnInfo which exposes the necessary column state information. When a <em>column&#8217;s state</em> is changed, this property will be updated with the relevant information. When the <em>property</em> is changed (i.e. by an outside source such as a view model), the data grid&#8217;s columns will adjust themselves as specified by the property&#8217;s new value.</p>
<h1>Catching Column Changes</h1>
<p>To keep ColumnInfo updated with information our grid columns&#8217; current display characteristics, we need to find out when these characteristics change.</p>
<p>Determining when column re-ordering occurs is easy. Simply override <a href="http://msdn.microsoft.com/en-us/library/system.windows.controls.datagrid.oncolumnreordered.aspx" target="_blank">OnColumnReordered</a>.</p>
<pre class="brush:csharp">protected override void OnColumnReordered(DataGridColumnEventArgs e)
{
    UpdateColumnInfo();
    Base.OnColumnReordered(e);
}</pre>
<p>With this code in place, if the user drags column 1 to the right of column 2 so that the column order changes from &#8220;Column 1 | Column 2&#8243; to &#8220;Column 2 | Column 1&#8243;, our yet-to-be-defined UpdateColumnInfo will be called.</p>
<p>Catching column sort changes (e.g. the user clicks on a column header to sort the grid by that column&#8217;s values) requires more work. DataGrid provides an <a href="http://msdn.microsoft.com/en-us/library/system.windows.controls.datagrid.onsorting.aspx" target="_blank">OnSorting override</a>, but this method is called <em>before</em> the sort occurs. We want to inspect the grid <em>after</em> the sort completes but no OnSortCompleted override is provided. Are we stuck?</p>
<p>Thankfully not. Each of the columns has a sort direction dependency property. Let&#8217;s  iterate through the grid&#8217;s Columns collection and attach a <a href="http://msdn.microsoft.com/en-us/library/system.componentmodel.dependencypropertydescriptor.aspx" target="_blank">DependencyPropertyDescriptor</a> <a href="http://msdn.microsoft.com/en-us/library/system.componentmodel.dependencypropertydescriptor.addvaluechanged.aspx" target="_blank">AddValueChanged</a> handler to each column&#8217;s <a href="http://msdn.microsoft.com/en-us/library/system.windows.controls.datagridcolumn.sortdirectionproperty.aspx" target="_blank">SortDirectionProperty</a>.</p>
<p>At first glance, the following code seems like a good way of implementing this:</p>
<pre class="brush:csharp">protected override void OnInitialized(EventArgs e)
{
	var sortDirectionPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(DataGridColumn.SortDirectionProperty, typeof(DataGridColumn));
	foreach (var column in Columns)
	{
	    sortDirectionPropertyDescriptor.AddValueChanged(column, (sender, x) =&gt; UpdateColumnInfo());
	}
	base.OnInitialized(e);
}</pre>
<p>However, the above creates a memory leak. The event handler lambda expression passed to AddValueChanged as its second argument captures a reference to <em>this</em> (our data grid). As long as this lambda expression is registered to be called when SortDirectionProperty changes, the captured <em>this </em>reference keeps the grid alive. The garbage collector won&#8217;t free up the memory used by our data grid, even if it is no longer being displayed.</p>
<p>We need a way to unregister our handler when the grid is no longer needed. Overriding <a href="http://msdn.microsoft.com/en-us/library/system.idisposable.dispose.aspx" target="_blank">Dispose</a> (from <a href="http://msdn.microsoft.com/en-us/library/system.idisposable.aspx" target="_blank">IDisposable</a>) would seem idea, except that DataGrid (along with most other WPF controls) does not implement IDisposable. Thankfully, <a href="http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.aspx" target="_blank">FrameworkElement</a> (a parent class of DataGrid) offers an <a href="http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.unloaded.aspx" target="_blank">Unloaded</a> event. We will unregister our SorDirectionProperty change handler when this event is raised.</p>
<p>As it turns out, Unloaded is sometimes raised when the control needs to stay around, such as when a user-initiated system theme change occurs. We want our handler to say around as long as the control does, so we need a way to keep our handler are in place after theme changes. FrameworkElement&#8217;s <a href="http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.loaded.aspx" target="_blank">Loaded</a> event comes in handy. It&#8217;s raised whenever the control is loaded—at the initial load and at reloads after theme changes. Our OnInitialized override looks like:</p>
<pre class="brush:csharp">private bool inWidthChange = false;
protected override void OnInitialized(EventArgs e)
{
    EventHandler sortDirectionChangedHandler = (sender, x) =&gt; UpdateColumnInfo();
    EventHandler widthPropertyChangedHandler = (sender, x) =&gt; inWidthChange = true;
    var sortDirectionPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(DataGridColumn.SortDirectionProperty, typeof(DataGridColumn));
    var widthPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(DataGridColumn.WidthProperty, typeof(DataGridColumn));

    Loaded += (sender, x) =&gt;
    {
	foreach (var column in Columns)
	{
	    sortDirectionPropertyDescriptor.AddValueChanged(column, sortDirectionChangedHandler);
	    widthPropertyDescriptor.AddValueChanged(column, widthPropertyChangedHandler);
	}
    };
    Unloaded += (sender, x) =&gt;
    {
	foreach (var column in Columns)
	{
	    sortDirectionPropertyDescriptor.RemoveValueChanged(column, sortDirectionChangedHandler);
	    widthPropertyDescriptor.RemoveValueChanged(column, widthPropertyChangedHandler);
	}
    };

    base.OnInitialized(e);
}</pre>
<p>In the above code, notice that we also added and removed a change notification handler for DataGridColumn.WidthProperty. This makes sense as one of our goals is to record column width changes. However, instead of calling UpdateColumnInfo, this event handler sets the bool instance variable inWidthChange to true. Why?</p>
<p>WidthProperty is changed almost continuously when the user drags to resize a column.  We&#8217;re only interested in the column&#8217;s final width when the resizing has finished. Setting inWidthChange indicates that resizing is happening. Overriding OnPreivewMouseLeftButtonUp lets us find out when resizing completes. Before calling UpdateColumnInfo in our override, we check inWidthChange to make sure that the button release corresponds with a resize. We don&#8217;t want to call UpdateColumnInfo for non-resize related button releases.</p>
<pre class="brush:applescript">protected override void OnPreviewMouseLeftButtonUp(System.Windows.Input.MouseButtonEventArgs e)
{
    if (inWidthChange)
    {
	inWidthChange = false;
	UpdateColumnInfo();
    }
    base.OnPreviewMouseLeftButtonUp(e);
}</pre>
<h1>Processing Changes</h1>
<p>Our code now calls UpdateColumnInfo whenever a relevant change occurs. UpdateColumnInfo sets the grid&#8217;s ColumnInfo property to a new ObservableCollection&lt;ColumnInfo&gt; that containing a ColumnInfo instance for each column in the grid&#8217;s Columns collection. The updatingColumnInfo bool will come in handy shortly.</p>
<pre class="brush:csharp">private bool updatingColumnInfo = false;
public ObservableCollection&lt;ColumnInfo&gt; ColumnInfo
{
    get { /* ...  */ }
    set { /* ...  */ }
}
private void UpdateColumnInfo()
{
    updatingColumnInfo = true;
    ColumnInfo = new ObservableCollection&lt;ColumnInfo&gt;(Columns.Select((x) =&gt; new ColumnInfo(x)));
    updatingColumnInfo = false;
}</pre>
<p>ColumnInfo is a struct holding information on a column&#8217;s display characteristics. The convenience constructor uses a <a href="http://msdn.microsoft.com/en-us/library/system.windows.controls.datagridcolumn%28v=vs.95%29.aspx" target="_blank">DataGridColumn</a> to populate the struct&#8217;s values. The Apply method reverses this process, applying the display characteristics described by the struct to the passed-in column.</p>
<pre class="brush:csharp">public struct ColumnInfo
{
        public ColumnInfo(DataGridColumn column)
        {
            Header = column.Header;
            PropertyPath = ((Binding)((DataGridBoundColumn)column).Binding).Path.Path;
            WidthValue = column.Width.DisplayValue;
            WidthType = column.Width.UnitType;
            SortDirection = column.SortDirection;
            DisplayIndex = column.DisplayIndex;
        }
        public void Apply(DataGridColumn column, int gridColumnCount, SortDescriptionCollection sortDescriptions)
        {
            column.Width = new DataGridLength(WidthValue, WidthType);
            column.SortDirection = SortDirection;
            if (SortDirection != null)
            {
                sortDescriptions.Add(new SortDescription(PropertyPath, SortDirection.Value));
            }
            if (column.DisplayIndex != DisplayIndex)
            {
                var maxIndex = (gridColumnCount == 0) ? 0 : gridColumnCount - 1;
                column.DisplayIndex = (DisplayIndex &lt;= maxIndex) ? DisplayIndex : maxIndex;
            }
        }
        public object Header;
        public string PropertyPath;
        public ListSortDirection? SortDirection;
        public int DisplayIndex;
        public double WidthValue;
        public DataGridLengthUnitType WidthType;
}</pre>
<p>The struct&#8217;s convenience constructor sets SortDirection from the column&#8217;s SortDirection property while the Apply method both sets this property and modifies the grid&#8217;s SortDescription collection. Why?</p>
<p>SortDirection indicates if the user has clicked on a column header to sort the grid by that column. However, programmaticly setting this property does not sort the grid—it simply sets the sort direction indicator on the column&#8217;s header (the little arrow displayed to indicate that the column has been sorted). If we want the grid sorted by this column, we must also update its SortDescription collection.</p>
<h1>Changes from the Other Direction</h1>
<p>So far, we update ColumnInfo whenever the user makes a relevant modification to the grid. Our data grid also needs to update its display in response to external ColumnInfo changes.</p>
<p>We&#8217;d like to be able to two-way data-bind to ColumnInfo, allowing us to connect the grid&#8217;s ColumnInfo to our view model the same way we do with ItemsSource.</p>
<pre class="brush:xml">&lt;local:EnhancedDataGrid ItemsSource="{Binding Items}"
                         ColumnInfo="{Binding Columns}" /&gt;</pre>
<p>To support two-way data-binding, ColumnInfo must be a dependency property.</p>
<pre class="brush:csharp">public static readonly DependencyProperty ColumnInfoProperty = DependencyProperty.Register("ColumnInfo",
	typeof(ObservableCollection&lt;ColumnInfo&gt;), typeof(EnhancedDataGrid),
	new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, ColumnInfoChangedCallback)
    );
public ObservableCollection&lt;ColumnInfo&gt; ColumnInfo
{
    get { return (ObservableCollection&lt;ColumnInfo&gt;)GetValue(ColumnInfoProperty); }
    set { SetValue(ColumnInfoProperty, value); }
}</pre>
<p>When this property is changed, we want to apply the changes to the data grid&#8217;s display. ColumnInfo&#8217;s dependency property definition wires in the necessary event handling.</p>
<pre class="brush:csharp">private static void ColumnInfoChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
    var grid = (EnhancedDataGrid)dependencyObject;
    if (!grid.updatingColumnInfo) { grid.ColumnInfoChanged(); }
}
private void ColumnInfoChanged()
{
    Items.SortDescriptions.Clear();
    foreach (var column in ColumnInfo)
    {
	var realColumn = Columns.Where((x) =&gt; column.Header.Equals(x.Header)).FirstOrDefault();
	if (realColumn == null) { continue; }
	column.Apply(realColumn, Columns.Count, Items.SortDescriptions);
    }
}</pre>
<p>The processing done in ColumnInfoChanged changes column widths and sort directions which will trigger the event handlers we registered in OnInitialized. The bool updatingColumnInfo (set in UpdateColumnInfo) prevents these changes from causing an recursive loop  of event handler calls.</p>
<h1>Done!</h1>
<p>There we have it—an enhanced data grid which allows us to easily persist user changes to its columns&#8217; display characteristics!</p>
<p>Our EnhancedDataGrid has a few limitations. It does not support dynamic addition or removal of columns, as it add and remove column event handlers only when the grid is loaded and unloaded, respectively. It relies on <em>((Binding)((DataGridBoundColumn)column).Binding).Path.Path</em> to properly identify the sort-by column(s), an assumption which may not hold true in certain scenarios. Lastly, external sources must replace ColumnInfo with a new ObservableCollection&lt;ColumnInfo&gt; in order to influence the grid&#8217;s display. Manipulations made to the existing ColumnInfo collection are ignored. Removing these constraints, if the need arises, is left as an exercise to the reader.</p>
<pre class="brush:csharp">/*
The below example code is offered "as-is" with no warrantees of any kind. Use at your own risk.
*/
using System;
using System.Linq;
using System.Windows.Controls;
using System.Windows;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Data;

namespace EnhancedwDataGridExample
{
    class EnhancedDataGrid : DataGrid
    {
        private bool inWidthChange = false;
        private bool updatingColumnInfo = false;
        public static readonly DependencyProperty ColumnInfoProperty = DependencyProperty.Register("ColumnInfo",
                typeof(ObservableCollection&lt;ColumnInfo&gt;), typeof(EnhancedDataGrid),
                new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, ColumnInfoChangedCallback)
            );

        private static void ColumnInfoChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var grid = (EnhancedDataGrid)dependencyObject;
            if (!grid.updatingColumnInfo) { grid.ColumnInfoChanged(); }
        }

        protected override void OnInitialized(EventArgs e)
        {
            EventHandler sortDirectionChangedHandler = (sender, x) =&gt; UpdateColumnInfo();
            EventHandler widthPropertyChangedHandler = (sender, x) =&gt; inWidthChange = true;
            var sortDirectionPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(DataGridColumn.SortDirectionProperty, typeof(DataGridColumn));
            var widthPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(DataGridColumn.WidthProperty, typeof(DataGridColumn));

            Loaded += (sender, x) =&gt;
            {
                foreach (var column in Columns)
                {
                    sortDirectionPropertyDescriptor.AddValueChanged(column, sortDirectionChangedHandler);
                    widthPropertyDescriptor.AddValueChanged(column, widthPropertyChangedHandler);
                }
            };
            Unloaded += (sender, x) =&gt;
            {
                foreach (var column in Columns)
                {
                    sortDirectionPropertyDescriptor.RemoveValueChanged(column, sortDirectionChangedHandler);
                    widthPropertyDescriptor.RemoveValueChanged(column, widthPropertyChangedHandler);
                }
            };

            base.OnInitialized(e);
        }
        public ObservableCollection&lt;ColumnInfo&gt; ColumnInfo
        {
            get { return (ObservableCollection&lt;ColumnInfo&gt;)GetValue(ColumnInfoProperty); }
            set { SetValue(ColumnInfoProperty, value); }
        }
        private void UpdateColumnInfo()
        {
            updatingColumnInfo = true;
            ColumnInfo = new ObservableCollection&lt;ColumnInfo&gt;(Columns.Select((x) =&gt; new ColumnInfo(x)));
            updatingColumnInfo = false;
        }
        protected override void OnColumnReordered(DataGridColumnEventArgs e)
        {
            UpdateColumnInfo();
            base.OnColumnReordered(e);
        }
        protected override void OnPreviewMouseLeftButtonUp(System.Windows.Input.MouseButtonEventArgs e)
        {
            if (inWidthChange)
            {
                inWidthChange = false;
                UpdateColumnInfo();
            }
            base.OnPreviewMouseLeftButtonUp(e);
        }
        private void ColumnInfoChanged()
        {
            Items.SortDescriptions.Clear();
            foreach (var column in ColumnInfo)
            {
                var realColumn = Columns.Where((x) =&gt; column.Header.Equals(x.Header)).FirstOrDefault();
                if (realColumn == null) { continue; }
                column.Apply(realColumn, Columns.Count, Items.SortDescriptions);
            }
        }
    }
    public struct ColumnInfo
    {
        public ColumnInfo(DataGridColumn column)
        {
            Header = column.Header;
            PropertyPath = ((Binding)((DataGridBoundColumn)column).Binding).Path.Path;
            WidthValue = column.Width.DisplayValue;
            WidthType = column.Width.UnitType;
            SortDirection = column.SortDirection;
            DisplayIndex = column.DisplayIndex;
        }
        public void Apply(DataGridColumn column, int gridColumnCount, SortDescriptionCollection sortDescriptions)
        {
            column.Width = new DataGridLength(WidthValue, WidthType);
            column.SortDirection = SortDirection;
            if (SortDirection != null)
            {
                sortDescriptions.Add(new SortDescription(PropertyPath, SortDirection.Value));
            }
            if (column.DisplayIndex != DisplayIndex)
            {
                var maxIndex = (gridColumnCount == 0) ? 0 : gridColumnCount - 1;
                column.DisplayIndex = (DisplayIndex &lt;= maxIndex) ? DisplayIndex : maxIndex;
            }
        }
        public object Header;
        public string PropertyPath;
        public ListSortDirection? SortDirection;
        public int DisplayIndex;
        public double WidthValue;
        public DataGridLengthUnitType WidthType;
    }
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://bengribaudo.com/blog/2012/03/14/1942/saving-restoring-wpf-datagrid-columns-size-sorting-and-order/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>XAML Binding&#8217;s StringFormat Ignored!</title>
		<link>http://bengribaudo.com/blog/2012/03/07/1920/xaml-bindings-stringformat-ignored</link>
		<comments>http://bengribaudo.com/blog/2012/03/07/1920/xaml-bindings-stringformat-ignored#comments</comments>
		<pubDate>Wed, 07 Mar 2012 14:09:01 +0000</pubDate>
		<dc:creator>Ben</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[.Net]]></category>
		<category><![CDATA[WPF]]></category>

		<guid isPermaLink="false">http://bengribaudo.com/?p=1920</guid>
		<description><![CDATA[Why, oh why isn&#8217;t &#60;Label Content=&#8221;{Binding Amount, StringFormat=C}&#8221; /&#62;&#8216;s output formatted as currency? Instead of displaying $123.90, the label shows 123.9. My StringFormat is ignored! Is WPF broken?! Though it might seem otherwise, WPF is working just fine. The binding&#8217;s StringFormat &#8230; <a href="http://bengribaudo.com/blog/2012/03/07/1920/xaml-bindings-stringformat-ignored">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Why, oh why isn&#8217;t <em>&lt;Label Content=&#8221;{Binding Amount, StringFormat=C}&#8221; /&gt;</em>&#8216;s output formatted as currency? Instead of displaying $123.90, the label shows 123.9. My <a href="http://msdn.microsoft.com/en-us/library/system.windows.data.bindingbase.stringformat.aspx" target="_blank">StringFormat</a> is ignored! <em></em> Is WPF broken?!<span id="more-1920"></span></p>
<p>Though it might seem otherwise, WPF is working just fine. The binding&#8217;s StringFormat property <em>only</em> applies if the binding needs to convert the bound object to a string. Since the Content property of any <a href="http://msdn.microsoft.com/en-us/library/system.windows.controls.contentcontrol.content.aspx" target="_blank">ContentControl</a> (such as <a href="http://msdn.microsoft.com/en-us/library/system.windows.controls.label.aspx" target="_blank">Label</a>) is of type object and a string is also an object, the binding system does not need to do a conversion and so  ignores its StringFormat property.</p>
<p>Change the Label to a <a href="http://msdn.microsoft.com/en-us/library/system.windows.controls.textblock.aspx" target="_blank">TextBlock</a>, move the binding to its <a href="http://msdn.microsoft.com/en-us/library/system.windows.controls.textblock.text.aspx" target="_blank">Text</a> property and the binding&#8217;s StringFormat is applied.</p>
<p><em>&lt;TextBlock Text=&#8221;{Binding Amount, StringFormat=C}&#8221; /&gt; </em>outputs $123.90.<em><br />
</em></p>
<p>Why? The binding subsystem knows that Text, the destination property, is of type string and so converts the non-string bound object to a string, applying StringFormat in the process.</p>
<p>So, <em>how do you</em> format the Content property of a ContentControl (such as the Label in our example)? Easy. Set the control&#8217;s <a href="http://msdn.microsoft.com/en-us/library/system.windows.controls.contentcontrol.contentstringformat.aspx" target="_blank">ContentString</a> property to the desired string format. If the Control&#8217;s <a href="http://msdn.microsoft.com/en-us/library/system.windows.controls.contentcontrol.content.aspx" target="_blank">Content</a> property is rendered as a string—which is what happens by default with a Label control—the format specified in ContentString will be applied during the object-to-string conversion.</p>
<p><em>&lt;Label Content=&#8221;{Binding Amount}&#8221; ContentStringFormat=&#8221;C&#8221; /&gt;</em> outputs $129.90!</p>
]]></content:encoded>
			<wfw:commentRss>http://bengribaudo.com/blog/2012/03/07/1920/xaml-bindings-stringformat-ignored/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SSRS &#8211; Updating Linked Report Page Properties</title>
		<link>http://bengribaudo.com/blog/2012/01/30/1851/ssrs-updating-linked-report-page-properties</link>
		<comments>http://bengribaudo.com/blog/2012/01/30/1851/ssrs-updating-linked-report-page-properties#comments</comments>
		<pubDate>Mon, 30 Jan 2012 20:01:35 +0000</pubDate>
		<dc:creator>Ben</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[SSRS]]></category>

		<guid isPermaLink="false">http://bengribaudo.com/?p=1851</guid>
		<description><![CDATA[Page property changes made to Microsoft SQL Server Reporting Services (SSRS) reports are not passed on to linked reports. For example, changing a parent report&#8217;s page size from letter to legal will not change child linked reports&#8217; page sizes to &#8230; <a href="http://bengribaudo.com/blog/2012/01/30/1851/ssrs-updating-linked-report-page-properties">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Page property changes made to Microsoft SQL Server Reporting Services (SSRS) reports are not passed on to linked reports. For example, changing a parent report&#8217;s page size from letter to legal will not change child linked reports&#8217; page sizes to legal.</p>
<p>Sometimes, it&#8217;s desirable to synchronize child report page settings to their parents. Brian Welcker posted a <a href="http://blogs.msdn.com/b/bwelcker/archive/2005/09/28/474898.aspx" target="_blank">code sample showing how to automate this property copying</a> using the now old-style .Net 2.0 Web Reference approach. Let&#8217;s implement the same functionality using the modern (though, unfortunately, more verbose) Service Reference approach.<span id="more-1851"></span></p>
<p>For our example, we&#8217;ll use C# executed in the context of a <em>Console Application</em>.</p>
<h2>Creating the Service Reference</h2>
<p>In <em>Solutions Explorer</em>, right-click on the project&#8217;s <em>References</em> folder and select <em>Add Service Reference</em>. In the dialog that&#8217;s displayed, input your reporting server&#8217;s web service URL, then click <em>Go</em>. Once the service information fetching completes, click <em>OK</em>. (If you&#8217;d like, adjust the namespace to be used for the service proxy classes before clicking <em>OK</em>.)</p>
<h2>Code</h2>
<pre class="brush:csharp">using System;
using System.ServiceModel;
using rs = PropertySync.ReportingService;

namespace PropertySync
{
    class Program
    {
        static void Main(string[] args)
        {
            var service = new rs.ReportingService2010SoapClient();

            // Retrieve a list of reports.
            rs.CatalogItem[] reports = null;
            service.ListChildren(new rs.TrustedUserHeader(), "/", true, out reports);

            // List of properties to copy from parent to linked reports.
            var requestedProperties = new rs.Property[] {
                new rs.Property { Name = "PageHeight" },
                new rs.Property { Name = "PageWidth" },
                new rs.Property { Name = "TopMargin" },
                new rs.Property { Name = "BottomMargin" },
                new rs.Property { Name = "LeftMargin" },
                new rs.Property { Name = "RightMargin" }
            };

            foreach (var report in reports) {
                Console.Write("{0} ({1})", report.Name, report.Path);

                if (report.TypeName == "LinkedReport") {
                    string parentReportLink = null;
                    service.GetItemLink(new rs.TrustedUserHeader(), report.Path, out parentReportLink);

                    // Fetch interested property values from parent report.
                    ReportingService.Property[] parentValues = null;
                    service.GetProperties(new rs.ItemNamespaceHeader(), new rs.TrustedUserHeader(), parentReportLink, requestedProperties, out parentValues);

                    // Set the child report's properties to the just-fetched values.
                    service.SetProperties(new rs.TrustedUserHeader(), report.Path, parentValues);
                    Console.WriteLine(" -- properties updated from {0}", parentReportLink);
                } else {
                    Console.WriteLine(" -- non-linked - skipped");
                }
            }
        }
    }
}</pre>
<p>Compile and run. If the application executes successfully, we&#8217;re done!</p>
<h2>Configuring the Binding</h2>
<p>If the program fails with a security error (perhaps a MessageSecurityException), you probably need to adjust your web service binding configuration. Your code is probably trying to execute web service methods anonymously (without passing authentication credentials) while SSRS is probably configured to require client authentication.</p>
<p>The exact binding settings required depend on your server&#8217;s configuration—detailing the various possibilities is beyond the scope of this article. A common client configuration scenario is illustrated below:</p>
<pre class="brush:csharp">var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;

var endpointUri = "http://reporting/ReportServer_SQL/ReportService2010.asmx";
var service = new rs.ReportingService2010SoapClient(binding, new EndpointAddress(endpointUri));
service.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;</pre>
<p>If you are using integrated (Windows) security, you may need to run your application as an administrator in order for credentials to be passed. (Hint: if you are launching the program from within Microsoft Visual Studio, run Visual Studio as an administrator.)</p>
]]></content:encoded>
			<wfw:commentRss>http://bengribaudo.com/blog/2012/01/30/1851/ssrs-updating-linked-report-page-properties/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP $_POST Empty After Setting post_max_size &amp; upload_max_filesize</title>
		<link>http://bengribaudo.com/blog/2012/01/27/1801/php-_post-empty-after-setting-post_max_size-upload_max_filesize</link>
		<comments>http://bengribaudo.com/blog/2012/01/27/1801/php-_post-empty-after-setting-post_max_size-upload_max_filesize#comments</comments>
		<pubDate>Fri, 27 Jan 2012 19:25:33 +0000</pubDate>
		<dc:creator>Ben</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Configuration]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://bengribaudo.com/?p=1801</guid>
		<description><![CDATA[$_POST was empty! Firebug showed that the web browser was submitting the form&#8217;s data to the server. Yet, for some reason, PHP was not putting the data into the $_POST array. If I switched the form&#8217;s action from POST to &#8230; <a href="http://bengribaudo.com/blog/2012/01/27/1801/php-_post-empty-after-setting-post_max_size-upload_max_filesize">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>$_POST was empty! <a href="http://getfirebug.com/" target="_blank">Firebug</a> showed that the web browser was submitting the form&#8217;s data to the server. Yet, for some reason, PHP was not putting the data into the $_POST array. If I switched the form&#8217;s action from POST to GET, PHP properly populated $_GET. What was wrong with $_POST?!<span id="more-1801"></span></p>
<p>Shortly prior to this problem appearing, I had set a couple of configuration directives in a <a href="http://wiki.dreamhost.com/Php.ini#PHP_5.3" target="_blank">php.ini file fragment</a>—something like this:</p>
<pre class="brush:shell">post_max_size	        2GB
upload_max_filesize     2GB</pre>
<p>Looks correct, doesn&#8217;t it? Nope!</p>
<p>I learned the hard way that PHP uses <a href="http://us3.php.net/manual/en/faq.using.php#faq.using.shorthandbytes" target="_blank"><em>single</em> letter abbreviations</a> to indicate kilobytes (K), megabytes (M) and gigabytes (G)! Dropping the &#8220;B&#8221; off of each &#8220;GB&#8221; fixed the problem—PHP went back to properly populating $_POST.</p>
<p>Interestingly, <a href="http://php.net/manual/en/function.phpinfo.php" target="_blank">phpinfo()</a> and <a href="http://php.net/manual/en/function.ini-get.php" target="_blank">ini_get()</a> won&#8217;t necessarily cough about the extraneous &#8220;B&#8221; being present. This deceptive acceptance of incorrectly formatted configuration values can complicate debugging.</p>
<p><em>Thanks to <a href="http://stackoverflow.com/a/1284934/117424" target="_blank">Stack Overflow</a> for providing the answer I needed to solve this perplexing $_POST issue!</em></p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://bengribaudo.com/blog/2012/01/27/1801/php-_post-empty-after-setting-post_max_size-upload_max_filesize/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Rake Package Task Fails with Status 127 on Windows</title>
		<link>http://bengribaudo.com/blog/2012/01/25/1771/rake-package-task-fails-with-status-127-on-windows</link>
		<comments>http://bengribaudo.com/blog/2012/01/25/1771/rake-package-task-fails-with-status-127-on-windows#comments</comments>
		<pubDate>Wed, 25 Jan 2012 18:44:47 +0000</pubDate>
		<dc:creator>Ben</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Rake]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://bengribaudo.com/?p=1771</guid>
		<description><![CDATA[The other day, I tried running a Rake Package task on a Windows system. Rake aborted with &#8220;Command failed with status (127): [zip -r Module.zip Module...]&#8220;. What could be the problem? Status code 127 is commonly used to indicate &#8220;command &#8230; <a href="http://bengribaudo.com/blog/2012/01/25/1771/rake-package-task-fails-with-status-127-on-windows">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>The other day, I tried running a <a href="http://rake.rubyforge.org/classes/Rake/PackageTask.html" target="_blank">Rake Package</a> task on a Windows system. Rake aborted with &#8220;<em>Command failed with status (127): [zip -r Module.zip Module...]</em>&#8220;. What could be the problem?<span id="more-1771"></span></p>
<p>Status code 127 is commonly used to indicate &#8220;command not found.&#8221; The error message&#8217;s bracketed portion indicates that Rake attempted to run an executable named &#8220;<em>zip</em>,&#8221; passing to it several arguments. By default, Windows systems do not include a command named zip. Hence, the error.</p>
<p>To fix this problem, configure Rake::PackageTask to use an alternative zip command by setting its <em>zip_command</em> property. In the example below, notice the inner quotes surrounding the path to the command. These were necessitated by the space in &#8220;Program Files.&#8221;</p>
<pre class="brush:ruby">Rake::PackageTask.new('Module', :noversion) do |p|
  p.need_zip = true
  p.zip_command = '"C:\Program Files\7-Zip\7z.exe" a -tzip'
  p.package_files.include(..)
end</pre>
<p>Rake::PackageTask also provides a <em>tar_command</em> property which can be used to override the command used to gzip and bzip2 archives.</p>
]]></content:encoded>
			<wfw:commentRss>http://bengribaudo.com/blog/2012/01/25/1771/rake-package-task-fails-with-status-127-on-windows/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Storing Incomplete (Partial) Dates in SQL Databases</title>
		<link>http://bengribaudo.com/blog/2011/12/16/1721/storing-incomplete-partial-dates-in-sql-databases</link>
		<comments>http://bengribaudo.com/blog/2011/12/16/1721/storing-incomplete-partial-dates-in-sql-databases#comments</comments>
		<pubDate>Fri, 16 Dec 2011 20:23:32 +0000</pubDate>
		<dc:creator>Ben</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://bengribaudo.com/?p=1721</guid>
		<description><![CDATA[Complete (full) dates and unknown dates can easily be stored in a database table via a nullable date column. What about storing incomplete (partial) dates—where the month, day and/or year is unknown? How do we record July 10 (unknown year) &#8230; <a href="http://bengribaudo.com/blog/2011/12/16/1721/storing-incomplete-partial-dates-in-sql-databases">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Complete (full) dates and unknown dates can easily be stored in a database table via a nullable date column. What about storing incomplete (partial) dates—where the month, day and/or year is unknown? How do we record July 10 (unknown year) or June 2012 (unknown day)?<span id="more-1721"></span></p>
<p>MySQL <a href="http://dev.mysql.com/doc/refman/5.0/en/using-date.html" target="_blank">supports incomplete dates</a> out of the box. Simply insert zeros in place of the missing date part:</p>
<pre class="brush:sql">INSERT INTO TestTable (ID, IncompleteDate) VALUES
(NULL, '0000-07-10'), -- unknown year
(NULL, '2012-00-15'), -- unknown month
(NULL, '2012-06-00'); -- unknown day</pre>
<p>Microsoft SQL Server doesn&#8217;t support incomplete dates so zeroing out a missing date part won&#8217;t work. Let&#8217;s examine several ways to work around this limitation.</p>
<h2>Placeholder Year</h2>
<p>Suppose we&#8217;re storing birth dates. Let&#8217;s say we know that a day and month will always be provided but sometimes the year will be omitted. That is, sometimes we&#8217;ll need to store dates in the format of March 18 (unknown year).</p>
<p>To use the placeholder year approach, pick a year outside the range of expected year values to use as the unknown year placeholder. In order to accommodate the date February 29 (unknown year), the placeholder year must be a <a href="http://en.wikipedia.org/wiki/Leap_year#Algorithm" target="_blank">leap year</a>.  In the case of our birth date example, let&#8217;s choose year 1200 as the unknown placeholder. It&#8217;s well outside the range of birth dates used in a normal line of business application and is a leap year. To store March 18 (unknown year), we&#8217;ll insert &#8220;1200-03-18&#8243;.</p>
<p>With this approach, normal SQL <a href="http://msdn.microsoft.com/en-us/library/ms186724.aspx#DateandTimeFunctions" target="_blank">date functions</a> work. However, if computations involving the year are invoked, dates using the placeholder year must be filtered out.</p>
<pre class="brush:sql">-- Failed Attempt: Find individuals born before 1980.
-- (Problem: Records with unknown birth years will be included in the result set.)
SELECT *
FROM TestTable
WHERE YEAR(BirthDate) &lt; 1980

-- Successful Attempt: Find individuals born before 1980
SELECT *
FROM TestTable
WHERE YEAR(BirthDate) &gt; 1200 AND YEAR(BirthDate) &lt; 1980</pre>
<h2>Date Parts in Separate Columns</h2>
<p>To implement, create three columns: day, month, year. If a particular date part may be unknown, allow the associate column to store null values. To insert March 18 (unknown year), we execute:</p>
<pre class="brush:sql">INSERT INTO TestTable (BirthDay, BirthMonth, BirthYear) VALUES
(3, 18, NULL);</pre>
<p>This method eliminates the use of a placeholder year and allows for unknown days and months, benefits not offered by the<em> placeholder year</em> approach. Its principle downside? Loss of SQL Server&#8217;s built-in date functionality (<a href="http://msdn.microsoft.com/en-us/library/ms186724.aspx#DateandTimeFunctions" target="_blank">date functions</a>, <a href="http://msdn.microsoft.com/en-us/library/ms187928.aspx" target="_blank">conversion/cast operations</a> and date validation checking).</p>
<p>Date validation can be manually recreated using a somewhat complex set of <a href="http://msdn.microsoft.com/en-us/library/ms188258.aspx" target="_blank">CHECK constraints</a>. The loss of date functions may not be the end of the world. In fact, some operations may become easier. No longer is <em>MONTH(BirthDate)</em> necessary. Selecting <em>BirthMonth</em> now achieves the same effect.</p>
<h2>CLR User-Defined Type</h2>
<p>In addition to the benefits of the <em>date parts in separate columns</em> approach, a <a href="http://msdn.microsoft.com/en-us/library/ms131120.aspx" target="_blank">CLR user-defined type (UDT)</a> allows for a degree of cast/conversion operations and stores its data in a single column on the server. With this method, all functionality is packaged into a data type which can easily be reused server-wide. This data type can also be included in client-side programs, allowing those programs to locally <a href="http://msdn.microsoft.com/en-us/library/ms131066.aspx" target="_blank">work with retrieved data</a> using all the methods and properties defined by the custom data type.</p>
<p>The downside? Out of the methods discussed, this is the most complex to implement</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://bengribaudo.com/blog/2011/12/16/1721/storing-incomplete-partial-dates-in-sql-databases/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Unit Testing &amp; Magento&#8217;s Autoloader</title>
		<link>http://bengribaudo.com/blog/2011/11/30/869/unit-testing-m-autoloader</link>
		<comments>http://bengribaudo.com/blog/2011/11/30/869/unit-testing-m-autoloader#comments</comments>
		<pubDate>Wed, 30 Nov 2011 21:29:37 +0000</pubDate>
		<dc:creator>Ben</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Magento]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Unit Testing]]></category>

		<guid isPermaLink="false">http://bengribaudo.com/?p=869</guid>
		<description><![CDATA[If code written for Magento is executed outside of Magento—as occurs when running unit tests—Magento&#8217;s normal startup process (which registers its autoload functionality) is not run. Without the autoloader in place, the first time PHP encounters a request to initialize &#8230; <a href="http://bengribaudo.com/blog/2011/11/30/869/unit-testing-m-autoloader">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>If code written for Magento is executed outside of Magento—as occurs when running unit tests—Magento&#8217;s normal startup process (which registers its autoload functionality) is not run. Without the <a href="http://php.net/manual/en/language.oop5.autoload.php" target="_blank">autoloader</a> in place, the first time PHP encounters a request to initialize a class which it doesn&#8217;t recognize, it will error out with a &#8220;class not found&#8221; error. Also, without Magento&#8217;s startup process running, PHP doesn&#8217;t know about the <em>Mage</em> family of singleton methods (e.g. <em>Mage::getModel(), Mage::helper()</em>) which are commonly used to create class instances.<span id="more-869"></span></p>
<h2>Solution</h2>
<p>Include/require (perhaps using <a href="http://php.net/manual/en/function.require-once.php" target="_blank">require_once</a>) app/Mage.php in the code you execute from outside of Magento. When Mage.php is included, the code in that file will register Magento&#8217;s autoloader with PHP. Also, Mage.php contains the method definitions for the <em>Mage</em> singleton methods.</p>
<p>Learn more about unit testing code written for Magento on <a href="http://www.magentocommerce.com/wiki/5_-_modules_and_development/phpunit_integration_with_magento" target="_blank">Magento&#8217;s wiki</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://bengribaudo.com/blog/2011/11/30/869/unit-testing-m-autoloader/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Passing a Memo Field to SAGE Payment Soluations&#8217; Gateway API</title>
		<link>http://bengribaudo.com/blog/2011/10/31/1681/passing-a-memo-field-to-sage-payment-soluations-gateway-api</link>
		<comments>http://bengribaudo.com/blog/2011/10/31/1681/passing-a-memo-field-to-sage-payment-soluations-gateway-api#comments</comments>
		<pubDate>Mon, 31 Oct 2011 22:02:20 +0000</pubDate>
		<dc:creator>Ben</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Payment Processing]]></category>

		<guid isPermaLink="false">http://bengribaudo.com/?p=1681</guid>
		<description><![CDATA[In SAGE Payment Solutions&#8217; virtual terminal, transactions have a memo field. However, the gateway&#8217;s HTTPS Bankcard Specifications API documentation doesn&#8217;t mention a way to programmatically set this field. Using the HTTPS POST API, is there a way to pass in &#8230; <a href="http://bengribaudo.com/blog/2011/10/31/1681/passing-a-memo-field-to-sage-payment-soluations-gateway-api">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>In SAGE Payment Solutions&#8217; virtual terminal, transactions have a memo field. However, the gateway&#8217;s <em>HTTPS Bankcard Specifications</em> API documentation doesn&#8217;t mention a way to programmatically set this field. Using the HTTPS POST API, is there a way to pass in memo text?<span id="more-1681"></span></p>
<p>Yes! In your authorization request, put the memo text in a field called<strong> T_memo</strong>.</p>
<p>Thanks to <a href="http://www.cornerstone.cc/" target="_blank">Cornerstone</a>, one of my client&#8217;s credit card processing vendor, for passing on this tip!</p>
]]></content:encoded>
			<wfw:commentRss>http://bengribaudo.com/blog/2011/10/31/1681/passing-a-memo-field-to-sage-payment-soluations-gateway-api/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Show Insert File Instead of Record Audio Dialog  &#8211; FileMaker Pro Container Fields</title>
		<link>http://bengribaudo.com/blog/2011/09/13/1640/show-insert-file-instead-of-record-audio-dialog-fmp-container-fields</link>
		<comments>http://bengribaudo.com/blog/2011/09/13/1640/show-insert-file-instead-of-record-audio-dialog-fmp-container-fields#comments</comments>
		<pubDate>Tue, 13 Sep 2011 13:58:03 +0000</pubDate>
		<dc:creator>Ben</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Container Field]]></category>
		<category><![CDATA[FileMaker Pro]]></category>

		<guid isPermaLink="false">http://bengribaudo.com/?p=1640</guid>
		<description><![CDATA[Click on a container field in a FileMaker Pro form and you&#8217;ll be presented with a Record Audio dialog. To store a file or other non-audio content, you must right-click on the field and then choose Insert File (or Insert Image or &#8230; <a href="http://bengribaudo.com/blog/2011/09/13/1640/show-insert-file-instead-of-record-audio-dialog-fmp-container-fields">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Click on a container field in a FileMaker Pro form and you&#8217;ll be presented with a <em>Record Audio</em> dialog. To store a file or other non-audio content, you must right-click on the field and then choose <em>Insert File</em> (or <em>Insert Image</em> or <em>Insert Object</em>, etc.) from the pop-up menu. When a container field is primarily used to store non-audio content, the required right-clicking quickly gets old. How do you modify a container field&#8217;s behavior so that a left-click displays the <em>Insert File</em> dialog?<span id="more-1640"></span></p>
<h2>Foundation: The Script Comes First</h2>
<p><a href="http://bengribaudo.com/wp-content/uploads/2011/09/EditScriptShowInsertFileDialog.jpg" target="_blank"><img class="alignright size-medium wp-image-1662" title="Edit Script - &quot;Show Insert File Dialog&quot;" src="http://bengribaudo.com/wp-content/uploads/2011/09/EditScriptShowInsertFileDialog-300x219.jpg" alt="" width="300" height="219" /></a>Let&#8217;s start by creating a script that displays an <em>Insert File</em> dialog. This script should contain two steps: <em>Set Error Options [On]</em> followed by <em>Insert File [Table::FieldName] </em>(where <em>Table</em> is the name of the table containing the container field and <em>FieldName</em> is the name of the container field).</p>
<p>(<em>Set Error Options [On]</em> isn&#8217;t strictly necessary. However, if it&#8217;s left out and the user clicks cancels out of the <em>Insert File</em> dialog, a script error will be displayed.)</p>
<h2>The Container&#8217;s Button Setup</h2>
<p>Now, configure the container field&#8217;s button setup so that clicking on the field runs the script we just created. While editing the layout, right-click on the field, choose <em>Button Setup</em>, change the action to <em>Perform Script</em> then click <em>Specify</em> and select the script you created above. Switch from edit layout mode to view mode, then click on the container field. You should see an <em>Insert File</em> dialog. Excellent!</p>
<p>Ah&#8230;but there&#8217;s a catch. Normally, to export or delete the file stored in the container, you&#8217;d right-click on the field and choose the appropriate option from the pop-up menu. However, when we configured button setup, we overrode the field&#8217;s click behavior for <em>both</em> left- and right-clicks. Right-click on the field and <em>Insert File</em> appears instead of the pop-up menu. Unfortunately, FileMaker Pro doesn&#8217;t provide an easy way to limit our script so that it only processes left-clicks. If we want the script to handle clicks, it must handle <em>all</em> of them. What can we do about this?</p>
<h2>Separate Buttons</h2>
<p>It&#8217;s not pretty, but it works. Next to the container field, create two buttons: export and delete. Via button setup, associate each with a script that performs the appropriate action.</p>
<p><a href="http://bengribaudo.com/wp-content/uploads/2011/09/EditScriptExportFile.jpg" target="_blank"><img class="alignright size-medium wp-image-1663" title="Edit Script - &quot;Export File&quot;" src="http://bengribaudo.com/wp-content/uploads/2011/09/EditScriptExportFile-300x219.jpg" alt="" width="300" height="219" /></a>Export&#8217;s script contains two steps: <em>Set Error Capture [On]</em> and <em>Export Field Contents [Table::FieldName]</em>.</p>
<p><a href="http://bengribaudo.com/wp-content/uploads/2011/09/EditScriptDeleteFile.jpg" target="_blank"><img class="alignright size-medium wp-image-1664" title="Edit Script - &quot;Delete File&quot;" src="http://bengribaudo.com/wp-content/uploads/2011/09/EditScriptDeleteFile-300x219.jpg" alt="" width="300" height="219" /></a>Delete&#8217;s script uses an <em>If</em> statement to check if the container field is empty. If the field is not empty, the script calls <em>Set Field [Table::FieldName, ""]</em>.</p>
<p>Why the <em>If</em> statement? Without it, clicking the delete button on a brand-new, empty portal row sets the container field to a value which causes the portal row to be inserted into the table, even though it is empty. The <em>If</em> statement prevents this.</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><a href="http://bengribaudo.com/wp-content/uploads/2011/09/ConditionalFormattingMakingButtonAppearDisabled.jpg" target="_blank"><img class="size-medium wp-image-1648 alignright" title="Conditional Formatting - Making Button Appear Disabled" src="http://bengribaudo.com/wp-content/uploads/2011/09/ConditionalFormattingMakingButtonAppearDisabled-300x258.jpg" alt="" width="300" height="258" /></a>To improve the user interface experience, conditional formatting can be used to make the two buttons appear disabled when the container field is empty.</p>
]]></content:encoded>
			<wfw:commentRss>http://bengribaudo.com/blog/2011/09/13/1640/show-insert-file-instead-of-record-audio-dialog-fmp-container-fields/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SET FMTONLY ON Changes SQL Control-of-Flow Statement Processing</title>
		<link>http://bengribaudo.com/blog/2011/08/30/1570/set-fmtonly-on-changes-sql-control-of-flow-statement-processing</link>
		<comments>http://bengribaudo.com/blog/2011/08/30/1570/set-fmtonly-on-changes-sql-control-of-flow-statement-processing#comments</comments>
		<pubDate>Tue, 30 Aug 2011 20:38:27 +0000</pubDate>
		<dc:creator>Ben</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://bengribaudo.com/?p=1570</guid>
		<description><![CDATA[The other day, my friend Jonathan (of Camenisch Creative) surprised me by pointing out that SET FMTONLY ON changes the way Microsoft SQL Server processes control-of-flow statements (IF&#8230;ELSE&#8230;END, GOTO, RETURN, etc.) When SET FMTONLY is OFF (the default), SQL Server &#8230; <a href="http://bengribaudo.com/blog/2011/08/30/1570/set-fmtonly-on-changes-sql-control-of-flow-statement-processing">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>The other day, my friend Jonathan (of <a href="http://camenischcreative.com/" target="_blank">Camenisch Creative</a>) surprised me by pointing out that SET FMTONLY ON <strong>changes the way Microsoft SQL Server processes control-of-flow statements</strong> (IF&#8230;ELSE&#8230;END, GOTO, RETURN, etc.)</p>
<p>When SET FMTONLY is OFF (the default), SQL Server processes control-of-flow statements normally. In the case of an IF statement, SQL Server determines which conditional expression evaluates to true and then executes the code associated with that expression. Turn FMTONLY to ON and SQL Server <strong>executes <em>every</em> conditional branch</strong>, even those associated with<strong> conditions evaluating to false</strong>!</p>
<p><span id="more-1570"></span></p>
<p>To illustrate, if SET FMTONLY is OFF, executing the following SQL returns one result set containing a column named <em>1 = 1</em>. With SET FMTONLY ON, the below SQL returns two result sets: one containing the already-mentioned <em>1 = 1</em> column and a second containing a column named <em>1 = 2</em>. When FMTONLY is ON, the code blocks associated with <em>both of these conditional expressions</em> are executed—even though one of the conditions evaluates to false!</p>
<pre class="brush:sql">IF 1 = 1
	SELECT '1 = 1' = 'Value'
ELSE IF 1 = 2
	-- Normally, this SELECT statement would not be executed because 1 does not equal 2.
	SELECT '1 = 2' = 'Value'
END</pre>
<p>Setting SET FMTONLY ON also changes the behavior of CONTINUE, RETURN and BREAK statements, TRY&#8230;CATCH blocks and WAITFOR statement as demonstrated by the following SQL:</p>
<pre class="brush:sql">SET FMTONLY ON;

DECLARE @counterContinue INT;
SET @counterContinue = 1;

WHILE (@counterContinue &lt; 10) BEGIN
	SET @counterContinue = @counterContinue + 1;
	CONTINUE;
	-- Normally, this next SELECT statement will never be executed because of the proceeding
	-- CONTINUE; however, SET FMTONLY ON causes it to be processed.
	SELECT 'Post Continue Table' = 'Value';
END

DECLARE @counterBreak INT;
SET @counterBreak = 1;

WHILE (@counterBreak &lt; 10) BEGIN
	SET @counterBreak = @counterBreak + 1;
	BREAK;
	-- If SET FMTONLY where OFF, this next SELECT statement wouldn't be run but
	-- because SET FMTONLY is ON, it will be executed.
	SELECT 'Post Break Table' = 'Value';
END

BEGIN TRY
	SELECT 'Try Table Column' = 'Value'
END TRY
-- Normally, this CATCH statement will only be executed if an error occurs in the TRY
-- statement block. With SET FMTONLY ON, the CATCH block will always be executed.
BEGIN CATCH
	SELECT 'Catch Table Column' = 'Value'
END CATCH

-- With SET FMTONLY ON, the no waiting occurs.
BEGIN
    WAITFOR DELAY '02:00';
    SELECT 'Wait' = 'Value'
END;

-- If SET FMTONLY is OFF, the code below this RETURN statement will never be executed.
-- When SET FMTONLY ON, the following SELECT statement is executed.
RETURN
AfterReturnLabel:
	SELECT 'After Return Label Table' = 'Value'</pre>
<h2>Why does FMTONLY ON cause this behavior change?</h2>
<p>Here&#8217;s my guess: SET FMTONLY ON is used to retrieve metadata describing the result set(s) that <em>could</em> be returned when the specified query is run (i.e. run with SET FMTONLY OFF). In order for SQL Server to provide metadata on <em>all</em> result sets that <em>could be</em> returned, SQL Server must process <em>all</em> branches of <em>all</em> control-of-flow statements.</p>
]]></content:encoded>
			<wfw:commentRss>http://bengribaudo.com/blog/2011/08/30/1570/set-fmtonly-on-changes-sql-control-of-flow-statement-processing/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

