Wednesday, August 31, 2011

VS : Changing .config file using conditional post-build event

Sometimes .config file - either app config file or web.config file - needs to be changed based on some condition during the build time. For example, one might want to use local SQL connection string in one case and use remote SQL connection string for another case. Due to pre-build / post-build event feature of Visual Studio, these cases can be solved without big effort.

Let's say one wants to use remote SQL connection by default and use local SQL connection only when special parameter (called UseLocalSQL) is defined. To implement this sceanrio, first one needs to add the condition to Post build event in VS Project Properties dialog.


if '$(UseLocalSQL)'=='True' Copy "$(ProjectDir)LocalApp.config" "$(TargetPath).config" /y

This means if UseLocalSQL parameter is defined and it's true, the copy .config command will be executed. If local SQL is not needed, default app.config will have remote SQL connection string.
(NOTE: one can also use pre-build event instead of post-build event. It's matter of implementation choice)

If one needs to use MSBUILD tool, the following can be used.

C>msbuild test.sln /t:Rebuild /p:Configuration=Release /p:UseLocalSQL=True

And if one uses TFS build system, the msbuild paramaeter can be specified as follows:


Friday, August 26, 2011

WPF RibbonControl : RibbonApplicationSplitMenuItem triggers click command twice

In WPF RibbonControl 4.0, single menu click on RibbonApplicationSplotMenuItem caused click event triggers twice. This means that if the menu is supposed to show a dialog, two same dialogs are going to pop up.

Here is an xaml that is using RibbonApplicationSplotMenuItem.

<ribbon:RibbonApplicationSplitMenuItem x:Uid="saveAsSplitMenu" Header="Save As"
                         Command="SaveAs" CommandParameter="SaveAsFile">
   <ribbon:RibbonApplicationMenuItem x:Uid="saveAsDoc" Header="Save As Doc"                                                
                         Command="SaveAs" CommandParameter="Doc" />
   <ribbon:RibbonApplicationMenuItem x:Uid="saveAsTxt" Header="Save As Text"
                         Command="SaveAs" CommandParameter="Text" />
</ribbon:RibbonApplicationSplitMenuItem>


And let's say we registered CommandBinding for built-in ApplicationCommands.SaveAs command as follows.


CommandManager.RegisterClassCommandBinding(typeof(RibbonWindow),
    new CommandBinding(ApplicationCommands.SaveAs, ExecutedSaveAsCommand, CanExecuteSaveAs));



ExecutedCommand handler is defined as follows.

private void ExecutedSaveAsCommand(object sender, ExecutedRoutedEventArgs e)
{         
    ....
    switch(e.Parameter.ToString())
    {
       case "...": 
         break;
         ... 
       case "SaveAsFile":
         if (e.OriginalSource.GetType() == 
             typeof(RibbonApplicationSplitMenuItem))
         {
            this.SaveAsFile();
         }
         break;
    }
}

RibbonApplicationSplitMenuItem triggers two events with different OriginalSource: one for RibbonButton and the other for RibbonApplicationSplitMenuItem. The example above shows event filtering of RibbonApplicationSplitMenuItem source. This might be considered as workaround until it will be fixed(?) later in RibbonControlLibary.

Monday, August 1, 2011

WPF - how to display different category in a TreeView using DataTemplateSelector

WPF TreeView typically displays hierarchical tree view by using HierarchicalDataTemplate. This HierarchicalDataTemplate allows you to display different types in different ways. For example, for class A type child node, one might want to display image and name while for class B child node, one might need to display name and size textblock. This usually can be archieved by using DataType attribute.

<HierarchicalDataTemplate x:Key="LargeAreaTemplate" DataType="{x:Type vm:LargeArea}" >
If one needs one hierarchical structure with consistent parent-child relationship, defining DataType in HierarchicalDataTemplate will do the trick. However, what if one needs two different hierarchies in a tree view? If two different categories (let's say,  for many-to-many relationship) should be shown in a give tree view, how can it be done with HierarchicalDataTemplate? The following picture shows an example.



There are 2 categories. By Area category shows Age child nodes. By Age category shows Area child nodes.












In a XAML, we can add different hierarchical data templates per each category. For [By Area] view, Area is parent and Age is child node type and vice versa for [By Age] view. If you look at below example, you can see that each category view has different data templates.


<Window x:Class="Tree.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="323" Width="253"
        xmlns:vm="clr-namespace:Tree"
        >
    
    <Window.Resources>
        <vm:MyTemplateSelector x:Key="MyTemplateSelector" />       
        
        <!-- AREA VIEW -->
        <HierarchicalDataTemplate x:Key="LargeAreaTemplate" DataType="{x:Type vm:LargeArea}" 
                                  ItemsSource="{Binding AgeCollection}"
                                  ItemTemplateSelector="{StaticResource MyTemplateSelector}">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Name}" />
                <TextBlock Text=" : Size=" />
                <TextBlock Text="{Binding AreaSize}" />
            </StackPanel>
        </HierarchicalDataTemplate>
        <HierarchicalDataTemplate x:Key="SmallAreaTemplate" DataType="{x:Type vm:SmallArea}" 
                                  ItemsSource="{Binding AgeCollection}"
                                  ItemTemplateSelector="{StaticResource MyTemplateSelector}">            
            <StackPanel Orientation="Horizontal">                
                <TextBlock Text="{Binding Name}" />
                <TextBlock Text=" : Population=" />
                <TextBlock Text="{Binding Population}" />
            </StackPanel>            
        </HierarchicalDataTemplate>
        <HierarchicalDataTemplate x:Key="LeafAgeTemplate" DataType="{x:Type vm:Age}"                                   
                                  ItemTemplateSelector="{StaticResource MyTemplateSelector}">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Name}" />
            </StackPanel>
        </HierarchicalDataTemplate>


        <!-- AGE VIEW -->
        <HierarchicalDataTemplate x:Key="AgeTemplate" DataType="{x:Type vm:Age}" 
                                  ItemsSource="{Binding AreaCollection}"
                                  ItemTemplateSelector="{StaticResource MyTemplateSelector}">               
            <StackPanel Orientation="Horizontal">                
                <TextBlock Text="{Binding Name}" />
            </StackPanel>
        </HierarchicalDataTemplate>
        <HierarchicalDataTemplate x:Key="LeafLargeAreaTemplate" DataType="{x:Type vm:LargeArea}"                                   
                                  ItemTemplateSelector="{StaticResource MyTemplateSelector}">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Name}" />
                <TextBlock Text=" : Size=" />
                <TextBlock Text="{Binding AreaSize}" />
            </StackPanel>
        </HierarchicalDataTemplate>
        <HierarchicalDataTemplate x:Key="LeafSmallAreaTemplate" DataType="{x:Type vm:SmallArea}"                                   
                                  ItemTemplateSelector="{StaticResource MyTemplateSelector}">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Name}" />
                <TextBlock Text=" : Population=" />
                <TextBlock Text="{Binding Population}" />
            </StackPanel>
        </HierarchicalDataTemplate>

        <!-- Style -->
        <Style TargetType="TreeViewItem">
            <Setter Property="IsExpanded" Value="True"/>
        </Style>                
    </Window.Resources>    
    
    <Grid Height="282" Width="230">
        <Button Content="By Area" Height="23" HorizontalAlignment="Left" Margin="20,20,0,0" 
                Name="btnArea" VerticalAlignment="Top" Width="90" Click="btnArea_Click" />
        <Button Content="By Age" Height="23" HorizontalAlignment="Right" Margin="0,20,26,0"
                Name="btnAge" VerticalAlignment="Top" Width="90" Click="btnAge_Click" />

        <TreeView Name="treeView1" HorizontalAlignment="Left" VerticalAlignment="Top" 
                  Margin="20,49,0,0" Width="184" Height="216" 
                  ItemsSource="{Binding Path=MyItems}"  
                  ItemTemplateSelector="{StaticResource MyTemplateSelector}">
        </TreeView>
    </Grid>    
</Window>

Since TreeView only selects one unique data template at a time for any given type, the sample above is using custom data template selector in ItemTemplateSelector. In a code behind file, the following class is defined.

public class MyTemplateSelector : DataTemplateSelector
   { 
      public override DataTemplate SelectTemplate(object item, DependencyObject container) 
      { 
         MethodInfo mi = container.GetType().GetMethod("FindResource") as MethodInfo;         
         if (mi != null)
         {
            if (MainWindow.ViewMode == ViewMode.ByArea)
            {
               switch (item.ToString())
               {
                  case "Tree.LargeArea":
                     return mi.Invoke(container, new object[] { "LargeAreaTemplate" }) as DataTemplate;
                  case "Tree.SmallArea":
                     return mi.Invoke(container, new object[] { "SmallAreaTemplate" }) as DataTemplate;
                  case "Tree.Age":
                     return mi.Invoke(container, new object[] { "LeafAgeTemplate" }) as DataTemplate;
               }
            }
            else
               switch (item.ToString())
               {
                  case "Tree.Age":
                     return mi.Invoke(container, new object[] { "AgeTemplate" }) as DataTemplate;
                  case "Tree.LargeArea":
                     return mi.Invoke(container, new object[] { "LeafLargeAreaTemplate" }) as DataTemplate;
                  case "Tree.SmallArea":
                     return mi.Invoke(container, new object[] { "LeafSmallAreaTemplate" }) as DataTemplate;
               }
         }      
         return null; 
      } 
   }


Basically what it does is to find appropriate data template and return it back to TreeView. If null is returned, TreeView searches for appropriate hierarchical data template for the DataType.