in

Software FX Community

Discuss and find help for all Software FX products.

WPF Blog

  • Chart FX for WPF Compatibility with Expression Blend 3

    With the release of Expression Blend 3 RC the supported design time API is almost the same as in Visual Studio so we are happy to report that starting with Chart FX for WPF build 3482 our wizard will be available in Blend 3. Although you can open a project in Blend 2 and interact with a Chart FX chart in the property grid, most of the design time API exposed by Visual Studio is not supported in Blend 2 so it was impossible for us to expose this UI.

    Note that integration with the Blend Toolbox requires a couple of registry keys pointing to the folder where the Chart FX for WPF binaries are located, our installers will create this key from now on but you can also create them manually following the instructions here.

    Blend1 Blend2

    There is one feature used in our wizard where Blend 3 still does not support the same functionality as Visual studio 2008 or 2010 and that is adding references to the project from the design time assembly, because of this there are some pages where we cannot expose certain options until you manually add the references. In Visual Studio we do expose them and automatically add the references as needed. The most important scenarios where this is notable are:

    • In the Gallery Page, if you select Other in VS we will show additional galleries exposed in extension assemblies such as Rose and HighLow. In Blend you have to manually add references to ChartFX.WPF.Rose and ChartFX.WPF.HighLow before these options are available

    • In the Motifs pages we expose Chart Styles, Borders and Palettes from additional assemblies, in the image below you will notice that Blend only exposes Styles from our core assembly. If you manually add references to ChartFX.WPF.Motifs and ChartFX.WPF.Motifs.HandDrawn you will have access to additional styles including Edge, Spotlight, Floating, Blinds and HandDrawn

    Blend3 

    Please note that if you installed our product on the beta period and have not reinstalled the release bits, you might have the Design assemblies in the same folder as the core dlls, we have now moved to a scheme where Design dlls are placed in a Design subdirectory, if you plan to use our hotfix/servicepack installers from the browser we recommend you manually move all the design files to make sure they are properly updated. If you’ve already installed a hotfix you will probably have a newer version already in the Design folder so you can remove them from the parent folder.

    Finally, now that I have your attention, I would like to remind you that all Chart FX assemblies with the string Design in their name should not be referenced in your project as they are not redistributable.

    JuanC

    Posted Jul 14 2009, 10:49 PM by JuanC with no comments
    Filed under: ,
  • Displaying charts in a Blend 3 Sketchflow prototype

    Now that Blend 3 + Sketchflow RC has been released, we thought it would be a good idea to write a post on how to integrate charts into your prototype, some of the manual steps described here will be fixed in future Chart FX for WPF builds by providing design time assemblies for Blend 3.

    Once you have created a WPF Sketchflow Prototype application, you have to

    • Add a reference to our assemblies

      In the projects tab add a reference to the PrototypeScreens project to ChartFX.WPF.dll
    • Create a chart

      In the assets window, expand the Controls\All item and select Chart, note that because the WPF toolkit also includes a chart class you have to be careful of which one you select. Our icon is a blue and green pie.

      SketchFlow1 

    As soon as you drop a chart in your screen, you will quickly notice that the default chart look does not go well with the squiggly style used in Sketchflow.

    SketchFlow2

    Luckily, we borrowed a great piece of code that Robby Ingebretsen wrote called HandDrawnShapes and extended it to create a style for our chart that looks as if it was hand drawn, to use this style you have to add a reference to ChartFX.WPF.Motifs.HandDrawn.dll (same way as our core assembly), then you need to add an extra namespace to the top-level UserControl tag in the XAML

    xmlns:cfxhanddrawn="http://schemas.softwarefx.com/chartfx/wpf/80/ext/handdrawn"

    And set the Chart style property to use the handdrawn style

      <cfx:Chart Style="{x:Static cfxhanddrawn:HandDrawn.Style}" Margin="8,8,118,147"/>
    

    SketchFlow3

    We got closer but clearly there is too much color, the font does not match and the outside chart border is not squiggly, although we could change properties in the chart to customize its appearance, we will get a more reusable result if we try to create a global style with these changes in the global resource dictionary so that all charts inherit these changes.

    To do this we will modify our app.xaml to create a style for charts that uses some of the handdrawn work but also integrates better with the sketch style used in your prototypes

    <Style TargetType="{x:Type cfx:Chart}">
      <Setter Property="Template"
    Value="{x:Static cfxmotifs:Simple.ChartTemplate}"/> <Setter Property="GalleryTemplates"
    Value="{x:Static cfxhanddrawn:HandDrawn.GalleryTemplates}"/> <Setter Property="MarkerTemplates"
    Value="{x:Static cfxhanddrawn:HandDrawn.MarkerTemplates}"/> <Setter Property="MergeResources"
    Value="{x:Static cfxhanddrawn:HandDrawn.MergeResources}"/> <Setter Property="Border" Value="{StaticResource BorderSketch}"/> <Setter Property="Palette" Value="{StaticResource SketchPalette}"/> <Setter Property="FontFamily" Value="{DynamicResource FontFamily-Sketch}"/> <Setter Property="FontSize" Value="{DynamicResource SizeDouble-Sketch}"/> </Style>

    For the first four properties we are simply using static properties defined in our handdrawn and simple templates, note that we are cheating on MergeResources because this property was recently added to the HandDrawn class so you might have to download our most recent hotfix.

    For the next two properties (Border and Palette) we are creating local copies, the border will use Sketchflow objects so that its appearance matches more the other sketchflow elements, the palette is basically a color palette where all backgrounds are transparent and all foregrounds and borders match the color used in the prototypes.

    The last 2 properties (FontFamily and FontSize) will use dynamic resources defined in SketchStyles.xaml

    SketchFlow4 SketchFlow5

    SketchFlow6 SketchFlow7

    Although the final product certainly looks simple which is key to reinforce the fact that this is a prototype and not fixate on visual attributes, you have a fully fledged charting engine that you can later customize the fit the look of your WPF application.

    JuanC

  • Security issues using charts in a WPF Browser Application (XBAP)

    XBAPs are deployed using ClickOnce and default to run in a sandbox, because of this you might encounter certain problems when trying to use Chart FX for WPF in them.

    Running in full trust will take care of all the Chart FX related issues so if you already needed full trust in your application then integrating charts should not be an issue, please note that you should make sure not only that you can run the application in your local machine but also that it can be run from other machines. A full trust XBAP will run fine when deployed from the local zone but needs to be signed when deploying from the web. You can read more about this here and here.

    If running in full trust is not an option and you try to use our chart control you will receive a Security exception related to Reflection, we have fixed this issue in build 3467, unfortunately even if you use a newer build you will then receive another security exception when parsing our default XAML, the reason for this is that we use BitmapEffects in our some of our templates including Glass which is our default.

    Note that even though we expose a property called UseEffects that allow you to hide these effects, setting this property to false will not fix the exception as the effects are still in the XAML although they will be not be applied and just trying to load the XAML causes the exception.

    To overcome this problem you must change the references in your application to use the ChartFX.XBAP.* assemblies, these assemblies do not contain any bitmap effects so you can still use our default Glass style, because we use the same namespaces and type names, there is no need to change any code but you must also change the reference in licenses.licx

    To summarize if you are building WPF browser applications you must use ChartFX.XBAP build 3467 or later in order to work in low-trust using the Internet or Intranet zones.

    XBAP1

    JuanC

    Posted Jul 01 2009, 12:03 PM by JuanC with no comments
    Filed under: ,
  • Chart FX for WPF Compatibility with VS2010 Beta 1

    If you are using Visual Studio 2010 Beta 1 and Chart FX for WPF to create your WPF applications you have probably found that you have no design time experience, i.e. if you click on the chart at design time you will not get our wizard.

    VS2010Beta1

    Unfortunately Beta 1 does not pickup the Design time assemblies we ship for Visual Studio 2008 because they reference older versions of the Microsoft.Windows.Design.* assemblies. We expect this will be fixed on future Visual Studio builds so that we can ship multiple design time dlls and each Visual Studio will load the appropriate version.

    In the meantime we have been building VS2010 compatible assemblies although they are not shipped with our installer, if you are using VS2010 and want to use our design time functionality you must do this

    • Install our most recent hotfix
    • Send an email to support at softwarefx dot com to request the VS2010 design time assemblies
    • If you have a Design subdirectory underneath the folder where ChartFX was installed, rename this folder and copy all files to its parent directory
    • Overwrite ChartFX.WPF.Design.dll and ChartFX.WPF.Design.VisualStudio.dll with the VS2010 compatible assemblies (keep a backup copy if you think you might want to switch back to VS 2008)

    If you are starting a new project you will not get our component in the toolbox or in the WPF tab in the Choose Items dialog, but you can browse for the ChartFX.WPF.dll assembly.

    Note that VisualStudio 2010 Beta 1 seems to ignore the ToolboxBrowsable attribute so it will suggest a bunch of classes that you will not need in your toolbox, make sure you uncheck all classes but Chart.

    JuanC

  • Showing vertical titles

    Chart FX for WPF uses the title class in several places including Chart.Titles, Axis.Title, LegendBox.Titles and CustomGridLine.Title; in most of these scenarios there are cases where the title needs to be drawn vertically. For example the following XAML generates a chart with 4 titles each docked in a different position

          <cfx:Chart.Titles>
            <cfx:Title DockPanel.Dock="Top">Top</cfx:Title>
            <cfx:Title DockPanel.Dock="Left">Left</cfx:Title>
            <cfx:Title DockPanel.Dock="Right">Right</cfx:Title>
            <cfx:Title DockPanel.Dock="Bottom">Bottom</cfx:Title>
          </cfx:Chart.Titles>
    

    Which results in a chart like this

    VerticalTitles1

    Note that we rotate the titles on the right side 90 degrees clockwise while the title on the left side is rotated 90 degrees counterclockwise. You can control this using the RotateVerticalMode property, in the following picture we will set the left title RotateVerticalMode to None which might be suitable for short titles, you can also force a specific rotation by using Clockwise or CounterClockwise. We are using Counterclockwise for the right title.

    VerticalTitles2

    Can we use this feature to draw vertical titles where the letters are not rotated? I am glad you asked, setting the RotateVerticalMode to None is useful if we “borrow” delay’s code for a vertical text block, in this case our XAML would set the title’s content to be an instance of VerticalTextBlock which derives from control

          <cfx:Chart.Titles>
            <cfx:Title DockPanel.Dock="Left" RotateVerticalMode="None">
              <local:VerticalTextBlock Text="SALES"/>
            </cfx:Title>
          </cfx:Chart.Titles>
    

    Which results in the following chart

    VerticalTitles3

    Note that the Title.Content is of type object so you can also provide any visuals, e.g. a StackPanel with an icon and some text, a button, etc.

    There are some scenarios though where Chart FX might reuse your title in more than one place, an axis for example could potentially be reused in multiple panes, in this case you will want to use the visuals in the Title.ContentTemplate property.

    JuanC

    Posted Jun 18 2009, 10:31 AM by JuanC with no comments
    Filed under:
  • Creating an Expression Dark Chart Style

    Styling a WPF application so that it looks similar to Expression Blend seems to be a common scenario, you can even download a couple of open-source projects from Codeplex called “WPF Futures” and “WPF Themes” that include Expression Dark as one of several styles you can use in your WPF applications.

    If you use one of these projects, all the common controls will be automatically styled (ListBox, Label, Slider, etc.) but it will not affect how your charts look, so you will get something like this

    ExpressionDark1

    The first thing we can do to make our chart look integrated with the application is to change the brushes used for all the elements (bars, background, titles, point labels, etc.), in order to do this we will add to our app.xaml a style targeting all charts as follows

    xmlns:cfx="http://schemas.softwarefx.com/chartfx/wpf/80"
    xmlns:cfxMotifs=http://schemas.softwarefx.com/chartfx/wpf/80/motifs
    <Style x:Key="{x:Type cfx:Chart}" TargetType="{x:Type cfx:Chart}">
      <Setter Property="Template" Value="{x:Static cfxMotifs:Simple.ChartTemplate}"/>
      <Setter Property="Palette" Value="{StaticResource PaletteExpressionDark}"/>
    </Style>

    The first setter will switch the chart template from the default (Glass) to a simpler template (Simple), this removes the reflection at the bottom as well as the 3D-like floor. The second setter to change the chart’s palette references a resource in our app.xaml that will hold the palette and will look like this

    <cfx:Palette x:Key="PaletteExpressionDark">
      <cfx:Palette.SeriesFill>
        <Brush>#5372B0</Brush>
        <Brush>#FFEC35</Brush>
        …
    </cfx:Palette.SeriesFill> <cfx:Palette.SeriesStroke> <Brush>#324468</Brush> <Brush>#897E1C</Brush> …
    </cfx:Palette.SeriesStroke> <cfx:Palette.SeriesForegroundInside> <Brush>#FF385516</Brush> <Brush>#FF575113</Brush> … </cfx:Palette.SeriesForegroundInside> <cfx:Palette.Foreground>#FFFFFFFF</cfx:Palette.Foreground> <cfx:Palette.Background>#FF333333</cfx:Palette.Background> <cfx:Palette.Border></cfx:Palette.Border> <cfx:Palette.PointLabelForeground>#FFFFFFFF</cfx:Palette.PointLabelForeground> <cfx:Palette.PointLabelForegroundInside>
    #FFFFFFFF
    </cfx:Palette.PointLabelForegroundInside> <cfx:Palette.Axis>#BDBDBD</cfx:Palette.Axis> <cfx:Palette.AxisGridLines>#FF8B8B8B</cfx:Palette.AxisGridLines>

    <cfx:Palette.PlotArea>#40595959</cfx:Palette.PlotArea> <cfx:Palette.MainArea>Transparent</cfx:Palette.MainArea> <cfx:Palette.Title>#DFDFDF</cfx:Palette.Title> <cfx:Palette.LegendForeground>#DFDFDF</cfx:Palette.LegendForeground> <cfx:Palette.DataViewBackground>#40595959</cfx:Palette.DataViewBackground>
    <cfx:Palette.ToolTipForeground>#EEEEEE</cfx:Palette.ToolTipForeground> <cfx:Palette.View3DAmbientLight>#808080</cfx:Palette.View3DAmbientLight> <cfx:Palette.ThumbStroke>#FFD1D1D1</cfx:Palette.ThumbStroke> <cfx:Palette.SelectionFill>#8C00AEFF</cfx:Palette.SelectionFill> </cfx:Palette>

    Note that this palette has been trimmed so there are several entries missing, you can check the attached XAML for a complete palette, I wanted to highlight the following

    • You can supply as many brushes as desired for the chart series, if there are more series we will loop through this list
    • You can optionally supply brushes used for the borders of the markers, if you don’t we will use a lightly darker version of the series brushes
    • SeriesForegroundInside will be used when we know the point label is drawn inside the marker while the single PointLabelForeground will be used when the point is drawn on the plot area.
    • If you want, you can instead provide a single PointLabelForegroundInside and remove the SeriesForegroundInside
    • There are additional entries for the DataView, Legend, ToolTip, Titles and 3D

    ExpressionDark2

    The combination of a simpler style and using the same colors/gradients makes a big change but there are still several things we can improve, note that the border has not changed, when we designed Chart FX for WPF we wanted to be able to separate the styling of the different chart elements so changing the chart template did not result in the border being changed. This also means that in your chart you can modify the border appearance without worrying about the other elements in the chart.

    <cfx:ControlTemplateBorder x:Key="BorderDark">
      <cfx:ControlTemplateBorder.Template>
        <ControlTemplate TargetType="{x:Type cfx:ChartBorder}">
          <ControlTemplate.Resources>
            <Thickness x:Key="cfxBorderThickness">1</Thickness>
            <Style x:Key="cfxInternalBorderStyle" TargetType="{x:Type ContentControl}">
              <Setter Property="Template">
                <Setter.Value>
                  <ControlTemplate TargetType="{x:Type ContentControl}">
                    <Border CornerRadius="2"
    BorderBrush="{Binding Path=Palette.AlternateBorder}"
    RelativeSource={RelativeSource TemplatedParent},
    Background="{TemplateBinding Background}" BorderThickness="1"> <ContentPresenter Margin="{TemplateBinding Padding}"/> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> <CornerRadius x:Key="cfxCornerRadius">2</CornerRadius> </ControlTemplate.Resources> <Border cfx:Chart.PanelName="BorderChild"
    BorderBrush="{TemplateBinding BorderBrush}"
    Background="{TemplateBinding Background}" CornerRadius="2"
    BorderThickness="{TemplateBinding BorderThickness}" /> </ControlTemplate> </cfx:ControlTemplateBorder.Template> </cfx:ControlTemplateBorder>
    <Style x:Key="{x:Type cfx:Chart}" TargetType="{x:Type cfx:Chart}">
      <Setter Property="Template" Value="{x:Static cfxMotifs:Simple.ChartTemplate}"/>
      <Setter Property="Palette" Value="{StaticResource PaletteExpressionDark}"/>
      <Setter Property="Border" Value="{StaticResource BorderDark}"/>
    </Style>
    

    We are now setting the Border property using a ControlTemplateBorder class, if you ignore the resources for a moment it is just a single Border with some template bindings but the PanelName=”BorderChild” is very important, we will find the element with this value for the Chart.PanelName attached property and we will add inside of it the other visuals that generate the chart.

    • If we find a resource named cfxBorderThickness it will be used as a default border thickness unless you set the Chart.BorderThickness property
    • The cfxInternalBorderStyle will be used for the borders of the legend box and dataview
    • If the element marked as “BorderChild” has some roundness you must set the cfxCornerRadius appropriately

    Additionally you will notice that we have a default scrollbar style that does not match with the one used in the listbox, for this we will have to modify another property called MergeResources that expects a ResourceDictionary (we did not use a built-in WPF feature that does this because it causes memory leaks)

      <ResourceDictionary x:Key="ResourcesDark">
        <Style x:Key="ThumbStyle" TargetType="{x:Type Thumb}" BasedOn="{x:Null}">
          <Setter Property="Template">
            <Setter.Value>…</Setter.Value>
          </Setter>
        </Style>
        <Style x:Key="ScrollRepeatButtonStyle" TargetType="{x:Type RepeatButton}">
          <Setter Property="Template">
            <Setter.Value></Setter.Value>
          </Setter>
        </Style>
        <Style TargetType="{x:Type ScrollBar}">
          <Setter Property="Template">
            <Setter.Value>…</Setter.Value>
          </Setter>
        </Style>
      </ResourceDictionary>
      <Style x:Key="{x:Type cfx:Chart}" TargetType="{x:Type cfx:Chart}">
        <Setter Property="Template" Value="{x:Static cfxMotifs:Simple.ChartTemplate}"/>
        <Setter Property="Palette" Value="{StaticResource PaletteExpressionDark}"/>
        <Setter Property="Border" Value="{StaticResource BorderDark}"/>
        <Setter Property="MergeResources" Value="{StaticResource ResourcesDark}"/>
      </Style>
    

    Again we removed most of the xaml but this is just a copy of how a scrollbar is styled for expression dark. We are now getting really close as you can see in the following pictures

    ExpressionDark3 ExpressionDark4

    Depending on your application you can actually stop the styling here but there is still some of the glass showing on the markers, if you decide you want simpler visuals you can add the following

    <ResourceDictionary x:Key="GalleryDark">
      <DataTemplate x:Key="Bar">
        <DataTemplate.Resources>
          <sys:Double x:Key="cfxDefStrokeThickness">1</sys:Double>
          <DataTemplate x:Key="cfxLastStack">…</DataTemplate>
          <DataTemplate x:Key="cfxMiddleStack">…</DataTemplate>
          <DataTemplate x:Key="cfxFirstStack">…</DataTemplate>
    </DataTemplate.Resources> <
    Border x:Name="bar" CornerRadius="2,2,0,0"
    Width="{Binding Path=Width}" Height="{Binding Path=Height}"
    Background="{Binding Path=Fill}" BorderBrush="{Binding Path=Stroke}"
    BorderThickness="{Binding Path=StrokeThickness}"
    Opacity="{Binding Path=Opacity}"
    RenderTransform="{Binding Path=Transform}" /> <DataTemplate.Triggers>…</DataTemplate.Triggers> </DataTemplate> <DataTemplate x:Key="Line"> <DataTemplate.Resources> <sys:Double x:Key="cfxDefStrokeThickness">3</sys:Double> <sys:Double x:Key="cfxDefMarkerSize">10</sys:Double> <sys:String x:Key="pointsCategory">lineShadow</sys:String> </DataTemplate.Resources> <Canvas Opacity="{Binding Path=Opacity}"> <Polyline Visibility="{Binding Path=EffectsVisibility}" x:Name="lineShadow"
    Points="{Binding Path=Points}"
    Canvas.Top="{Binding Path=StrokeThickness,
    Converter={StaticResource multiplyConverter},
    ConverterParameter=1.50}"
    Stroke="#FF000000" StrokeThickness="{Binding Path=StrokeThickness}"
    StrokeDashArray="{Binding Path=StrokeDashArray}" Opacity="0.2"> <Polyline.BitmapEffect> <BlurBitmapEffect Radius="2"/> </Polyline.BitmapEffect> </Polyline> <Polyline Points="{Binding Path=Points}" x:Name="points"
    Stroke="{Binding Path=Stroke}"
    StrokeThickness="{Binding Path=StrokeThickness}"
    StrokeDashArray="{Binding Path=StrokeDashArray}" /> </Canvas> <DataTemplate.Triggers>…</DataTemplate.Triggers> </DataTemplate> </ResourceDictionary>
    <Style x:Key="{x:Type cfx:Chart}" TargetType="{x:Type cfx:Chart}">
      <Setter Property="Template" Value="{x:Static cfxMotifs:Simple.ChartTemplate}"/>
      <Setter Property="Palette" Value="{StaticResource PaletteExpressionDark}"/>
      <Setter Property="Border" Value="{StaticResource BorderDark}"/>
      <Setter Property="MergeResources" Value="{StaticResource ResourcesDark}"/>
      <Setter Property="GalleryTemplates" Value="{StaticResource GalleryDark}"/>
      <Setter Property="MarkerTemplates" Value="{x:Static cfx:MarkerTemplates.Simple}"/>
    </Style>

    The GalleryTemplates property expects a ResourceDictionary where you supply styles for all our galleries, note the following key points

    • If we do not find a template for a particular gallery we will use the template from the Simple style. This gives you a simple but not too basic style for most galleries, in this case we are only customizing bar and line
    • Each style can provide defaults, for example the line will have a thickness of 3 and the markers will be size 10 if you do not modify the appropriate properties.
    • Bars allow you to specify an alternate template for the first, middle and last stacks, this allows you to have a rounded top without having all intermediate segments rounded
    • You can bind visibility of “effects” to a property called EffectsVisibility (shown here for the shadow of the line), this will be controlled by Chart.UseEffects
    • You can also provide a ResourceDictionary that controls how the line markers look, we are using a built-in dictionary that generates simpler markers.

    This is what our final style looks like

    ExpressionDark5 ExpressionDark6
    ExpressionDark7

    Chart FX for WPF includes the following built-in styles

    • In our core ChartFX.WPF assembly we support Glass, Simple and Basic
    • In the ChartFX.WPF.Motifs assembly we include Edge, Spotlight, Floating and Blinds
    • In the ChartFX.WPF.Motifs.HandDrawn assembly we include HandDrawn

    You can quickly switch styles in your app with a single line of code, if you add a reference to the extra 2 assemblies you will  be able to browse through our styles in the ChartFX.WPF.Motifs namespace, remember that you are assigning a Style property so you have to then select the Style property (e.g. ChartFX.WPF.Motifs.Simple.Style)

    ExpressionDarkStyles

    We are considering adding Expression Dark to ChartFX.WPF.Motifs as well as other popular application styles, your feedback will let us know which styles we should focus on, but we wanted to show that is possible to completely control your charts appearance to make it integrated in your application.

    JuanC

    The following APIs were not harmed during the production of this post

    Palette: Gives you control over all brushes and colors used in the chart. 
    Border: Allows you to change the chart border independently from the chart template.
    GalleryTemplates and MarkerTemplates. Control how markers (bar, line, area, etc.) look
    MergeResources: Additional resources such as scrollbar. 
    UseEffects: Hide shadows, reflections from the chart visuals.

    Posted Jun 15 2009, 10:54 AM by JuanC with no comments
    Filed under: ,
  • Hiding series using the Legend Box

    If you have a chart with multiple series and need to provide UI to show/hide series, you can use styling on the legend box items to display a checkbox for each series.

    xmlns:cfxConverters="http://schemas.softwarefx.com/chartfx/wpf/80/converters"
    
    <DataTemplate x:Key="CheckLegend">
      <DataTemplate.Resources>
        <cfxConverters:VisibilityToBooleanConverter x:Key="VisibilityToBool"
    FalseVisibility="Hidden"/> </DataTemplate.Resources> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <CheckBox Margin="2,0" VerticalAlignment="Center"
    IsChecked="{Binding Path=Visibility,
    Converter={StaticResource VisibilityToBool}}" /> <Rectangle Stroke="{Binding Path=Stroke}" Fill="{Binding Path=Fill}"
    Grid.Column="1" Width="12" Height="12" VerticalAlignment="Center"
    Margin="2,0" /> <TextBlock FontFamily="{Binding Path=FontFamily}"
    FontSize="{Binding Path=FontSize}" Text="{Binding Path=Text}"
    Grid.Column="2" VerticalAlignment="Center" Margin="2,0" /> </Grid> </DataTemplate>

    Unfortunately templating items in the legend box has to be done in the code because of our API so we need to add the following line of code to our Window/Page Loaded event (we expect to support a XAML way to set these templates in future builds but you know what they say … shipping is also a feature)

    LegendItemAttributes itemAttr = chart1.LegendBox.ItemAttributes[chart1.Series];
    itemAttr.Template = (DataTemplate) FindResource("CheckLegend");

    CheckLegend1

    Where do all these bindings come from?

    Note that you are styling a logical item we create internally in code, this class has information about the specific series (Fill, Stroke, Text) as well as “global” information about the legend box (FontFamily, FontSize, etc.). This class also will pick up individual changes applied to a specific series in the legend box when using the ItemAttributes[collection, index] API.

    If you want to see which properties you can bind to you can use the following trick: setup one of your bindings with an invalid path, e.g. Stroke="{Binding Path=FooBar}" , debug your app and note the message in the Visual Studio output window that looks like this

    System.Windows.Data Error: 39 : BindingExpression path error: 'FooBar' property not found on 'object'
    ''LegendSeriesItem' (HashCode=45082239)'. BindingExpression:Path=FooBar;
    DataItem='LegendSeriesItem' (HashCode=45082239); target element is 'Rectangle' (Name='');
    target property is 'Stroke' (type 'Brush')

    This will give you the class name you are styling, in this case is called LegendSeriesItem. Then you can use ildasm or reflector to view all the properties exposed by this class.

    The VisibilityToBoolConverter exposed in the ChartFX.WPF.Converters namespace is used to convert between Visibility and booleans and also allows you to specify whether false should be translated as Hidden or Collapsed. A Hidden series will not be plotted but will still interact with the axis while a collapsed series will not interact with the axis. This means that if Series1 goes from 0 to 100 and Series2 goes from 0 to 20 and you collapse Series1, now the Y axis will rescale to show 0 to 20, if Series1 instead was Hidden, the axis would remain showing the 0 to 100 range.

    Fine tuning our template

    Now is time to make sure our template works in other situations, if you switch the gallery to line you will quicky notice that our legend box items will still show rectangles even though Chart FX would have used markers in the legend box by default. We need to then change the Rectangle in our template as follows

    xmlns:cfxControls="http://schemas.softwarefx.com/chartfx/wpf/80/controls"
    
    <
    cfxControls:MarkerLegendControl Content="{Binding Path=Self}"
    Grid.Column="1" Margin="2,0" />

    This control requires an extra namespace that you should add to the root of your XAML node. Now a line chart will look like this

    CheckLegend2

    If you hover your mouse over one of the lines you will notice that the checkbox and series text do not get dimmed, this can be solved by adding a trigger to our datatemplate

          <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding Path=Dimmed}">
              <DataTrigger.Value>
                <sys:Boolean>True</sys:Boolean>
              </DataTrigger.Value>
              <Setter Property="Opacity" Value="0.25"/>
            </DataTrigger>
          </DataTemplate.Triggers>
    

    Finally even though you might be using just a string to show what a series represents, Chart FX offers Content and ContentTemplate properties that allows you to provide visuals for your series, e.g.

      <cfx:Chart.Series>
        <cfx:SeriesAttributes Content="US">
          <cfx:SeriesAttributes.ContentTemplate>
            <DataTemplate>
              <StackPanel Orientation="Horizontal">
                <Image Source="pack://siteoforigin:,,,/Img/US.png" Width="16"/>
                <TextBlock Text="United States" Margin="4,0,0,0"/>
              </StackPanel>
            </DataTemplate>
          </cfx:SeriesAttributes.ContentTemplate>
        </cfx:SeriesAttributes>
        <cfx:SeriesAttributes Content="Canada">
          <cfx:SeriesAttributes.ContentTemplate>
            <DataTemplate>
              <StackPanel Orientation="Horizontal">
                <Image Source="pack://siteoforigin:,,,/Img/Canada.png" Width="16"/>
                <TextBlock Text="Canada" Margin="4,0,0,0"/>
              </StackPanel>
            </DataTemplate>
          </cfx:SeriesAttributes.ContentTemplate>
        </cfx:SeriesAttributes>
      </cfx:Chart.Series>
    

    In this case, our TextBlock object in the legend box template will clearly be insufficient so we have to replace it with a ContentControl as follows

    <Border Background="Transparent" Grid.Column="2"
    Margin="2,0" VerticalAlignment="Center"> <ContentControl IsHitTestVisible="False" Content="{Binding Path=Content}"
    ContentTemplate="{Binding Path=ContentTemplate}"
    Foreground="{Binding Path=Foreground}"
    FontFamily="{Binding Path=FontFamily}" FontSize="{Binding Path=FontSize}"
    FontStyle="{Binding Path=FontStyle}" FontWeight="{Binding Path=FontWeight}"/> </Border>

    The reason why we set IsHitTestVisible to false in the ContentControl and wrap it in a border is due to highlighting, you can verifiy that if you remove the property and the border and move the Grid.Column and Margin to the ContentControl, hovering your mouse over the flag will not cause a series highlight.

    Also important is the fact that we used ContentTemplate instead of setting up our visuals with the Content property. The reason for this is that we will try to use the Series.Content in several places, for example in the tooltips and a visual cannot be hosted in 2 places, by using ContentTemplate we are making sure that a copy of the visual is being created as needed.

    CheckLegend3

    JuanC

    The following APIs were not harmed during the production of this post

    LegendBox.ItemAttributes: To set properties for a collection of items (or specific item) in the legend box.
    VisibilityToBoolConverter: Converts from visibility to bool including support to specify which visibility should be used for false.
    MarkerLegendControl: To display a marker in the legend box adapting to the series gallery
    Series.Content and Series.ContentTemplate: To change series representation in LegendBox, Tooltips and DataView.

    Posted May 28 2009, 05:02 PM by JuanC with no comments
    Filed under: ,
  • Minimizing Ink

    Sometimes you need nice looking charts with lots of gradients and animations but sometimes what you really need is to maximize the space and show trends without too many visual artifacts, this is something that Edward Tufte refer to as “Minimizing Ink”, although WPF controls tend to be on the fancy side of the equation, you can actually achieve both with little effort.

    Imagine for example that you have a list of products and want to show a list of products including general information about the product (Product Name, Version, etc.) along with a trend of how these products are being downloaded.

    public class ProductInfo
    {
        public string Name { get; set; }
        public string Version { get; set; }
        public List<ProductDownloads> Downloads { get; set; }
        public string LatestRelease { get; set; } 
    } public class ProductDownloads { public double Count { get; set; } }

    For simplicity we will use a templated listbox to show multiple columns so our first approach to the problem will look like this

    <ListBox Grid.IsSharedSizeScope="true">
      <ListBox.ItemTemplate>
        <DataTemplate>
          <Grid>
            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="Auto" SharedSizeGroup="ColumnName"/>
              <ColumnDefinition Width="Auto" SharedSizeGroup="ColumnVersion"/>
              <ColumnDefinition Width="150" />
              <ColumnDefinition Width="Auto" SharedSizeGroup="ColumnRelease"/>
            </Grid.ColumnDefinitions>
            <TextBlock VerticalAlignment="Center" Grid.Column="0"
    Text="{Binding Path=Name}" Margin="4,0"/> <TextBlock VerticalAlignment="Center" Grid.Column="1"
    Text="{Binding Path=Version}" Margin="4,0"/> <cfx:Chart x:Name="chart1" Grid.Column="2"
    ItemsSource="{Binding Path=Downloads}"
    Gallery="Line" Margin="4,0"> <cfx:Chart.Series> <cfx:SeriesAttributes BindingPath="Count"/> </cfx:Chart.Series> <cfx:Chart.LegendBox> <cfx:LegendBox Visibility="Collapsed"/> </cfx:Chart.LegendBox> </cfx:Chart> <TextBlock VerticalAlignment="Center" Grid.Column="3"
    Text="{Binding Path=LatestRelease}" Margin="4,0"/> </Grid> </DataTemplate> </ListBox.ItemTemplate> </ListBox>

    MinimizeInk1

    Clearly not a good example of ink minimizing but we have to start somewhere, note that we are setting up all the bindings and we are also hiding the chart’s legend box, we are not specifying the size of the chart (because the desired size would be impossible to achieve with our default style).

    The listbox is populated with a list of ProductInfo so our datatemplate applies to each product, because of this, we can simply point the chart’s ItemsSource property to a Binding for the Downloads collection. Inside of the chart we want 1 data series using the Count property.

    Now that we got the basis of our sample out of the way we will focus on the chart customizations

    <cfx:Chart x:Name="chart1" Grid.Column="2" ItemsSource="{Binding Path=Downloads}"
    Gallery="Line" Margin="4,0" Height="20"> <cfx:Chart.Series> <cfx:SeriesAttributes BindingPath="Count" Stroke="Black" StrokeThickness="1"> <cfx:SeriesAttributes.Marker> <cfx:MarkerAttributes Visibility="Collapsed"/> </cfx:SeriesAttributes.Marker> </cfx:SeriesAttributes> </cfx:Chart.Series> <cfx:Chart.LegendBox> <cfx:LegendBox Visibility="Collapsed"/> </cfx:Chart.LegendBox> <cfx:Chart.AxisY> <cfx:Axis Visibility="Collapsed"/> </cfx:Chart.AxisY> <cfx:Chart.AxisX> <cfx:Axis Visibility="Collapsed"/> </cfx:Chart.AxisX> <cfx:Chart.PlotArea> <cfx:PlotAreaAttributes Margin="0" AxesStyle="None"
    Background="{x:Null}" Stroke="{x:Null}"/> </cfx:Chart.PlotArea> <cfx:Chart.Template> <ControlTemplate> <Border cfx:Chart.PanelName="Plot"/> </ControlTemplate> </cfx:Chart.Template> </cfx:Chart>

    In this step, we have done a series of simple changes such as hiding both axes and markers and also setting some brushes, note that our plotarea is normally surrounded by some margin but in such a small chart we do not need any margins (PlotArea.Margin). Because of all these changes we can now set the chart’s height to 20.

    We are also simplifying the chart template, a similar result could be accomplished by using one of our predefined styles (ChartFX.WPF.Motifs.Simple.Style) but this style still allows you to use a legend box or dataview so it has some unnecessary visuals. The template we are using only contains a border that will hold the plotarea, note that this border does not try to honor the chart’s background but this is also not needed.

    MinimizeInk2

    We can now focus on some fit and finish, note that by default a listbox will change the Foreground of the selected item to white but the chart is not honoring it. We will need a trigger in the DataTemplate to accomplish this.

    <DataTemplate.Triggers>
      <DataTrigger
    Binding="{Binding RelativeSource={RelativeSource FindAncestor,
    AncestorType={x:Type ListBoxItem}}, Path=IsSelected}"
    Value="True"> <Setter TargetName="series" Property="Stroke" Value="White" /> </DataTrigger> </DataTemplate.Triggers>

    Because the DataTemplate is actually applied to a ContentPresenter we need our trigger to find the first ancestor of type ListBoxItem as this class will actually hold the IsSelected

    MinimizeInk3

    Even though the charts are very small you can still hover over the line and you will get a tooltip, this can be disabled by setting the Series.Tooltips.IsEnabled to false. You could also modify the trigger if you wanted tooltips only on the selected chart.

        <cfx:SeriesAttributes.ToolTips>
          <cfx:ToolTipAttributes x:Name="tooltips" IsEnabled="False"/>
        </cfx:SeriesAttributes.ToolTips> 
      <DataTemplate.Triggers>
        <DataTrigger
    Binding="{Binding RelativeSource={RelativeSource FindAncestor,
    AncestorType={x:Type ListBoxItem}}, Path=IsSelected}"
    Value="True"> <Setter TargetName="series" Property="Stroke" Value="White" /> <Setter TargetName="tooltips" Property="IsEnabled" Value="True" /> </DataTrigger> </DataTemplate.Triggers>

    We hope to post more how-to's with a variety of chart scenarios using Chart FX for WPF, please feel free to use the comments if there is a particular chart you want to accomplish.

    JuanC

    Posted May 22 2009, 06:00 PM by JuanC with 4 comment(s)
    Filed under: ,
More Posts « Previous page
Copyright 2008 Software FX, Inc.