Click here to Skip to main content
15,867,756 members
Articles / Desktop Programming / WPF

ObjectPresenter - How to Generate an Object's Testing GUI from a Given Object

Rate me:
Please Sign up or sign in to vote.
4.94/5 (27 votes)
5 Jan 2012CPOL11 min read 37K   924   49  
In this article, I explain step by step, how we can create a WPF custom control that gets an object and, generates a GUI that enables editing that object's properties and invoking that object's methods.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:ObjectPresentation">
    
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="/ObjectPresentation;component/Styles/ButtonsStyles.xaml" />
    </ResourceDictionary.MergedDictionaries>

    <DataTemplate x:Key="methodResultDataTemplate">
        <DataTemplate.Resources>
            <Storyboard x:Key="showOutputs">
                <DoubleAnimation Storyboard.TargetName="outputs"
                                 Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).(ScaleTransform.ScaleX)"
                                 To="1"
                                 Duration="0:0:0.2" />
                <DoubleAnimation Storyboard.TargetName="outputs"
                                 Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).(ScaleTransform.ScaleY)"
                                 To="1"
                                 Duration="0:0:0.2" />
            </Storyboard>
            <Storyboard x:Key="hideOutputs">
                <DoubleAnimation Storyboard.TargetName="outputs"
                                 Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).(ScaleTransform.ScaleX)"
                                 To="0"
                                 Duration="0:0:0.2" />
                <DoubleAnimation Storyboard.TargetName="outputs"
                                 Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).(ScaleTransform.ScaleY)"
                                 To="0"
                                 Duration="0:0:0.2" />
            </Storyboard>
        </DataTemplate.Resources>
        <StackPanel>
            <TextBlock Text="Current result:"
                       Foreground="DarkGreen" 
                       FontSize="14"
                       FontWeight="Bold"
                       Margin="0,0,0,5"/>                
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="Method's name:" Margin="0,0,5,0" Foreground="DarkGreen" />
                <TextBlock Text="{Binding MethodName}" Foreground="DarkBlue" />
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="Result's time:" Margin="0,0,5,0" Foreground="DarkGreen" />
                <TextBlock Text="{Binding ResultTime}" Foreground="Blue" />
            </StackPanel>
            <StackPanel x:Name="returnValueRegion"
                        Orientation="Horizontal"
                        Visibility="Collapsed">
                <TextBlock Text="Return value:" Margin="0,0,5,0" Foreground="DarkGreen" />
                <ScrollViewer VerticalScrollBarVisibility="Disabled"
                                              HorizontalScrollBarVisibility="Auto">
                    <ContentControl Content="{Binding MethodReturnValue}" />
                </ScrollViewer>
            </StackPanel>
            <StackPanel x:Name="outputsRegion"
                        Orientation="Horizontal"
                        Visibility="Collapsed">
                <TextBlock Text="Outputs:" Margin="0,0,5,0" Foreground="DarkGreen" />
                <StackPanel>
                    <ToggleButton x:Name="toggleOutputs"
                                  HorizontalAlignment="Left"
                                  Style="{StaticResource expandButtonStyle}"
                                  ToolTip="Expand outputs."
                                      IsChecked="True" />
                    <ScrollViewer x:Name="outputs"
                                  Margin="15,0,0,0"
                                  VerticalScrollBarVisibility="Disabled"
                                              HorizontalScrollBarVisibility="Auto">
                        <ScrollViewer.LayoutTransform>
                            <ScaleTransform ScaleX="0" ScaleY="0" />
                        </ScrollViewer.LayoutTransform>
                        <ItemsControl ItemsSource="{Binding MethodOutputs}" />
                    </ScrollViewer>
                </StackPanel>
            </StackPanel>
            <StackPanel x:Name="exceptionRegion"
                        Orientation="Horizontal"
                        Visibility="Collapsed">
                <TextBlock Text="Exception:" Margin="0,0,5,0" Foreground="DarkGreen" />
                <ScrollViewer VerticalScrollBarVisibility="Disabled"
                                              HorizontalScrollBarVisibility="Auto">
                    <ContentControl Content="{Binding MethodException}" />
                </ScrollViewer>
            </StackPanel>
        </StackPanel>
        <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding HasReturnValue}"
                         Value="True">
                <Setter TargetName="returnValueRegion"
                        Property="Visibility"
                        Value="Visible" />
            </DataTrigger>
            <DataTrigger Binding="{Binding HasOutputs}"
                         Value="True">
                <Setter TargetName="outputsRegion"
                        Property="Visibility"
                        Value="Visible" />
            </DataTrigger>
            <DataTrigger Binding="{Binding HasException}"
                         Value="True">
                <Setter TargetName="exceptionRegion"
                        Property="Visibility"
                        Value="Visible" />
            </DataTrigger>
            <Trigger SourceName="toggleOutputs"
                         Property="IsChecked"
                         Value="True">
                <Setter TargetName="toggleOutputs"
                        Property="ToolTip"
                        Value="Collapse outputs." />
                <Trigger.EnterActions>
                    <BeginStoryboard Storyboard="{StaticResource showOutputs}"/>
                </Trigger.EnterActions>
                <Trigger.ExitActions>
                    <BeginStoryboard Storyboard="{StaticResource hideOutputs}" />
                </Trigger.ExitActions>
            </Trigger>

        </DataTemplate.Triggers>
    </DataTemplate>
    
    <Style TargetType="{x:Type local:InterfacePresenter}">
        <Setter Property="Background" Value="LightYellow" />
        <Setter Property="BorderBrush" Value="Orange" />
        <Setter Property="BorderThickness" Value="2" />
        <Setter Property="Padding" Value="10" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:InterfacePresenter}">
                    <ControlTemplate.Resources>
                        <Storyboard x:Key="currentMethodResultRegionChangedStoryboard">
                            <ColorAnimationUsingKeyFrames Storyboard.TargetName="currentMethodResultRegion"
                                                           Storyboard.TargetProperty="(Control.BorderBrush).(SolidColorBrush.Color)">
                                <SplineColorKeyFrame  KeyTime="0:0:0.3"
                                                      KeySpline="0.5,0 0.5,1"
                                                      Value="DarkOrange" />
                                <SplineColorKeyFrame  KeyTime="0:0:0.6"
                                                      KeySpline="0.5,0 0.5,1"
                                                      Value="Orange" />
                            </ColorAnimationUsingKeyFrames>
                            <ThicknessAnimationUsingKeyFrames Storyboard.TargetName="currentMethodResultRegion"
                                                           Storyboard.TargetProperty="BorderThickness">
                                <SplineThicknessKeyFrame  KeyTime="0:0:0.3"
                                                      KeySpline="0.5,0 0.5,1"
                                                      Value="4" />
                                <SplineThicknessKeyFrame  KeyTime="0:0:0.6"
                                                      KeySpline="0.5,0 0.5,1"
                                                      Value="2" />
                            </ThicknessAnimationUsingKeyFrames>
                        </Storyboard>
                    </ControlTemplate.Resources>
                    <DockPanel>
                        <Border x:Name="currentMethodResultRegion"
                                DockPanel.Dock="Bottom"
                                Background="{TemplateBinding Background}" 
                                BorderThickness="{TemplateBinding BorderThickness}"
                                CornerRadius="15"
                                Padding="{TemplateBinding Padding}"
                                Margin="5"
                                RenderTransformOrigin="0.5,0.5">
                            <Border.BorderBrush>
                                <SolidColorBrush Color="Orange" />
                            </Border.BorderBrush>
                            <Border.RenderTransform>
                                <ScaleTransform />
                            </Border.RenderTransform>
                            <ScrollViewer HorizontalScrollBarVisibility="Auto"
                                          VerticalScrollBarVisibility="Auto">
                                <ContentControl Content="{Binding CurrentMethodResult, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                                            ContentTemplate="{StaticResource methodResultDataTemplate}" />
                            </ScrollViewer>
                        </Border>

                        <Border BorderBrush="{TemplateBinding BorderBrush}" 
                                BorderThickness="{TemplateBinding BorderThickness}"
                                Background="{TemplateBinding Background}"
                                CornerRadius="15"
                                Padding="{TemplateBinding Padding}"
                                Margin="5">
                            <Grid>
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto" />
                                    <RowDefinition />
                                </Grid.RowDefinitions>
                                <TextBlock Text="Methods:"
                                           Foreground="DarkGreen"
                                           FontSize="14"
                                           FontWeight="Bold"
                                           Margin="0,0,0,5" />
                                <ScrollViewer Grid.Row="1"
                                              VerticalScrollBarVisibility="Auto"
                                              Margin="0,5,0,0">
                                    <ItemsControl ItemsSource="{Binding InterfaceMethods, RelativeSource={RelativeSource Mode=TemplatedParent}}"  >
                                        <ItemsControl.ItemsPanel>
                                            <ItemsPanelTemplate>
                                                <WrapPanel />
                                            </ItemsPanelTemplate>
                                        </ItemsControl.ItemsPanel>
                                        <ItemsControl.ItemTemplate>
                                            <DataTemplate>
                                                <local:MethodPresenter MethodInformation="{Binding}"
                                                                       ObjectInstance="{Binding DataContext.ObjectInstance, ElementName=templatedParentHolder}"
                                                                       StoreMethodResults="{Binding DataContext.StoreMethodResults, ElementName=templatedParentHolder}"
                                                                       KnownTypes="{Binding DataContext.KnownTypes, ElementName=templatedParentHolder}"
                                                                       AutoGenerateCompatibleTypes="{Binding DataContext.AutoGenerateCompatibleTypes, ElementName=templatedParentHolder}"
                                                                       DataTemplates="{Binding DataContext.DataTemplates, ElementName=templatedParentHolder}"
                                                                       Margin="5"/>
                                            </DataTemplate>
                                        </ItemsControl.ItemTemplate>
                                    </ItemsControl>
                                </ScrollViewer>
                                <FrameworkElement x:Name="templatedParentHolder"
                                                  DataContext="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}}" 
                                                  Visibility="Collapsed" />
                            </Grid>
                        </Border>
                    </DockPanel>
                    <ControlTemplate.Triggers>
                        <EventTrigger RoutedEvent="local:InterfacePresenter.CurrentMethodResultChanged">
                            <BeginStoryboard Storyboard="{StaticResource currentMethodResultRegionChangedStoryboard}" />
                        </EventTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Israel Israel
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions