Saturday 28 July 2012

Silverlight Metro Time Line Control

I’ve come up with a nice little user control that plots “activities” over the past couple of days.

And when I say I’ve come up with, I mean I was inspired by/copied the design from the new Microsoft Dynamics software.

It’s currently making use of the Telerik chart controls, as that’s what I am using at work but I’m sure that part could be swapped out for a Silverlight Toolkit chart.

image

Timeline.xaml

<UserControl x:Class="FxChart.TimelinePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:chart="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Charting"
xmlns:charting="clr-namespace:Telerik.Windows.Controls.Charting;assembly=Telerik.Windows.Controls.Charting"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:FxChart"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
d:DesignHeight="300"
d:DesignWidth="400"
mc:Ignorable="d">
<UserControl.Resources>
<Color x:Key="BlueColor">#1BA1E2</Color>
<Color x:Key="LightBlueColor">#FFB2E0F4</Color>
<Color x:Key="LightGreyColor">#ffc9c8c8</Color>
<Color x:Key="GreyColor">#ff6d6d6d</Color>
<SolidColorBrush x:Key="BlueBrush" Color="{StaticResource BlueColor}" />
<SolidColorBrush x:Key="LightBlueBrush" Color="{StaticResource LightBlueColor}" />
<SolidColorBrush x:Key="LightGrayBrush" Color="{StaticResource LightGreyColor}" />
<SolidColorBrush x:Key="GrayBrush" Color="{StaticResource GreyColor}" />

<Style x:Key="CustomPointMark" TargetType="telerik:PointMark">
<Setter Property="Height" Value="25" />
<Setter Property="Width" Value="15" />
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="telerik:PointMark">
<Canvas>
<Path x:Name="PART_PointMarkPath"
Canvas.Left="{TemplateBinding PointMarkCanvasLeft}"
Canvas.Top="{TemplateBinding PointMarkCanvasTop}"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
Data="M198.5,96.5 C142.16696,96.5 96.5,142.16696 96.499992,198.5 C96.5,254.83304 142.16696,300.5 198.5,300.5 C254.83304,300.5 300.5,254.83304 300.5,198.5 C300.5,142.16696 254.83304,96.5 198.5,96.5 z M200.5,0.5 C310.95694,0.5 400.5,90.04306 400.5,200.5 C400.5,266.0838 368.93259,324.29465 320.16318,360.76709 L319.90009,360.95898 L202.78197,561.74298 L92.60006,368.92462 L88.678093,366.34314 C35.477753,330.4017 0.49999619,269.53558 0.5,200.5 C0.49999619,90.04306 90.043045,0.5 200.5,0.5 z"
Fill="{TemplateBinding Fill}"
Stretch="Fill"
Stroke="{TemplateBinding Stroke}"
StrokeLineJoin="Round"
StrokeThickness="{TemplateBinding StrokeThickness}"
Style="{TemplateBinding ShapeStyle}" />
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

<Style x:Key="SeriesItemLabelStyle" TargetType="telerik:SeriesItemLabel">
<Setter Property="FontSize" Value="12" />
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="Padding" Value="2,0" />
<Setter Property="IsHitTestVisible" Value="False" />
<Setter Property="Foreground" Value="Gray" />
<Setter Property="LabelStyle">
<Setter.Value>
<Style TargetType="Border">
<Setter Property="BorderThickness" Value="0" />
</Style>
</Setter.Value>
</Setter>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}" TextAlignment="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource TemplatedParent}}" />
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="telerik:SeriesItemLabel">
<Canvas x:Name="PART_MainContainer">
<ContentPresenter Margin="{TemplateBinding Padding}" />
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

<Style x:Key="RectangleStyle" TargetType="Rectangle">
<Setter Property="Margin" Value="0,0,-1,0" />
<Setter Property="Stroke" Value="{StaticResource LightGrayBrush}" />
<Setter Property="StrokeThickness" Value="1" />
<Setter Property="Fill" Value="Transparent" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
</Style>

<Style x:Key="EndRectangleStyle" TargetType="Rectangle">
<Setter Property="Margin" Value="0,0,0,0" />
<Setter Property="Stroke" Value="{StaticResource BlueBrush}" />
<Setter Property="StrokeThickness" Value="1" />
<Setter Property="Fill" Value="{StaticResource BlueBrush}" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
</Style>

<Style x:Key="DateTextBlockStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="{StaticResource GrayBrush}" />
<Setter Property="FontSize" Value="12" />
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="ExtraBold" />
</Style>
</UserControl.Resources>

<Grid x:Name="LayoutRoot" Background="White">
<StackPanel Margin="100">
<chart:RadChart x:Name="RadChart1"
Height="80"
BorderThickness="0" />

<Grid Margin="0,-2,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

<Grid.RowDefinitions>
<RowDefinition Height="40" />
</Grid.RowDefinitions>

<Rectangle Grid.Column="0" Style="{StaticResource EndRectangleStyle}" />
<Rectangle Grid.Column="1"
Margin="-1,0,-1,0"
Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="2" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="3" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="4" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="5" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="6" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="7" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="8" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="9" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="10" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="11" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="12" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="13" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="14" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="15" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="16" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="17" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="18" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="19" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="20" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="21" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="22" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="23" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="24" Style="{StaticResource EndRectangleStyle}" />
<Rectangle Grid.Column="25" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="26" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="27" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="28" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="29" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="30" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="31" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="32" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="33" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="34" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="35" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="36" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="37" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="38" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="39" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="40" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="41" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="42" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="43" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="44" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="45" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="46" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="47" Style="{StaticResource RectangleStyle}" />
<Rectangle Grid.Column="48" Style="{StaticResource EndRectangleStyle}" />
</Grid>

<Grid>
<TextBlock x:Name="YesterdayTextBlock"
Margin="10,0,0,0"
HorizontalAlignment="Left"
Style="{StaticResource DateTextBlockStyle}" />
<TextBlock x:Name="TodayTextBlock"
Margin="10,0,0,0"
HorizontalAlignment="Center"
Style="{StaticResource DateTextBlockStyle}" />
<TextBlock x:Name="TomorrowTextBlock"
Margin="0,0,10,0"
HorizontalAlignment="Right"
Style="{StaticResource DateTextBlockStyle}" />
</Grid>

</StackPanel>
</Grid>

</UserControl>


Timeline.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using Telerik.Windows.Controls;
using Telerik.Windows.Controls.Charting;

namespace FxChart
{
public partial class TimelinePage
{
public TimelinePage()
{
InitializeComponent();
InitializeChart();
InitializeDateLabels();
}

private void InitializeDateLabels()
{
this.YesterdayTextBlock.Text = DateTime.Today.AddDays(-1).ToString("dd.MM.yyyy");
this.TodayTextBlock.Text = DateTime.Today.ToString("dd.MM.yyyy");
this.TomorrowTextBlock.Text = DateTime.Today.AddDays(1).ToString("dd.MM.yyyy");
}

private void InitializeChart()
{
this.RadChart1.LayoutUpdated += this.RadChart1_LayoutUpdated;

var seriesMapping = GetLineSeries();
RadChart1.SeriesMappings.Add(seriesMapping);

RadChart1.Background = new SolidColorBrush(Colors.Transparent);
RadChart1.DefaultView.ChartLegend.Visibility = Visibility.Collapsed;
RadChart1.DefaultView.ChartArea.Padding= new Thickness(0);

AxisX axisX = new AxisX()
{
AutoRange = false,
IsDateTime = true,
Visibility = Visibility.Collapsed,
MinValue = DateTime.Today.AddDays(-1).ToOADate(),
MaxValue = DateTime.Today.AddDays(1).ToOADate()
};

RadChart1.DefaultView.ChartArea.AxisX = axisX;

AxisY axisY = new AxisY()
{
AutoRange = false,
IsZeroBased = true,
Visibility = Visibility.Collapsed,
MinValue = 0,
MaxValue = 4,
StripLinesVisibility = Visibility.Collapsed
};

RadChart1.DefaultView.ChartArea.AxisY = axisY;
RadChart1.ItemsSource = GetData();
}

void RadChart1_LayoutUpdated(object sender, EventArgs e)
{
var labelsPanel = this.RadChart1.FindChildByType<LabelsPanel>();
if (labelsPanel == null || labelsPanel.Children.Count == 0)
return;

this.RadChart1.LayoutUpdated -= this.RadChart1_LayoutUpdated;

foreach (SeriesItemLabel seriesItemLabel in labelsPanel.Children)
{
double leftAdjustment = -(seriesItemLabel.ActualWidth / 2 + 20);
seriesItemLabel.Margin = new Thickness(leftAdjustment, -40, 0, 0);
}
}

private SeriesMapping GetLineSeries()
{
Style pathStyle = new Style(typeof (Path));
//pathStyle1.Setters.Add(new Setter(Shape.StrokeDashArrayProperty, "1"));
pathStyle.Setters.Add(new Setter(Shape.StrokeThicknessProperty, 0));

Style lineStyle = new Style(typeof (SelfDrawingSeries));
lineStyle.Setters.Add(new Setter(SelfDrawingSeries.BorderLineStyleProperty, pathStyle));

SeriesMapping seriesMapping = new SeriesMapping();
seriesMapping.ItemMappings.Add(new ItemMapping("YValue", DataPointMember.YValue));
seriesMapping.ItemMappings.Add(new ItemMapping("ActivityDateTime", DataPointMember.XValue));
seriesMapping.ItemMappings.Add(new ItemMapping("LabelTime", DataPointMember.Label));

var lineDefinition = new LineSeriesDefinition() { SeriesStyle = lineStyle, ShowItemLabels = true, ShowPointMarks = true};
lineDefinition.Appearance.PointMark.Fill = this.Resources["BlueBrush"] as SolidColorBrush;
lineDefinition.PointMarkItemStyle = this.Resources["CustomPointMark"] as Style;

lineDefinition.SeriesItemLabelStyle = this.Resources["SeriesItemLabelStyle"] as Style;
seriesMapping.SeriesDefinition = lineDefinition;

return seriesMapping;
}

private IList<TimelineData> GetData()
{
return new List<TimelineData>(10)
{
new TimelineData() {ActivityDateTime = DateTime.Today.AddHours(-16), YValue = 1},
new TimelineData() {ActivityDateTime = DateTime.Today.AddHours(-12), YValue = 1},
new TimelineData() {ActivityDateTime = DateTime.Today.AddHours(-8), YValue = 1},
new TimelineData() {ActivityDateTime = DateTime.Today.AddHours(-4), YValue = 1},
new TimelineData() {ActivityDateTime = DateTime.Today.AddHours(8), YValue = 1},
new TimelineData() {ActivityDateTime = DateTime.Today.AddHours(10), YValue = 1},
new TimelineData() {ActivityDateTime = DateTime.Today.AddHours(12), YValue = 1},
new TimelineData() {ActivityDateTime = DateTime.Today.AddHours(16), YValue = 1},
};
}
}

public class TimelineData
{
public int YValue { get; set; }
public DateTime ActivityDateTime { get; set; }

public string LabelTime { get { return ActivityDateTime.ToString("h tt"); } }
}
}

Source


You can download the source:  https://github.com/stevenh77/FxChart


Before you run the application, make sure the RootVisual in the App.xaml.cs file is set to the TimelinePage object.


Telerik Controls


You can download a trial copy of the Telerik controls:  http://www.telerik.com/products/silverlight/overview.aspx

Tuesday 17 July 2012

Metro style Slider

I was recently asked to include 3 combo boxes on a data entry screen, each with a fixed number of items.

The additional clicks to open the combo then scroll down to select the desired item seemed inefficient, when all the user wanted to do was answer the three questions then click the submit/next button.  Rapid and quick fire were the order of the day!

So I came up with a slider with text descriptions so that the user can click either the slider or the text to make their selection.  Obviously it takes up quite a bit more space but by coming up with a Metro style, which pays homage to the Telerik Slider style, it makes for quite a pleasant looking metro interface.

I’ve coded this up in Silverlight 4 but it also works in Silverlight 5.

The solution can be upgraded from fixed hardcoded values to a dynamic data driven items source, which would require auto resizing etc.  This would be the preferred solution but for now this quick solution does the trick!

Online Demo

Slider Style

For this example project the slider style is stored in the App.xaml file:

<Application x:Class="MetroSlider_SL4.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Application.Resources>
<Style x:Key="MetroThumbStyle" TargetType="Thumb">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Thumb">
<Grid Background="#FF319FFD" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="MetroSliderStyle" TargetType="Slider">
<Setter Property="Maximum" Value="10" />
<Setter Property="Minimum" Value="1" />
<Setter Property="Value" Value="1" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Slider">
<Grid x:Name="Root">
<Grid.Resources>
<ControlTemplate x:Key="RepeatButtonTemplate">
<Grid x:Name="Root"
Background="Transparent"
Opacity="0" />
</ControlTemplate>
</Grid.Resources>

<Grid x:Name="VerticalTemplate" Visibility="Visible">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>

<Rectangle Grid.Row="0"
Grid.RowSpan="3"
Width="6"
Margin="0,5,0,5"
Fill="#ffc8c6c6"
RadiusX="1"
RadiusY="1"
StrokeThickness="0" />
<RepeatButton x:Name="VerticalTrackLargeChangeDecreaseRepeatButton"
Grid.Row="2"
Width="18"
IsTabStop="False"
Template="{StaticResource RepeatButtonTemplate}" />
<Thumb x:Name="VerticalThumb"
Grid.Row="1"
Width="18"
Height="11"
IsTabStop="True"
Style="{StaticResource MetroThumbStyle}" />
<RepeatButton x:Name="VerticalTrackLargeChangeIncreaseRepeatButton"
Grid.Row="0"
Width="18"
IsTabStop="False"
Template="{StaticResource RepeatButtonTemplate}" />

</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
</Application>

Source


https://github.com/stevenh77/MetroSlider_SL4