Wednesday 31 August 2011

StackPanelRegionAdapter

Extend Prism to make use of a stackpanel with this simple solution:

You can use the same technique with a gird or other container control, simply register your adapter(s) in the bootstrapper.

Saturday 27 August 2011

Friday 26 August 2011

Threadsafe Events in .Net

Usually the event pattern used by C# devs would be something long these lines:

When actually this provides for a cleaner threadsafe implementation:

Thanks to Rob Eisenberg for the suggestion!

Remember the event keyword is just a modifier for a delegate declaration that allows it to be included in an interface, constraints it invocation from within the class that declares it, provides it with a pair of customisable accessors (add and remove) and forces the signature of the delegate (Object sender, EventArgs e).

Thursday 25 August 2011

Convert Text to Path in Xaml

In Expression Blend you can convert any object to a path:  Object Menu > Path > Convert to Path.

Or you can download my WPF app

ClickOnce installer http://stevenhollidge.com/blog-source-code/clickonce/text2path/Text2Path.html
Source code https://github.com/stevenh77/Text2Path

 

Screenshot

image

The Code

<Controls:MetroWindow x:Class="Text2Path.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Text2Path"
Height="500"
Width="660"
ShowMinButton="False"
ShowMaxRestoreButton="False"
ShowIconOnTitleBar="true"
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro">

<Controls:MetroWindow.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colours.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseLight.xaml" />
<ResourceDictionary Source="Resources/Icons.xaml" />
</ResourceDictionary.MergedDictionaries>

<Style TargetType="ComboBox">
<Setter Property="FontFamily" Value="Segoe UI"/>
<Setter Property="FontSize" Value="18"/>
<Setter Property="Margin" Value="10"/>
</Style>

<Style TargetType="TextBox">
<Setter Property="FontFamily" Value="Segoe UI"/>
<Setter Property="FontSize" Value="18"/>
<Setter Property="Margin" Value="10"/>
<Setter Property="Height" Value="30" />
</Style>

<Style TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI"/>
<Setter Property="FontSize" Value="18"/>
<Setter Property="Margin" Value="10"/>
<Setter Property="Height" Value="30" />
</Style>

</ResourceDictionary>

</Controls:MetroWindow.Resources>

<Controls:MetroWindow.WindowCommands>
<Controls:WindowCommands>
<Button x:Name="BlogButton" Content="blog" Click="BlogButton_Click" />
</Controls:WindowCommands>
</Controls:MetroWindow.WindowCommands>

<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="150" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="150" />
<ColumnDefinition Width="150" />
</Grid.ColumnDefinitions>

<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="50" />
<RowDefinition Height="50" />
<RowDefinition Height="100" />
<RowDefinition Height="200" />
</Grid.RowDefinitions>

<TextBlock Text="Text:" Grid.Column="0" Grid.Row="0" />
<TextBox x:Name="TextTextBlock" Grid.Column="1" Grid.Row="0" Grid.ColumnSpan="3" />
<Button x:Name="ConvertButton" Grid.Column="4" Grid.Row="0" Content="Convert" Click="ConvertButton_Click" VerticalAlignment="Top" Margin="10"/>

<TextBlock Text="Culture:" Grid.Column="0" Grid.Row="1" />
<ComboBox x:Name="CultureComboBox" Grid.Column="1" Grid.Row="1" />

<TextBlock Text="Direction:" Grid.Column="2" Grid.Row="1" />
<ComboBox x:Name="DirectionComboBox" Grid.Column="3" Grid.Row="1" IsReadOnly="True" />

<TextBlock Text="Font:" Grid.Column="0" Grid.Row="2" />
<ComboBox x:Name="FontComboBox" Grid.Column="1" Grid.Row="2" />

<TextBlock Text="Font Size:" Grid.Column="2" Grid.Row="2" />
<ComboBox x:Name="FontSizeComboBox" Grid.Column="3" Grid.Row="2" />

<TextBlock Text="Result:" Grid.Row="3" VerticalAlignment="Top" Visibility="Collapsed"/>
<Path x:Name="OutputPath" Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="3"
Stroke="Black" Fill="Black"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>

<TextBlock x:Name="PathTextBlock" Text="Path:" Grid.Row="4" VerticalAlignment="Top" Visibility="Collapsed"/>
<TextBox x:Name="OutputTextBox" Grid.Column="1" Grid.ColumnSpan="3" Grid.Row="4"
IsReadOnly="True"
VerticalAlignment="Stretch"
Visibility="Collapsed"
TextWrapping="WrapWithOverflow"
FontSize="12"
Height="180"/>

<Button x:Name="CopyButton" Visibility="Collapsed" Grid.Column="4" Grid.Row="4" Content="Copy" Click="CopyButton_Click" VerticalAlignment="Top" Margin="10"/>
<TextBlock x:Name="ClipboardResponse" Grid.Column="4" Grid.Row="4" VerticalAlignment="Top" HorizontalAlignment="Center" FontSize="12" Foreground="Red" Margin="0,60,0,0"/>

</Grid>
</Controls:MetroWindow>


 

using System;
using System.Collections;
using System.Drawing.Text;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;

namespace Text2Path
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
Populate();
SetDefaults();
}

private void Populate()
{
CultureComboBox.ItemsSource = Helper.GetAvailableCultures();
DirectionComboBox.ItemsSource = Helper.GetDirections();
FontComboBox.ItemsSource = Helper.GetFonts();
FontSizeComboBox.ItemsSource = Helper.GetFontSizes();
}

private void SetDefaults()
{
TextTextBlock.Text = "Convert this text!";
CultureComboBox.SelectedItem = "en-US";
DirectionComboBox.SelectedItem = "Left to right";
FontComboBox.SelectedItem = "Segoe UI";
FontSizeComboBox.SelectedItem = "48";
}

private void ConvertButton_Click(object sender, RoutedEventArgs e)
{
try
{
this.Cursor = Cursors.Wait;

var path = Helper.Text2Path(TextTextBlock.Text,
CultureComboBox.Text,
(DirectionComboBox.Text == "Left to right"),
FontComboBox.Text,
Int32.Parse(FontSizeComboBox.Text));

OutputTextBox.Text = string.Format("<Path Stroke={0}Black{0} Fill={0}Black{0} Data={0}{1}{0} />", '"', path);
PathTextBlock.Visibility = Visibility.Visible;
CopyButton.Visibility = Visibility.Visible;

OutputPath.Data = Geometry.Parse(path);
}
catch (Exception ex)
{
OutputTextBox.Text = ex.Message;
}
finally
{
OutputTextBox.Visibility = Visibility.Visible;
this.Cursor = Cursors.Arrow;
}
}

private void BlogButton_Click(object sender, RoutedEventArgs e)
{
System.Diagnostics.Process.Start("http://stevenhollidge.blogspot.com");
}

private void CopyButton_Click(object sender, RoutedEventArgs e)
{
try
{
Clipboard.SetData(DataFormats.Text, OutputTextBox.Text);
ClipboardResponse.Text = "Success";
}
catch
{
ClipboardResponse.Text = "Failed";
}
}
}

public class Helper
{
public static string Text2Path(String text, string culture, bool leftToRight, string font, int fontSize)
{
if (culture == "") culture = "en-us";

var ci = new CultureInfo(culture);
var fd = leftToRight ? FlowDirection.LeftToRight : FlowDirection.RightToLeft;
var ff = new FontFamily(font);
var tf = new Typeface(ff, FontStyles.Normal, FontWeights.Normal, FontStretches.Normal);
var t = new FormattedText(text, ci, fd, tf, fontSize, Brushes.Black);
var g = t.BuildGeometry(new Point(0, 0));
var p = g.GetFlattenedPathGeometry();

return p.ToString();
}

public static IEnumerable GetAvailableCultures()
{
return CultureInfo.GetCultures(CultureTypes.SpecificCultures | CultureTypes.NeutralCultures)
.Select(x => x.Name)
.ToList();
}

public static IEnumerable GetDirections()
{
return new [] {"Left to right", "Right to left" };
}

public static IEnumerable GetFonts()
{
return new InstalledFontCollection().Families.Select(font => font.Name).ToList();
}

public static IEnumerable GetFontSizes()
{
return new [] { 8, 9, 10, 11, 12, 14, 16, 18, 20, 24, 26, 28, 36, 48, 72 }.Select(x => x.ToString());
}
}
}

Country Map Fonts

I’ve just stumbled across the GeoBat font which gives you map images – pretty cool!

http://www.dafont.com/geobats.font

ScreenShot114

Wednesday 24 August 2011

Custom Markup Extension Silverlight

Here is an example of a simple custom markup extension.

The custom markup extension is bound to the text property of a textblock with both parameters hardcoded for this example: Value1 = 10 and Value2 = 20.

Giving us the result of:

ScreenShot113

Silverlight5 source code here:

http://stevenhollidge.com/blog-source-code/SimpleCustomMarkupExtension-Calculator.zip

Silverlight DateTime converter

Thursday 18 August 2011

Silverlight Showcases

Always good to check out other Silverlight apps for inspiration, layout designs and widget styles.

Sales Manager Dashboard

dashboard

Market Plus

screenshot1

Some screenshots from Scott Barnes (ex Silverlight Product Manager for Microsoft)

iWantUsrMgmt

VisionOfFIXWPF

5432518031_5ca9f036e9_z

Tuesday 16 August 2011

Silverlight Application Framework

This custom framework contains the following functionality:

Client

  • Ribbon menu bar (from DivElements)
  • Navigation frames for the main container and content pages
  • MVVM architecture
  • MVVM Light Locator to make ViewModels available to Views
  • MVVM Light Messenger for content navigation, status updates and logout
  • MVVM Light RelayEvent for Commanding in View Models
  • MVVM Light EventToCommand for key press binding in ViewModels
  • Custom client-side session data management
  • Mockable dialog box service
  • Custom Silverlight “loader”

Server

  • WCF Services (not WCF RIA Services)
  • Custom ASP.NET Authentication and login UI
  • ASP.NET Authorisation to allow for both secure and unsecure services
  • Text to Path web service to generate Xaml Path data from any string & font

 

Silverlight Application Framework demo from Steven Hollidge on Vimeo.

Navigation with the Ribbon and MVVM Light Messenger

The Ribbon button has a tag property that has the desired Uri set as its content.

When the button is clicked, a message is sent that contains the desired Uri.

The messenger listener receives the message and calls the ContentFrame.NavigateTo method.

Click here to download the source code:

http://stevenhollidge.com/blog-source-code/SilverlightExampleApp.zip

PS    Prefer a Treeview Menu to the Ribbon?

If you prefer the style of a Tree View menu, which is free compared to the Ribbon which is licensed, you can check out my blog posting here.

http://stevenhollidge.blogspot.com/2011/06/treeview-menu-for-silverlight.html

[ScreenShot008%255B5%255D.png]