Tuesday, 28 August 2012

Silverlight Metro Context Menu

Here is a quick example project for creating a Silverlight Metro styled Context Menu.

Live Demo:   http://stevenhollidge.com/blog-source-code/metrocontextmenu

<Application x:Class="MetroContextMenu.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit">
<Application.Resources>
<Style TargetType="toolkit:ContextMenu">
<Setter Property="Background" Value="White" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="Padding" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="toolkit:ContextMenu">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="2">
<Grid>
<ItemsPresenter Margin="{TemplateBinding Padding}" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

<Style TargetType="toolkit:MenuItem">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontSize" Value="14" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="Padding" Value="20,2,20,2" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="toolkit:MenuItem">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Duration="0"
Storyboard.TargetName="Presenter"
Storyboard.TargetProperty="Opacity"
To="0.1" />
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Unfocused" />
<VisualState x:Name="Focused">
<Storyboard>
<DoubleAnimation Duration="0"
Storyboard.TargetName="Bg"
Storyboard.TargetProperty="Opacity"
To="1" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle x:Name="Bg"
Fill="#34C5EBFF"
Opacity="0"
Stroke="#8071CBF1"
StrokeThickness="1" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

<ContentPresenter x:Name="Presenter"
Margin="{TemplateBinding Padding}"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}" />
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
</Application>

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;

namespace MetroContextMenu
{
public class TextBoxCutCopyPasteContextMenuBehavior : Behavior<TextBox>
{
private readonly ContextMenu contextMenu;
private readonly MenuItem copyMenuItem;
private readonly MenuItem cutMenuItem;
private readonly MenuItem pasteMenuItem;

public TextBoxCutCopyPasteContextMenuBehavior()
{
contextMenu = new ContextMenu();

cutMenuItem = new MenuItem { Header = "Cut" };
cutMenuItem.Click += CutClick;
contextMenu.Items.Add(cutMenuItem);

copyMenuItem = new MenuItem { Header = "Copy" };
copyMenuItem.Click += CopyClick;
contextMenu.Items.Add(copyMenuItem);

pasteMenuItem = new MenuItem { Header = "Paste" };
pasteMenuItem.Click += PasteClick;
contextMenu.Items.Add(pasteMenuItem);
}

void PasteClick(object sender, RoutedEventArgs e)
{
AssociatedObject.SelectedText = Clipboard.GetText();
contextMenu.IsOpen = false;
}

void CutClick(object sender, RoutedEventArgs e)
{
Clipboard.SetText(AssociatedObject.SelectedText);
AssociatedObject.SelectedText = string.Empty;
AssociatedObject.Focus();
contextMenu.IsOpen = false;
}

void CopyClick(object sender, RoutedEventArgs e)
{
Clipboard.SetText(AssociatedObject.SelectedText);
AssociatedObject.Focus();
contextMenu.IsOpen = false;
}

protected override void OnAttached()
{
AssociatedObject.MouseRightButtonDown += AssociatedObject_MouseRightButtonDown;
AssociatedObject.MouseRightButtonUp += AssociatedObjectMouseRightButtonUp;
AssociatedObject.SetValue(ContextMenuService.ContextMenuProperty, contextMenu);
base.OnAttached();
}

void AssociatedObjectMouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
pasteMenuItem.IsEnabled = Clipboard.ContainsText();

if (string.IsNullOrEmpty(AssociatedObject.SelectedText))
{
cutMenuItem.IsEnabled = false;
copyMenuItem.IsEnabled = false;
}
else
{
cutMenuItem.IsEnabled = true;
copyMenuItem.IsEnabled = true;
}

contextMenu.IsOpen = true;
}

void AssociatedObject_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
}

protected override void OnDetaching()
{
AssociatedObject.MouseRightButtonDown -= AssociatedObject_MouseRightButtonDown;
AssociatedObject.MouseRightButtonUp -= AssociatedObjectMouseRightButtonUp;
base.OnDetaching();
}
}
}

Source code:  https://github.com/stevenh77/MetroContextMenu

Friday, 3 August 2012

LINQ Outer Joins

image
using System;
using System.Linq;

namespace LinqPlay
{
class Program
{
static void Main(string[] args)
{
// REQUIREMENT: Using linq show all beverages for all days
// including days where beverages have zero count

// Enum beverages: Coke, Fanta, Lilt, Sprite
// Date range: yesterday, today and tomorrow
// Stats data: 3 cokes, 1 sprite yesterday
// 1 sprite, 4 Fanta today

// SETUP THE COLLECTIONS OF DATA

string[] beverages = Enum.GetNames(typeof(Beverages));

DateTime yesterday = DateTime.Today.AddDays(-1);
DateTime today = DateTime.Today;
DateTime tomorrow = DateTime.Today.AddDays(1);

DateTime[] dates = new[] { yesterday, today, tomorrow };

var stats = new []
{
new ChartData() {Text = "Coke", Timestamp = yesterday, Value = 3},
new ChartData() {Text = "Sprite", Timestamp = yesterday, Value = 1},
new ChartData() {Text = "Sprite", Timestamp = today, Value = 1},
new ChartData() {Text = "Fanta", Timestamp = today, Value = 4}
};

// JOIN THE DATA

var qry = (from x in (from b in beverages
from d in dates
select new ChartData() {Text = b, Timestamp = d.Date})
join s in stats on new { x.Text, x.Timestamp} equals new { s.Text, s.Timestamp }
into temp1 from temp2 in temp1.DefaultIfEmpty()
select new ChartData()
{
Text = x.Text,
Timestamp = x.Timestamp,
Value = (temp2 == null ? 0 : temp2.Value)
})
.OrderBy(x => x.Timestamp).ThenBy(x => x.Text);

foreach (var chartData in qry)
{
Console.WriteLine(chartData.ToString());
}
}

enum Beverages
{
Coke,
Lilt,
Fanta,
Sprite
}

class ChartData
{
public string Text { get; set; }
public DateTime Timestamp { get; set; }
public int Value { get; set; }

public override string ToString()
{
return string.Format("{0}\t{1}\t{2}", Timestamp.ToString("dd/MM/yyyy"), Text, Value);
}
}
}
}

Enum description attribute

image
using System;
using System.ComponentModel;
using System.Linq;
using System.Reflection;

namespace EnumPlay
{
enum Position
{
[Description("Last line of defence, always crazy")]
Goalie = 0,

[Description("Big fast and love getting stuck in")]
Defender,

[Description("Play makers and tough tacklers")]
Midfielder,

[Description("Goalscorer for the team")]
Forward
}

public static class EnumExtensions
{
public static string GetDescription(this Enum value)
{
FieldInfo fieldInfo = value.GetType().GetField(value.ToString());
DescriptionAttribute attribute = Attribute.GetCustomAttribute(fieldInfo, typeof(DescriptionAttribute)) as DescriptionAttribute;
return attribute == null ? value.ToString() : attribute.Description;
}
}

class Program
{
static void Main(string[] args)
{
OutputValues();
OutputNames();
OutputDescriptions();
}

private static void OutputValues()
{
Console.WriteLine("Values");

var values = Enum.GetValues(typeof(Position));

foreach (int value in values)
{
Console.WriteLine("\t{0}", value);
}

Console.WriteLine();
}

private static void OutputNames()
{
Console.WriteLine("Names");

var names = Enum.GetNames(typeof(Position));

foreach (string name in names)
{
Console.WriteLine("\t{0}", name);
}

Console.WriteLine();
}


private static void OutputDescriptions()
{
Console.WriteLine("Descriptions");

var enums = Enum.GetValues(typeof(Position)).Cast<Position>(); ;

foreach (Position item in enums)
{
var description = item.GetDescription();
Console.WriteLine("\t{0}", description);
}

Console.WriteLine();
}
}
}

Silverlight's missing methods

public static class Enums
{
//EXAMPLE USAGE:
// string[] names = Enums.GetNames<Position>();
public static string[] GetNames<T>()
{
var type = typeof(T);

if (!type.IsEnum) throw new ArgumentException("Type '" + type.Name + "' is not an enum");

return (from field in type.GetFields(BindingFlags.Public | BindingFlags.Static)
where field.IsLiteral
select field.Name).ToArray();
}

// EXAMPLE USAGE:
// Position[] values = Enums.GetValues<Position>();
public static T[] GetValues<T>()
{
var type = typeof(T);

if (!type.IsEnum) throw new ArgumentException("Type '" + type.Name + "' is not an enum");

return (from field in type.GetFields(BindingFlags.Public | BindingFlags.Static)
where field.IsLiteral
select (T)field.GetValue(null)).ToArray();
}

// EXAMPLE USAGE:
// Position position = Position.Goalie;
// string description = position.GetDescription();
public static string GetDescription(this Enum e)
{
FieldInfo fieldInfo = e.GetType().GetField(e.ToString());
DescriptionAttribute attribute = Attribute.GetCustomAttribute(fieldInfo, typeof(DescriptionAttribute)) as DescriptionAttribute;
return attribute == null ? e.ToString() : attribute.Description;
}

// EXAMPLE USAGE:
// string[] descriptions = Enums.GetDescriptions<Position>();
public static string[] GetDescriptions<T>()
{
var type = typeof(T);

if (!type.IsEnum) throw new ArgumentException("Type '" + type.Name + "' is not an enum");

var enums = GetValues<T>();
return (from enumeration in enums
select (enumeration as Enum).GetDescription())
.ToArray();
}
}