Skip to main content
Email Password   helpLost your password?
Click Here to Goto my Blog Home

Targeting visual studio 2008; wpf, xbap, silverlight

Recently I had a business need to hide specific XAML elements based off of whether an xml node was present in a bound XML Document. Note though that I did not want to hide the element when I was formatting it in Design Mode. I guess I’m just particular that way, I kind of like to see what it is I’m working on.

It took me a few hours to figure out all the particulars so I thought I’d share the results of my labor, and perhaps save you all a few gray hairs.

The basic premise is that I load an XML Document into an XMLDataProvider resource at run-time. I then bind a particular TextBlock’s visibility property to a node in the XML. To accomplish my goal of hiding or showing the TextBlock if applicable I send the node’s value off to a ValueConverter. If the converter receives a value it returns Visibility.Visible, if it does not then it returns Visibility.Collapsed. Note that I could have just as easily returned Visibility.Hidden if I wanted to format my elements in order to accomplish the “missing tooth” effect.

You’ll see in my code that I implemented the multi value version of the value converter, namely IMultiValueConverter. Note that I tried the more rudimentary IValueConverter first. The issue I found, though, is that WPF will not send a DependencyProperty.UnsetValue to a single value converter the way it will to a multi value converter.

In the end I reasoned that a Multi Value converter is better anyway, as it will allow me to hide an element if any one of any given array of XML nodes is missing. I guess, as in chess, I’m always trying to think a few moves ahead. If in the future a business need presents itself to bind to an array of nodes, and then hide the element if any one of them is missing, the code will accommodate that now as well.

To make sure that I can see the element when I am working on it in the designer, I added an IsInDesignMode shared function in the Application class. It’s nice to have it in the Application class so that I can call it from whatever page or class I may be working in whenever I need it. The function uses .net’s GetIsInDesignMode function on a new DependencyObject. The DependencyObject will instantiate differently in design mode than it will in runtime.

So without further ado, here is our code example demonstrating the technique explained above.

1) Create a new WPF XBAP application called WpfBrowserApplication1.

2) Now add the IsInDesignMode function to the application class:

Class Application

 ' Application events, such as Startup(), Exit(), and DispatcherUnhandledException
 ' can be handled in this file

    Public Shared Function IsInDesignMode() As Boolean
        Return ComponentModel.DesignerProperties.GetIsInDesignMode(New DependencyObject())
    End Function

End Class

3) Add a multi value converter that will loop through an array of values given it and send a Visibility.Collapsed if it receives any null values:

Public Class NodesMissingToHidden
    Implements IMultiValueConverter

    Public Function Convert(ByVal values() As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IMultiValueConverter.Convert

        Dim ret As System.Windows.Visibility = Visibility.Visible

        'If we are in design mode then we want to show the element no matter what
        If Application.IsInDesignMode = True Then
            Return ret
        End If

        'loop through the values, if any value is missing in the array then set Visibility.Hidden
        For Each o In values
            If o Is Nothing OrElse o.Equals(DependencyProperty.UnsetValue) Then
                ret = Visibility.Collapsed
            End If
        Next

        Return ret
    End Function

    Public Function ConvertBack(ByVal value As Object, ByVal targetTypes() As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object() Implements System.Windows.Data.IMultiValueConverter.ConvertBack
        Throw New NotImplementedException()
    End Function
End Class

4) Alter the default Page1 accordingly with a grid and a stack panel on it. Note that page must reference the default clr-namespace. The Grid.Resources contain an XMLDataProvider to be loaded during runtime, and a reference to the converter class. Inside the stack panel you will bind three TextBlock’s visbility property to an xpath that either exists or does not exist. If the xml node does not exist WPF will send a DependencyProperty.UnsetValue to the converter, which will in turn return Visibility.Collapsed.

<Page x:Class="Page1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:z="clr-namespace:WpfBrowserApplication1"
    Title="Page1">
    <Grid Name="MyGrid">
                     
        <Grid.Resources>
            <XmlDataProvider x:Key="MyXML"  />
            <z:NodesMissingToHidden x:Key="NodesMissingToHidden" />
        </Grid.Resources>

        <StackPanel>
            <TextBlock >
                <TextBlock.Visibility> 
                    <MultiBinding Converter="{StaticResource NodesMissingToHidden}" >
                        <Binding Source="{StaticResource MyXML}" XPath="Movies/Movie[@Name='Star Wars']" />
                    </MultiBinding>
                </TextBlock.Visibility>
                Click here to goto <Hyperlink NavigateUri="http://www.starwars.com" Foreground="Blue">Star Wars</Hyperlink>
            </TextBlock>

            <!--Note this movie is not in the XML so the link should NOT show in RUNTIME, yet it should in DESIGN time-->
            <TextBlock >
                <TextBlock.Visibility> 
                    <MultiBinding Converter="{StaticResource NodesMissingToHidden}" >
                        <Binding Source="{StaticResource MyXML}" XPath="Movies/Movie[@Name='The Sound of Music']" />
                    </MultiBinding>
                </TextBlock.Visibility>
                Click here to goto <Hyperlink NavigateUri="http://www.soundofmusic.com" Foreground="Blue">The Sound of Music</Hyperlink>
            </TextBlock>

            <TextBlock >
                <TextBlock.Visibility> 
                    <MultiBinding Converter="{StaticResource NodesMissingToHidden}" >
                        <Binding Source="{StaticResource MyXML}" XPath="Movies/Movie[@Name='Spiderman']" />
                    </MultiBinding>
                </TextBlock.Visibility>
                Click here to goto <Hyperlink NavigateUri="http://www.spiderman.com" Foreground="Blue">Spiderman</Hyperlink>
            </TextBlock>
        </StackPanel>
        
    </Grid>
</Page>

5) Lastly let’s load some XML into the XMLDataProvider at run time:

Class Page1 

    Private Sub Page1_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded

        Dim xml As New System.Xml.XmlDocument()
        xml.LoadXml("<Movies><Movie Name='Star Wars' /><Movie Name='Superman' /><Movie Name='Spiderman' /></Movies>")

        Dim dp As XmlDataProvider
        Dim g As Grid = Me.MyGrid

        dp = CType(g.Resources("MyXML"), XmlDataProvider)
        dp.Document = Xml

    End Sub

End Class
You must Sign In to use this message board.
 
 
Per page   
  
-- There are no messages in this forum --


Last Updated 17 Jun 2009 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2009