Calendar

<<  November 2017  >>
MonTueWedThuFriSatSun
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

View posts in large calendar

RecentComments

None

 
 
     
 

A different way to display the output from Get-DirAsXml is with Xaml. I will probably occasionally refer back to the previous posts on how to do it using Windows.Forms. So here is step one, displaying the output as a simple TreeView

function Xaml-DisplayDir{
    param([xml]$xml)
    $xaml = @"
<Window
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Xaml-DisplayDir"
    Height="300" Width="600"
    ResizeMode="CanResizeWithGrip"
    WindowStyle="SingleBorderWindow">
   
<Window.Resources>
       
<XmlDataProvider x:Key="DirectoryData" XPath="/DAXContainer/*">
           
<x:XData>
               
<DAXContainer xmlns="">
                    $($xml.SelectSingleNode("/*").OuterXml)

                </DAXContainer>
           
</x:XData>
       
</XmlDataProvider>
       
<HierarchicalDataTemplate x:Key="FolderTemplate" ItemsSource="{Binding XPath=folder}">
           
<StackPanel Orientation="Horizontal">
               
<TextBlock Text="{Binding XPath=@Name}" Margin="2,0,0,0" />
           
</StackPanel>
       
</HierarchicalDataTemplate>
       
<DataTemplate x:Key="FileTemplate">
           
<StackPanel Orientation="Horizontal">
               
<TextBlock Text="{Binding XPath=@Name}" Margin="0,0,0,0" />
           
</StackPanel>
       
</DataTemplate>
   
</Window.Resources>
   
<Grid>
       
<TreeView Margin="0, 0, 0, 0" Name="TREEVIEW" HorizontalAlignment="Stretch" DataContext="{Binding Source={StaticResource DirectoryData}, XPath=/DAXContainer/*}" ItemTemplate="{StaticResource FolderTemplate}" ItemsSource="{Binding}" />
    </Grid>
</Window>

"@
    $RESULT = ""
    $WINDOW = [Windows.Markup.XamlReader]::Load((New-Object System.Xml.XmlNodeReader([xml]$xaml)))
    $TREEVIEW = $WINDOW.FindName("TREEVIEW")
   
    $TREEVIEW.add_SelectedItemChanged({
            $RESULT = $this.Selecteditem.Name
            write-host "SelectedItemChanged"
    })
    [void]$WINDOW.ShowDialog()
    $WINDOW.Close()
    $WINDOW.Dispose()
    $RESULT
}

There are a few things going on here.

  1. The whole thing is a Powershell Mashup script.
  2. It declares a here-string that contains the Xaml markup.
  3. It inserts the Xml into the middle of the Xaml markup. (highlighted)
  4. It creates a Window object from the resulting markup.
  5. It adds a simple event handler to the TREEVIEW object.
  6. Displays the result.

I like to think of it like this. Get-DirAsXml is used to get some Xml, the Xml is docorated or transformed (step 3 above. The Xml is wrapped in Xaml), the resulting Xml is displayed in a window.

transformdiagram1

It can be used like this

PS> . .\Xaml-DisplayDir.ps1 # dot source the script
PS> Xaml-DisplayDir (Get-DirAsXml test)

It might be displayed like this

Xaml-DisplayDir

Here is the code

Xaml-DisplayDir.zip (1.09 kb)

So onto the next step. Add a ListView to display the files. This code will do it

<ListView Name="LISTVIEW" Grid.Column="2" HorizontalAlignment="Stretch" ItemsSource="{Binding Path=SelectedItem, ElementName=TREEVIEW, Mode=OneWay}">
   
<ListView.View>
       
<GridView AllowsColumnReorder="true" ColumnHeaderContainerStyle="{DynamicResource CustomHeaderStyle}">
           
<GridViewColumn Header="Name" CellTemplate="{StaticResource ItemTemplate}" />
           
<GridViewColumn DisplayMemberBinding="{Binding XPath=@Length}" Header="Length" />
           
<GridViewColumn DisplayMemberBinding="{Binding XPath=@LastWriteTime}" Header="Date" />
       
</GridView>
   
</ListView.View>
</ListView>

This adds a ListView with 3 columns Name, Length and Date. The columns will display the data specified in the XPath binding i.e. @Name, @Length, @LastWriteTime. We don't need to do anything like add an event handler to populate and display the items in the list because the ListView.ItemSource attribute is 'bound' to the TREEVIEW.SelectedItem.

We also want to add icons for the folders and files. Rather than using an ImageList and indexing into it like Forms, Xaml is a bit more like html and uses markup to display an image and the text. Like this StackPanel

<DataTemplate xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Key="ItemTemplate">
   
<StackPanel Orientation="Horizontal">
       
<Image Width="16" Height="16" Stretch="Fill" Source="ico2.png" />
       
<TextBlock Text="{Binding XPath=@Name}" Margin="0,0,0,0" />
   
</StackPanel>
</DataTemplate>

It might be displayed like this

Xaml-DisplayDirExp

You will notice that the folder test2 in the listview has the wrong icon. This can be fixed with a Trigger.

<Style TargetType="Image">
   
<Style.Triggers>
       
<DataTrigger Binding="{Binding Path=Name}" Value="folder">
           
<Setter Property="Source" Value="{StaticResource FolderImage}" />
       
</DataTrigger>
       
<DataTrigger Binding="{Binding Path=Name}" Value="file">
           
<Setter Property="Source" Value="{StaticResource FileImage}" />
       
</DataTrigger>
   
</Style.Triggers>
</Style>

I would like to bind the DataTrigger to XPath=local-name() but XPath functions are not supported DOH!

To keep things self contained I have set the Image.Source property to rather badly drawn Xaml icons.

It can be used like this

PS> . .\Xaml-DisplayDirExp.ps1 # dot source the script
PS> Xaml-DisplayDirExp (Get-DirAsXml test -props @{Length="";LastWriteTime=""} )

Here is the code

Xaml-DisplayDirExp.zip (2.22 kb)

Because Xaml is Xml we can process it with Xslt. We can add a little identity transform to our script that has an aditional template to hande the DAXContainer

[xml]$xslt = @'
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
   
<xsl:param name="DAX" />
   
<xsl:template match="node()|@*">
       
<xsl:copy>
           
<xsl:apply-templates select="@*|node()" />
       
</xsl:copy>
   
</xsl:template>
   
<xsl:template match="DAXContainer">
       
<DAXContainer>
           
<xsl:copy-of select="$DAX" />
       
</DAXContainer>
   
</xsl:template>
</xsl:stylesheet>
'@
[xml]$resultxaml = (Invoke-Transform -inxml $xaml -inxsl $xslt -arguments @{'DAX'=$xml})

This is a simple T-DaxDecorate Translet. (Notice that the single quote Here-String from Powershell is used for $xslt. This is to avoid Powershell's symbol substitution that would have translated  <xsl:copy-of select="$DAX" /> into invalid Xslt.)

The Invoke-Transform is taking the Xaml as the input Xml and the motherload xml is passed as a parameter and copied directly to the output. Something like this

transformdiagram2

It can be used like this

PS> . .\Xaml-DisplayDirExpXslt.ps1 # dot source the script
PS> Xaml-DisplayDirExp (Get-DirAsXml test -props @{Length="";LastWriteTime=""} )

Here is the code

 Xaml-DisplayDirExpXslt.zip (1.99 kb)

As in the Forms version we will want to specify the columns that are displayed. So far the Name, Length and LastWriteTime are hard coded in the Xaml Form but we can pass another Xml into the mix that specifies the columns we want. Something like this

PS> Xaml-DisplayDirExp (Get-DirAsXml test -props @{Length=""; LastWriteTime=""} -ExtendedProps @{Title=""; Subject=""; Author=""; Category=""; Keywords=""; Comments=""}) -columns ([xml]"<columns><c n='Length' b='@Length' /><c n='Date' b='@LastWriteTime' /></columns>")

The nice thing about this is that the column name and the columns binding path can be specified.

[xml]$xslt = @'
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xaml="http://schemas.microsoft.com/winfx/2006/xaml/presentation" version="1.0">
   
<xsl:param name="COLUMNS" />
   
<xsl:template match="node()|@*">
       
<xsl:copy>
           
<xsl:apply-templates select="@*|node()" />
       
</xsl:copy>
   
</xsl:template>
   
<xsl:template match="xaml:ListView[@Name='LISTVIEW']/xaml:ListView.View/xaml:GridView">
       
<xsl:copy>
           
<xsl:apply-templates select="@*|node()" />
           
<xsl:apply-templates select="$COLUMNS/columns/c" />
       
</xsl:copy>
   
</xsl:template>
   
<xsl:template match="columns/c">
       
<xaml:GridViewColumn DisplayMemberBinding="{{Binding XPath={@b}}}" Header="{@n}" />
   
</xsl:template>
</xsl:stylesheet>
'@
[xml]$resultxaml = (Invoke-Transform -inxml $xaml -inxsl $xslt -arguments @{COLUMNS=$columns})

This is a simple T-DaxExplorerColumn Translet (Notice that the curly braces in the DisplayMemberBinding are escaped to avoid Xslt AVT substitution.)

We can expand it to specify that we want column justification

PS> Xaml-DisplayDirExp (Get-DirAsXml test -props @{Length=""; LastWriteTime=""} -ExtendedProps @{Title=""; Subject=""; Author=""; Category=""; Keywords=""; Comments=""}) -columns ([xml]"<columns><c n='Length' b='@Length' j='Right' /><c n='Date' b='@LastWriteTime' j='Right' /></columns>")

[xml]$xslt = @'
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xaml="http://schemas.microsoft.com/winfx/2006/xaml/presentation" version="1.0">
   
<xsl:param name="COLUMNS" />
   
<xsl:template match="node()|@*">
       
<xsl:copy>
           
<xsl:apply-templates select="@*|node()" />
       
</xsl:copy>
   
</xsl:template>
   
<xsl:template match="xaml:ListView[@Name='LISTVIEW']/xaml:ListView.View/xaml:GridView">
       
<xsl:copy>
           
<xsl:apply-templates select="@*|node()" />
           
<xsl:apply-templates select="$COLUMNS/columns/c" />
       
</xsl:copy>
   
</xsl:template>
   
<xsl:template match="columns/c">
       
<xaml:GridViewColumn Header="{@n}">
           
<xaml:GridViewColumn.CellTemplate>
               
<xaml:DataTemplate>
                   
<xaml:TextBlock Text="{{Binding XPath={@b}}}"
                            TextAlignment="{@j}" />
               
</xaml:DataTemplate>
           
</xaml:GridViewColumn.CellTemplate>
       
</xaml:GridViewColumn>
   
</xsl:template>
</xsl:stylesheet>
'@
[xml]$resultxaml = (Invoke-Transform -inxml $xaml -inxsl $xslt -arguments @{COLUMNS=$columns})

This is a simple T-DaxExplorerColumnWithJustify Translet

For this to work properly we need to add a Style to the ListViewItem that will contain the TextBlock. It needs to stretch to fill the grid cell. This style can go into a Resources section that is in scope. I put it into the Window.Resources section.

<Style TargetType="{x:Type ListViewItem}">
   
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>

It might be displayed like this.

Xaml-DisplayDirExpXsltColumns

Here is the code

 Xaml-DisplayDirExpXsltColumns.zip (2.21 kb)

Adding a ToolTip to items in the ListView is easy. We just have to add a ToolTip property to the ItemTemplate StackPanel and bind it to the InfoTip attribute.

<StackPanel.ToolTip>
   
<TextBlock Text="{Binding XPath=@InfoTip}" />
</StackPanel.ToolTip>

Xaml-DisplayDirExpToolTip

Adding XPath searching seems simple at first. Add a Button and a TextBox to the last Row of the Grid

<Grid Grid.Row="2" Grid.ColumnSpan="3" Background="Aquamarine">
   
<Grid.ColumnDefinitions>
       
<ColumnDefinition Width="100" />
       
<ColumnDefinition Width="*" />
   
</Grid.ColumnDefinitions>
   
<Button Name="SEARCHBUTTON" Content="Search" Width="100" />
   
<TextBox Name="SEARCHTEXT" Grid.Column="2" HorizontalAlignment="Stretch" Width="Auto" />
</Grid>

Then add a Click event to the Button and set the result of the XPath expression to the ItemsSource propert of the ListView. This works but it wrecks the binding of the ListView and further clicking on the TreeView doesn't do anything obviously. I tried a few things but in the end I decided on a 2 ListView approach. One is visible when the search button is clicked and the other when the treeview is clicked. It isn't perfect but it works until I can find a way of recreating the binding.

It might display like this

Xaml-DisplayDirExpSearch

Here is the code

 Xaml-DisplayDirExpXsltSearch.zip (2.75 kb)

Now that we have another control on the form it is worth applying some styling. We can add say a gradient to the button.

<Style TargetType="{x:Type Button}">
   
<Setter Property="Background">
       
<Setter.Value>
           
<LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
               
<GradientStop Color="#FF0000FF" Offset="0" />
               
<GradientStop Color="#FFFFFFFF" Offset="0.338" />
               
<GradientStop Color="#FFFFFFFF" Offset="0.716" />
               
<GradientStop Color="#FF0000FF" Offset="1" />
           
</LinearGradientBrush>
       
</Setter.Value>
   
</Setter>
</Style>

Styles can get very big very quickly and a way of keeping them seperate is to include them like the columns and DAXml by doing a merge in Xslt

 

[xml]$xslt = @'
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xaml="http://schemas.microsoft.com/winfx/2006/xaml/presentation" version="1.0">
   
<xsl:param name="STYLE" />
   
<xsl:template match="node()|@*">
       
<xsl:copy>
           
<xsl:apply-templates select="@*|node()" />
       
</xsl:copy>
   
</xsl:template>
   
<xsl:template match="xaml:Window.Resources">
       
<xsl:copy>
           
<xsl:apply-templates select="@*|node()" />
           
<xsl:apply-templates select="$STYLE/*/*" />
       
</xsl:copy>
   
</xsl:template>
</xsl:stylesheet>
'@
[xml]$resultxaml = (Invoke-Transform -inxml $xaml -inxsl $xslt -arguments @{STYLE=$style})

This is a simple T-DaxExplorerStyler Translet

It might be displayed like this

Xaml-DisplayDirExpSearchStyle

or

Xaml-DisplayDirExpSearchStyle3  

Here is the code

Xaml-DisplayDirExpXsltStyle.zip (2.81 kb) 

Adding Drag'n'Drop is very similar to the Forms version except there is no DragStart event and the object model is slightly different but the code is very similar. I added a third pane like before that contains a TreeView. The drag rule is simple, items can be dragged from the ListView to the right-hand TreeView. The Template for the TreeView is modified to display folders and files. The resulting Xml is put into the pipeline when the form closes.

It might be displayed like this (without cursor)

Xaml-DisplayDirExpDragDrop

Here is the code

Xaml-DisplayDirExpDragDrop.zip (3.44 kb)

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Add comment