Taoffi's blog

prisonniers du temps

Xamarin forms backyard - exploring MSBuild projects

In a previous post, I talked about how to manually change the project (.csproj) file to create an App.xaml and App.xaml.cs files in a Xamarin forms project. The reason for this is to be able to declare and use global application resources (styles, assets… etc.) linked to the main application file (App.cs changed to App.xaml.cs). That is, in a way, to repair a guilty XF project template that doesn't create these items by default.

Like in all development processes, when you use xamarin forms, you spend your time coding and building your projects.

In Windows, this essential build process goes through MSBuild which reads and interprets .csproj file's instructions into specific actions to compile and package your applications.

Those .csproj files are XML files containing configurations, variables and instructions… many can be manipulated in Visual Studio or Xamarin Studio. Still, some of these configurations require a deeper dive into MSBuild details. Your project, for instance, may sometimes fail to build for reasons that are difficult to trace without such dive.

Diving into MSBuild is something different from simply reading the .csproj xml instructions.

Understanding the way these instructions would be interpreted and handled by MSBuild is crucial for your investigations success.

MSBuild API to the rescue

Fortunately, MSBuild offers an API that allows you to explore or modify the build engine behavior according to your requirements.

I used this API to build a simple MSBuild browser (Windows WPF application) that allows you to examine the detailed interpretations of MSBuild to a .csproj or .vbproj file.

A project configuration browser

The browser uses the objects defined in the MSBuild Microsoft.Build.Evaluation namespace.

Three main view models of the application are used to represent MSBuild objects:

  • iProject: msbuild project view model. Encapsulates the collection of:
  • iProjectItemType: project item types (and actions). Each project item type encapsulating a collection of:
  • iProjectItem: an item of the related type. This object encapsulates a property bag of the related MSBuild item's properties (For instance: metadata).

 

PropertyBag object is used to allow future application extensibility. It parses MSBuild objects' properties independently of the version referenced (the current application references MSBuild 14.0.0.0).

 

The result looks like this:

 

You can download the binaries Here

If you are interested in extending features, you can download the source code here.

 

Choices in a logical tree view – WPF sample

Using check boxes in TreeView control is a handy way for presenting choices in their logical tree-like structure.

In real life, though, choices can be a mix of inclusive (check-box) and exclusive (radio-button) options.

I expose here a solution for using that mix of option types in one same tree view.

The problem

The problem is divided into three main subjects:

  • How to use a mix of checkbox / radio button nodes in the same tree view control
  • How to get a radio button to be toggled from checked to unchecked status: Checkboxes are, 'naturally', able to be toggled from checked to unchecked status. This is not the case for Radio buttons. The result is that when you use radio button in a tree view, you will be able to check it but not to get it uncheck!
  • How to handle exclusive choices selection. That is when an exclusive option gets selected (checked), for instance,we must unselect all other exclusive sibling options.

 

To solve the first question, we will use:

  • A tree node object which indicates its option type (exclusive / inclusive)
  • Hierarchical control templates for each choice type
  • A template selector which will select the correct template according to the node object choice type

 

To solve the second, we will simply create a new Toggled Radio Button (which derives from RadioButton) and get this new object handle the Click event to toggle its selection status.

 

 

public class RadioToggleButton : RadioButton
{
    protected override void OnClick()
    {
        IsChecked = !IsChecked;
    }
}

 

 

The third question will be solved by implementing the required behaviors within our special tree node object.

 

The TreeNode object

The TreeNode object exposes few properties:

  • A Title
  • A Parent node (TreeNode)
  • A list of Children (List of TreeNode items)
  • A boolean flag which indicates if the node represents an exclusive choice option
  • A boolean flag which indicates if the node is selected

Through these properties, TreeNode object can expose other properties like its Root node, the First exclusive parent or descendant… etc.

 

The TreeNode Hierarchical data template

 

<UserControl.Resources>
…
…
    <!-- hierarchical template for checkbox treeview items -->
    <HierarchicalDataTemplate x:Key="checkBoxTemplate" 
        DataType="{x:Type app:TreeNode}"
        ItemsSource="{Binding Children}">
        <StackPanel Orientation="Horizontal">
            <CheckBox Focusable="False"
                VerticalAlignment="Center"
                IsChecked="{Binding IsSelected, Mode=TwoWay}" />
            <TextBlock Text="{Binding Title}" />
        </StackPanel>
    </HierarchicalDataTemplate>
 
    <!-- hierarchical template for (toggled) radio buttons treeview items -->
    <HierarchicalDataTemplate x:Key="radioButtonTemplate" 
            DataType="{x:Type app:TreeNode}"
            ItemsSource="{Binding Children}">
        <StackPanel Orientation="Horizontal">
            <ctrl:RadioToggleButton Focusable="False"
            VerticalAlignment="Center"
            IsChecked="{Binding IsSelected, Mode=TwoWay}" />
            <TextBlock Text="{Binding Title}" Margin="4, 1, 0, 0" />
        </StackPanel>
    </HierarchicalDataTemplate>
</UserControl.Resources>

 

 

The TreeView node's Item template selector

 

public class TreeNodeXTemplateSelector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(
                object item, DependencyObject container)
    {
        FrameworkElement    element = container    as FrameworkElement;
        TreeNode    node    = item  as TreeNode;
 
        if (element != null && node != null)
        {
            if (node.IsExclusive)
                return element.FindResource("radioButtonTemplate")
                                 as HierarchicalDataTemplate;
            return element.FindResource("checkBoxTemplate")
                                as HierarchicalDataTemplate;
        }
        return null;
    }
}

 

 

We can now use an ItemTemplateSelector to tell the Tree view control to select the adequate data template for each item according the tree node choice selection type (exclusive / inclusive)

 

<UserControl.Resources>
    <app:TreeNodeXTemplateSelector	x:Key="templateSelector" />
    …
    …
</UserControl.Resources>

 

<TreeView x:Name="treeview1" ItemsSource="{Binding Root.Children}"
                  ItemTemplateSelector="{StaticResource templateSelector}"/>

 

 

Exclusive node selection behavior

TreeNode selection behavior can be summarized as follows:

  • If the node is inclusive: do nothing (just set the selected flag)
  • If the node is exclusive (and selected):
    • Unselect all exclusive siblings (siblings = Parent's Children)
    • Select all inclusive child nodes
    • Select the first exclusive child node if any

 

public void UpdateSelection()
{
    if(! _isExclusive)
        return;
 
    if(_isSelected == true)
    {
        UnSelectSiblings();
    }
 
    SelectChildren(_isSelected);
}

 

 

 

protected void SelectChildren(bool selected)
{
    if(! selected)
    {
        UnSelectChildren();
        return;
    }
 
    TreeNode firstEx = FirstExclusiveChild;
 
    if(firstEx != null)
        firstEx.IsSelected = selected;
 
    foreach(TreeNode node in _children)
    {
        if(node.IsExclusive)
            continue;
 
        node.SetSelection(value: selected, updateChildren: true);
    }
}

 

 

Sample screenshot

 

Download the sample code TreeViewRadioAndCheckButtons.zip (67.88 kb)

Object Visibility and learning cycle… just a vision of

In everyday work, we come through new tools or new versions of tools we knew before.

The cycle of playing with and manipulating these tools’ objects to attain a reasonable level of mastering… is often daunting!

To minimize the learning cycle of tools, some helpful features have been introduced: like documentation, tooltips… etc.

Documentation is of course important, but, for me, tooltips are much more helpful. They just appear when I need them in the context of using the object or the property.

The thing is: when you manipulate an object for the first time, you really know few things about its composition and, even less, about its role in the global mechanics of the tool in question. You just know it is there, and it should be there for some reason and that you should spend some time to understand: its role, its structure, and finally how can it be useful for you (if at all it could be!)

One way to shorten this learning cycle is to let others show you how to use the tool and its object. Quite useful, but, on one hand, this occults some side of self-experience (important)… and also negatively interferes in your critical view of the tool (which is often useful for the tool’s enhancement itself)

What we do to know about (most) simple toys seems more rational: You just set something on or off (left/right or up/down…)… then put the toy on work and see... after some cycles you end up by figuring out what is the ‘best’ position for your needs (or mood!).

Another representation, which may also give an interesting slant related to this subject, is the DeepZoom technology used in maps applications where you can first see the whole world map, and then, zooming-in on the map you get more details about a given country, city, streets, buildings… etc.

A good path for reducing the time and effort needed to learn a subject or a tool would be:

  • To be able to see (explore) the global image of the subject or tool’s structure in action;
  • Be able to zoom-in on its objects and see their properties (progressively detailed according to your zoom level);
  • Be able to change the value of a give object’ property… and perceive the impact of the change on the global behavior.

This proposed path cannot of course be applied in all situations or contexts, but can be useful in many (most) cases.

We may, for instance, need to create a simulation context inside which we can ‘run’ the specific tool or object. An approach which may also be useful for product tests and benchmarking.

Some interesting works have been done on some aspects (like The Property Grid project)… More is to be done on the visualization of objects and properties by zoom level. Will try to write a sample on this in a future post.

 

WPF and a ComboBox dilemma (again!)

Sometime ago, I discussed a ComboBox mouse scroll issue in Silverlight. I, again, had a new problem with the ComboBox control… this time in a WPF application.

The ItemsSource update problem

In my case, I had a ComboBox whose ItemsSource was bound to a list of items which was updated dynamically, and its SelectedItem bound to one item of the list. The problem was that the ComboBox was correctly updating the SelectedItem, while the drop-down list continued to display the older list of items! ... Here is a sample illustration:

 

Figure 1: This is the first context:

 

Figure 2: The item list changed… the ComboBox correctly displays the selected item (2.476563)… which doesn’t exist at all in the items list. The drop-down list still displays the old items list!

 

Such odd situations make you feel that either there should be an error somewhere in your code… or that the CombBox itself is a control that you should reinvent yourself!

 

Check your code!

Here is my Xaml code… that really seems so ‘standard’


<ComboBox x:Name="combobox_rates"
    ItemsSource="{Binding Combinations, Mode=OneWay}"
    SelectionChanged="combobox_rates_SelectionChanged"
    Height="24"
    Width="280"
    ItemTemplate="{DynamicResource combi_list_template}"
    SelectedItem="{Binding Path=SelectedCombination}" />

According to someone advice, let-s add a IsSynchronizedWithCurrentItem=”True”:


<ComboBox x:Name="combobox_rates"
    IsSynchronizedWithCurrentItem="True"
    

Desperately, that doesn’t solve the problem!
Another advice: Bind the ItemsSource to an ObservableCollection<T> (instead of a List<T>): nothing changes…
A third one: add an UpdateSourceTrigger=PropertyChanged to the Binding… let’s just try

ItemsSource="{Binding Combinations, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"

 

The solution

Still, we didn’t try this… let’s use the IsAsync Binding attribute:

ItemsSource="{Binding Combinations, IsAsync=True, Mode=OneWay}"

Oh… that seems to work great!
Is sofware development a Science, an Art, or Craftsmanship?!