Calendar

<<  August 2017  >>
MonTueWedThuFriSatSun
31123456
78910111213
14151617181920
21222324252627
28293031123
45678910

View posts in large calendar

RecentComments

None

 
 
     
 
I recently spotted Standalone XQuery Implementation in .NET on Codeplex and decided to integrate it with DAXml explorer. I'm going to start with a previous post and add a few things. A ToolBar to switch to XQuery search and run the XQuery <ToolBarTray Background="White" Grid.Row="0">     <ToolBar Band="1" BandIndex="0">         <Button Name="SEARCHTOOL">             <TextBlock>Search</TextBlock>         </Button>         <Button Name="RUNTOOL">             <TextBlock>Run</TextBlock>         </Button>     </ToolBar>     <ToolBar Band="1" BandIndex="1" Visibility="{Binding ElementName=TABCONTROL, Path=Visibility}">         <TextBlock FontSize="20"> A </TextBlock>         <Slider Name="TEXTSIZE" Maximum="50" Width="100" Value="12" Orientation="Horizontal" HorizontalAlignment="Left" AutoToolTipPlacement="BottomRight" AutoToolTipPrecision="2" />     </ToolBar> </ToolBarTray> and a TabControl to switch between the XQuery and the results <TabControl Name="TABCONTROL" Grid.Column="2" Grid.Row="1" Visibility="Hidden">     <TabItem Name="TABXQUERY" Header="XQuery">         <TabItem.Content>             <TextBox Name="TABQUERYSOURCE" AcceptsReturn="True" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" FontSize="{Binding ElementName=TEXTSIZE, Path=Value}" />         </TabItem.Content>     </TabItem>     <TabItem Name="XQUERYRESULT" Header="Result">         <TabItem.Content>             <ListView Name="LISTVIEW2" Grid.Column="2" Grid.Row="1" HorizontalAlignment="Stretch" Visibility="Visible">                 <ListView.View>                     <GridView AllowsColumnReorder="true" ColumnHeaderContainerStyle="{DynamicResource CustomHeaderStyle}">                         <GridViewColumn Header="Name" CellTemplate="{StaticResource ItemTemplate}">                         </GridViewColumn>                     </GridView>                 </ListView.View>             </ListView>         </TabItem.Content>     </TabItem> </TabControl> We need a couple of events wired up $SEARCHTOOL.add_Click({     if ($TABCONTROL.Visibility -eq [System.Windows.Visibility]::Visible){         $TABCONTROL.Visibility = [System.Windows.Visibility]::Hidden         $LISTVIEW.Visibility = [System.Windows.Visibility]::Visible     }else{         $TABCONTROL.Visibility = [System.Windows.Visibility]::Visible         $LISTVIEW.Visibility = [System.Windows.Visibility]::Hidden     } }) that will switch from normal to search view. And an event to do the XQuery on the DAXml $RUNTOOL.add_Click({     Add-Type -path "D:\QueryMachine.XQuery.1.3\QueryMachine.XQuery.1.3\QueryMachine.XQuery.dll"     $xqresult = [DataEngine.XQuery.XPathFactory]::QueryNodes($xml, $TABQUERYSOURCE.Text)     $LISTVIEW2.ItemsSource = $xqresult     $TABXQUERYRESULT.IsSelected = $true }) This might be displayed like this Normal explorer view. XQuery search view. XQuery result view. It can be used like this Xaml-DisplayDirExp (get-dirasxml ..\test -props @{Length="";LastWriteTime="";Extension="";FullName=""} ) or like this Xaml-DisplayDirExp (get-dirasxml ..\test -CustomProps {     param ($element, $directory, $file, $prefix, $namespace)     try{         [xml]$xmp = (pslib:\xml\ReadingXMP.exe $file.FullName)         $xmpr = $xmp.SelectSingleNode("/*")         $inode = $element.OwnerDocument.ImportNode($xmpr, $true)         [void]$element.AppendChild($inode)     }catch{} } -props @{Length="";LastWriteTime="";Extension="";FullName=""} ) which will use the ReadingXMP from this previous post to stuff XMP RDF from JPG files into the DAXml. Depending on which method you use will depend on the XQuery you use. With DAXml stuffing you can use an XQuery like this declare namespace MPReg="http://ns.microsoft.com/photo/1.2/t/Region#"; for $a in //file[@Extension=".jpg"][//MPReg:PersonDisplayName = "Chris Bayes"] order by $a/@Length return $a and if you use sidecar files then you can use XQuery like this declare namespace MPReg="http://ns.microsoft.com/photo/1.2/t/Region#"; for $a in //file[@Extension=".jpg"] where doc(concat(@FullName, ".xmp"))//MPReg:PersonDisplayName = "Chris Bayes" order by $a/@Length return $a either way the XQuery will return all JPG files in all your folders where 'Chris Bayes' is in the MPReg:PersonDisplayName of the XMP RDF ordered by the file size. If you used order by $a//exif:GPSLatitude you would get all JPG files in all your folders where 'Chris Bayes' is in the MPReg:PersonDisplayName of the XMP RDF ordered by the e/w distance from the Greenwich Meridian. Here is the code Xaml-DisplayDirExpXQuerySearch.zip
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. The whole thing is a Powershell Mashup script. It declares a here-string that contains the Xaml markup. It inserts the Xml into the middle of the Xaml markup. (highlighted) It creates a Window object from the resulting markup. It adds a simple event handler to the TREEVIEW object. 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. It can be used like this PS> . .\Xaml-DisplayDir.ps1 # dot source the scriptPS> Xaml-DisplayDir (Get-DirAsXml test) It might be displayed like this 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 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 scriptPS> 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 It can be used like this PS> . .\Xaml-DisplayDirExpXslt.ps1 # dot source the scriptPS> 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. 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> 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 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 or    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) Here is the code Xaml-DisplayDirExpDragDrop.zip (3.44 kb)