Showing posts with label DataBinding. Show all posts
Showing posts with label DataBinding. Show all posts

Wednesday, 25 April 2012

Binding Converter Parameters to StaticResources

Here is the bad news, you can’t but here’s the good news, using a bit of jiggery pokery you can easily work around it!

Scenario

image

I had a view containing three ellipses that I wanted to colour based on a property for their data context.  The expected result was blue if the value was zero, red if the value was greater than zero and yellow if something went wrong.

The Obvious Approach Doesn’t Work

I had my colours set in my UserControl.Resources so I would like to have used the following code:

<UserControl.Resources>
<SolidColorBrush x:Name="ZeroBrush" Color="SteelBlue" />
<SolidColorBrush x:Name="OverZeroBrush" Color="Red" />
</UserControl.Resources>

<Ellipse Fill="{Binding Path=Count,
Converter={converters:CountToBrushConverter
ZeroBrush={StaticResource ZeroBrush},
OverZeroBrush={StaticResource OverZeroBrush}}}" />
public class CountToBrushConverter : MarkupExtension, IMultiValueConverter
{
private static CountToBrushConverter _converter;
private readonly SolidColorBrush INCORRECT_USAGE_OF_CONVERTER = new SolidColorBrush(Color.FromArgb(255, 255, 255, 0));

public SolidColorBrush ZeroBrush { get; set; }
public SolidColorBrush OverZeroBrush { get; set; }

public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
int count;
if (!Int32.TryParse(values[0].ToString(), out count)) return INCORRECT_USAGE_OF_CONVERTER;

return count == 0 ? ZeroBrush : OverZeroBrush;
}

public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}

public override object ProvideValue(IServiceProvider serviceProvider)
{
return _converter ?? (_converter = new CountToBrushConverter());
}
}

The problem with this approach is that the value converter properties are not dependency objects and therefore cannot have a binding assigned to them.


The Solution


Swap your IValueConverter for an IMultiValueConverter and use an implementation of the multi-binding approach from WPF. 

<Ellipse Fill="{xaml:MultiBinding Converter={converters:CountToBrushConverter}, 
Source1={Binding Path=Count},
Source2={StaticResource ZeroBrush},
Source3={StaticResource OverZeroBrush}}" />
public class CountToBrushConverter : MarkupExtension, IMultiValueConverter
{
private static CountToBrushConverter _converter;
private readonly SolidColorBrush INCORRECT_USAGE_OF_CONVERTER = new SolidColorBrush(Color.FromArgb(255, 255, 255,0));

/// <summary>
/// Returns a SolidBrushColour based on whether the count = 0 or not
/// </summary>
/// <param name="values">Array of objects: value[0] = Count, value[1] = ZeroBrush, value[2] = OverZeroBrush</param>
/// <param name="targetType">Not used.</param>
/// <param name="parameter">Not used.</param>
/// <param name="culture">Not used.</param>
/// <returns>SolidBrushColor</returns>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null || values.Length != 3) return INCORRECT_USAGE_OF_CONVERTER;

int count;
if (!Int32.TryParse(values[0].ToString(), out count)) return INCORRECT_USAGE_OF_CONVERTER;

var zeroBrush = values[1] as SolidColorBrush;
if (zeroBrush == null) return INCORRECT_USAGE_OF_CONVERTER;

var overZeroBrush = values[2] as SolidColorBrush;
if (overZeroBrush == null) return INCORRECT_USAGE_OF_CONVERTER;

return count == 0
? zeroBrush
: overZeroBrush;
}

public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}

public override object ProvideValue(IServiceProvider serviceProvider)
{
return _converter ?? (_converter = new CountToBrushConverter());
}
}

Multi Bindings for Silverlight


Henrik Jonsson has an implementation on Code Project:
http://www.codeproject.com/Articles/286171/MultiBinding-in-Silverlight-5

Colin Eberhardt has another option on his blog:
http://www.scottlogic.co.uk/blog/colin/2010/05/silverlight-multibinding-solution-for-silverlight-4/

You can download a zip of the version I used:
http://stevenhollidge.com/blog-source-code/MultiBinding.zip

Tuesday, 24 April 2012

Silverlight Glimpse

Silverlight Glimpse

Silverlight Glimpse helps developers track down binding errors, highlight validation errors and unhandled exceptions in their application.

A small Silverlight Glimpse window sits on top of your Silverlight application displaying real time error counts for bindings, validations and exceptions as your application hits it's bugs.

To view the detail of each individual error just click the expand button on the window to show all the relevant information.

This debugging tool written in C# and compiles to a dll which can be referenced from your Silverlight application.

It’s an open source project, written C# and available on CodePlex:  https://silverlightglimpse.codeplex.com/

History

This tool is based upon the original Glimpse for Silverlight 3 written in VB.NET by Karl Shifflett.  For those that don’t know him, he’s a WPF disciple and one of the top guys in the XAML!

His blog post in 2009 on the original project can be found here:

http://karlshifflett.wordpress.com/2009/06/08/glimpse-for-silverlight-viewing-exceptions-and-binding-errors/

Adding Silverlight Glimpse to your App

Simply wrap the setting of your RootVisual with the following code:

private void Application_Startup(object sender, StartupEventArgs e)
{
try
{
this.RootVisual = new MainPage();
Glimpse.Service.Load(this);
}
catch (Exception ex)
{
Glimpse.Service.DisplayLoadFailure(this, ex);
}
}

On Start up


When your application loads Silverlight Glimpse will add a little window in the top left hand corner of the screen.


OneBrokenBinding


Exception on Application Start up


If an exception occurs during the loading of your root visual page, Glimpse will take over and display the relevant information for you to troubleshoot.


ExceptionOnStartup


User Interface


The three numbers relate to the three different types of errors that Silverlight Glimpse can detect:


image


Expanded View


When the user clicks on the Expand button you get to see the detail of the binding errors, validation errors and unhandled exceptions.


There is also a debug tab which I’ll explain shortly.


BindingError


In this example, the Silverlight screen had one binding in the tbDescription textbox as the Path was set to DescriptionWithATypeo.  This helps the developer track down and fix the bug.


ide


Polling vs Real Time


The validation errors and unhandled exceptions are updated in real time.


Unfortunately, due to the Silverlight and security restrictions I wasn’t able to hook into events for the binding errors (which are also output to the Output window in Visual Studio).


Therefore Silverlight Glimpse polls the root visual element and all it’s children to pick up the broken bindings.  The refresh rate defaults to five seconds but you can override that value.


The debug tab shows the length of time it took to traverse the VisualTree to locate the broken bindings.


timebindings


Validation Errors


This user has started entering data which has so far failed on all four of the controls.  Silverlight Glimpse detects these validations based on controls using NotifyOnValidationError:

<TextBox Text="{Binding Username,
Mode=TwoWay,
NotifyOnValidationError=True}" />

This can help developers that are working on custom styles and may not have visual feedback available.


validationerroorsvalidationdetail


Unhandled Exceptions


If your application fails to handle any exceptions these will be detected and displayed in real time.


error


errordetail


Current Known Restrictions


Binding errors and validation errors are not currently being detected in Child Windows (part of the Silverlight SDK).  Exceptions will still be tracked.


Open Source on CodePlex


https://silverlightglimpse.codeplex.com/

Monday, 23 April 2012

PropertyPathHelper

Ever needed to get the value from a binding in code?   Well here you go.

public static class PropertyPathHelper
{
private static readonly Dummy _dummy = new Dummy();

public static object GetValue(object source, string propertyPath)
{
var binding = new Binding(propertyPath)
{
Mode = BindingMode.OneTime,
Source = source
};
BindingOperations.SetBinding(_dummy, Dummy.ValueProperty, binding);
return _dummy.GetValue(Dummy.ValueProperty);
}

private class Dummy : DependencyObject
{
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value",
typeof (object),
typeof (Dummy),
new PropertyMetadata(null));
}
}
Thanks to Thomas Levesque for his answer on Stack Overflow:
http://stackoverflow.com/questions/3577802/wpf-getting-a-property-value-from-a-binding-path

Tuesday, 27 March 2012

WPF Custom Control with Image

In this example we are going to create a custom control in WPF called MetroTile.

Our MetroTile control will expose three properties to let designers set an image icon, a display count and a text property.  The usual core properties for controls are also settable such as background, foreground, margin, etc.

This screenshot shows three instances of the custom control in use, all with the generic template applied.  The RabbitMQ and ZeroMq tiles override the default background. 

image

Class Diagram

image

Key concepts

Three dependency properties that allow the Xaml data binding system to do its work.

I’ve also shown how to expose a bubbling RoutedEvent (travels up the visual tree until handled) through the DisplayCountChanged event.  As is common, I’ve included a Preview RoutedEvent that tunnels down the visual tree, which fires just before the main event.

A public RoutedUICommand called ResetCountCommand is also included which you can bind to from Xaml.  Commands are preferred to methods as you cannot bind directly to methods.

MetroTile Custom Control

Default Template

default-template

The display icon is located in top left hand corner when set by the developer using the custom control.  When the developer first includes the control in their xaml, the following default values are visible on their design surface:

From Template Purple background
  White foreground
From Custom Control Empty display icon
  “Not Set” display text
  Zero display count

Example of MetroTile in use

image

For information on the functionality of this example application, visit the following blog post:  http://stevenhollidge.blogspot.co.uk/2012/03/metro-messaging.html

Custom Control: MetroTile

using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace Common.Controls
{
[TemplatePart(Name="PART_DISPLAY_ICON", Type=typeof(Image))]
[TemplatePart(Name = "PART_DISPLAY_COUNT_CONTAINER", Type = typeof(TextBlock))]
[TemplatePart(Name = "PART_DISPLAY_TITLE_CONTAINER", Type = typeof(TextBlock))]
[Description("Represents a metro styled tile that displays an icon, a count and a title")]
public class MetroTile : Control
{
#region Constructor

static MetroTile()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MetroTile),
new FrameworkPropertyMetadata(typeof(MetroTile)));

CommandManager.RegisterClassCommandBinding(
typeof(MetroTile),
new CommandBinding(ResetCountCommand, OnResetCountCommand));
}

#endregion

#region Dependency Properties

#region DisplayIcon

public static readonly DependencyProperty DisplayIconProperty =
DependencyProperty.Register("DisplayIcon",
typeof (ImageSource),
typeof (MetroTile),
new PropertyMetadata(null));

[Description("The icon displayed in the tile."), Category("Common Properties")]
public ImageSource DisplayIcon
{
get { return (ImageSource)this.GetValue(DisplayIconProperty); }
set { this.SetValue(DisplayIconProperty, value); }
}

#endregion

#region DisplayCount

public static readonly DependencyProperty DisplayCountProperty =
DependencyProperty.Register("DisplayCount",
typeof(int),
typeof(MetroTile),
new UIPropertyMetadata(0));

[Description("The count displayed in the tile."), Category("Common Properties")]
public int DisplayCount
{
get { return (int)this.GetValue(DisplayCountProperty); }
set { this.SetValue(DisplayCountProperty, value); }
}

#endregion

#region DisplayText

[Description("The text displayed in the tile."), Category("Common Properties")]
public static readonly DependencyProperty DisplayTextProperty =
DependencyProperty.Register("DisplayText",
typeof(string),
typeof(MetroTile),
new PropertyMetadata("Not set"));

public string DisplayText
{
get { return (string)this.GetValue(DisplayTextProperty); }
set { this.SetValue(DisplayTextProperty, value); }
}
#endregion

#endregion

#region Events

public static readonly RoutedEvent DisplayCountChangedEvent =
EventManager.RegisterRoutedEvent("DisplayCountChanged",
RoutingStrategy.Bubble,
typeof(RoutedEventHandler),
typeof(MetroTile));

public event RoutedEventHandler DisplayCountChanged
{
add { AddHandler(DisplayCountChangedEvent, value); }
remove { RemoveHandler(DisplayCountChangedEvent, value); }
}

protected virtual void OnDisplayCountChanged(int oldValue, int newValue)
{
// 1. Pair of events: A preview that tunnels and a main event that bubbles
// 2. We could also create our own RoutedEventArgs that includes oldValue & new Value

var previewEvent = new RoutedEventArgs(PreviewDisplayCountChangedEvent);
RaiseEvent(previewEvent);

var e = new RoutedEventArgs(DisplayCountChangedEvent);
RaiseEvent(e);
}

public static readonly RoutedEvent PreviewDisplayCountChangedEvent =
EventManager.RegisterRoutedEvent("PreviewDisplayCountChanged",
RoutingStrategy.Tunnel,
typeof(RoutedEventHandler),
typeof(MetroTile));

public event RoutedEventHandler PreviewDisplayCountChanged
{
add { AddHandler(PreviewDisplayCountChangedEvent, value); }
remove { RemoveHandler(PreviewDisplayCountChangedEvent, value); }
}

#endregion

#region Commands

public static readonly ICommand ResetCountCommand =
new RoutedUICommand("ResetCount", "ResetCount", typeof(MetroTile));

private static void OnResetCountCommand(object sender, ExecutedRoutedEventArgs e)
{
var target = (MetroTile)sender;
target.DisplayCount = 0;
}

#endregion

}
}
<Style TargetType="{x:Type c:MetroTile}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type c:MetroTile}">
<Border Background="{StaticResource MetroPurpleBrush}">
<Grid Background="{Binding Path=Background, RelativeSource={RelativeSource TemplatedParent}}">

<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="1.25*" />
</Grid.RowDefinitions>

<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

<Image x:Name="PART_DISPLAY_ICON"
Grid.Row="0"
Grid.Column="0"
Margin="10,10,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Source="{Binding Path=DisplayIcon,
RelativeSource={RelativeSource TemplatedParent},
UpdateSourceTrigger=PropertyChanged}"
Stretch="None" />

<TextBlock x:Name="PART_DISPLAY_COUNT_CONTAINER"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="0,0,10,0"
HorizontalAlignment="Right"
FontSize="48"
Foreground="White"
Text="{Binding Path=DisplayCount,
StringFormat=N0,
RelativeSource={RelativeSource TemplatedParent},
UpdateSourceTrigger=PropertyChanged}" />

<TextBlock x:Name="PART_DISPLAY_TITLE_CONTAINER"
Grid.Row="2"
Grid.RowSpan="2"
Grid.Column="0"
Grid.ColumnSpan="3"
Margin="10"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
FontSize="24"
Foreground="White"
Text="{Binding Path=DisplayText,
RelativeSource={RelativeSource TemplatedParent},
UpdateSourceTrigger=PropertyChanged}"
TextWrapping="Wrap" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

 


Source code:  http://stevenhollidge.com/blog-source-code/Messaging.zip

Monday, 12 March 2012

Dependency Properties in User Control

In order to be able to expose custom properties that take advantage of xaml data binding, you need to use dependency properties.

In this simple example we create a user control exposing four dependency properties, which will allow our developers to be able to create the following instance:

player-control

Our User Control

class-diagram

For each property you’ll need a dependency property field which gets exposed through a normal .NET property wrapper.  This is to enable Xaml to make use of the data binding system:

PlayerControl.xaml.cs

using System.ComponentModel;
using System.Windows;
using System.Windows.Media;

namespace WpfCustomUserControl
{
public partial class PlayerControl
{
#region Dependency Properties

public static readonly DependencyProperty ShirtNumberProperty =
DependencyProperty.Register("ShirtNumber",
typeof(int),
typeof(PlayerControl),
new PropertyMetadata(0));

[Bindable(true)]
public int ShirtNumber
{
get { return (int)this.GetValue(ShirtNumberProperty); }
set { this.SetValue(ShirtNumberProperty, value); }
}

public static readonly DependencyProperty KitColorProperty =
DependencyProperty.Register("KitColor",
typeof(Brush),
typeof(PlayerControl),
new PropertyMetadata(new SolidColorBrush(Color.FromArgb(0, 0, 0, 0))));

[Bindable(true)]
public Brush KitColor
{
get { return (Brush)this.GetValue(KitColorProperty); }
set { this.SetValue(KitColorProperty, value); }
}

public static readonly DependencyProperty SurnameProperty =
DependencyProperty.Register("Surname",
typeof(string),
typeof(PlayerControl),
new PropertyMetadata("Not set"));

[Bindable(true)]
public string Surname
{
get { return (string)this.GetValue(SurnameProperty); }
set { this.SetValue(SurnameProperty, value); }
}

public static readonly DependencyProperty PositionProperty =
DependencyProperty.Register("Position",
typeof(string),
typeof(PlayerControl),
new PropertyMetadata("Not set"));

[Bindable(true)]
public string Position
{
get { return (string)this.GetValue(PositionProperty); }
set { this.SetValue(PositionProperty, value); }
}

#endregion

public PlayerControl()
{
InitializeComponent();
}
}
}

Note: I've included a bindable attribute to the dependency properties that is not required but I add as a best practice.


The xaml for our user control can then hook up the data binding.


PlayerControl.xaml

<UserControl x:Class="WpfCustomUserControl.PlayerControl"
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"
x:Name="CustomPlayer"
Width="120"
Height="90"
mc:Ignorable="d">
<Grid>
<Button>
<StackPanel>
<Ellipse Width="30"
Height="30"
Fill="{Binding Path=KitColor,
ElementName=CustomPlayer,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Margin="0,-25"
HorizontalAlignment="Center"
FontSize="16"
Foreground="White"
Text="{Binding Path=ShirtNumber,
ElementName=CustomPlayer,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Margin="0,10,0,5"
HorizontalAlignment="Center"
FontSize="15"
Text="{Binding Path=Surname,
ElementName=CustomPlayer,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />

<TextBlock HorizontalAlignment="Center"
Text="{Binding Path=Position,
ElementName=CustomPlayer,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Button>
</Grid>
</UserControl>


Note: No data binding is set on the user control as this will be set at runtime by the developer using the usercontrol. The element name within the usercontrols controls points to itself to enable data binding to work.


We can now easily reuse our user control setting the properties for each instance.


England-team


MainWindow.xaml


<Window x:Class="WpfCustomUserControl.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfCustomUserControl"
Title="England Footy Team"
Icon="Football.ico"
Height="500" Width="600">
<Grid x:Name="mainGrid">
<Grid.RowDefinitions>
<RowDefinition Height="10"/>
<RowDefinition Height="100"/>
<RowDefinition Height="120"/>
<RowDefinition Height="120"/>
<RowDefinition Height="120"/>
<RowDefinition Height="10"/>
</Grid.RowDefinitions>

<Grid.Resources>
<Style TargetType="WrapPanel">
<Setter Property="HorizontalAlignment" Value="Center" />
</Style>
</Grid.Resources>

<WrapPanel Name="panelGoalKeeper" Grid.Column="0" Grid.Row="1">
<local:PlayerControl x:Name="btnGK"
ShirtNumber="1"
KitColor="Green"
Surname="Hart"
Position="Keeper" />
</WrapPanel>

<WrapPanel Name="panelDefenders" Grid.Column="0" Grid.Row="2">
<local:PlayerControl x:Name="btnDF1"
ShirtNumber="2"
KitColor="Crimson"
Surname="Richards"
Position="Strong as an Ox"
Margin="0,25,25,0"/>
<local:PlayerControl x:Name="btnDF2"
ShirtNumber="5"
KitColor="Crimson"
Surname="Cahill"
Position="Ball playing stopper"
Margin="0,0,5,0"/>
<local:PlayerControl x:Name="btnDF3"
ShirtNumber="6"
KitColor="Crimson"
Surname="Jones"
Position="Marauding Sweeper"
Margin="5,0,0,0"/>
<local:PlayerControl x:Name="btnDF4"
ShirtNumber="3"
KitColor="Crimson"
Surname="Baines"
Position="Freekick specialist"
Margin="25,25,0,0"/>

</WrapPanel>

<WrapPanel Name="panelMidfielders" Grid.Column="0" Grid.Row="3">

<local:PlayerControl x:Name="btnMD1"
ShirtNumber="7"
KitColor="Crimson"
Surname="Wilshire"
Position="Playmaker"
Margin="0,25,25,0"/>
<local:PlayerControl x:Name="btnMD2"
ShirtNumber="4"
KitColor="Crimson"
Surname="Parker"
Position="Battler" />
<local:PlayerControl x:Name="btnMD3"
ShirtNumber="8"
KitColor="Crimson"
Surname="Gerrard"
Position="Attacking Midfielder"
Margin="25,25,0,0"/>
</WrapPanel>

<WrapPanel Name="panelStrikers" Grid.Column="0" Grid.Row="4">
<local:PlayerControl x:Name="btnST1"
ShirtNumber="9"
KitColor="Crimson"
Surname="Sturridge"
Position="The Future is bright"
Margin="0,25,25,0"/>
<local:PlayerControl x:Name="btnST2"
ShirtNumber="10"
KitColor="Crimson"
Surname="Rooney"
Position="Number 10" />
<local:PlayerControl x:Name="btnST3"
ShirtNumber="11"
KitColor="Crimson"
Surname="Welbeck"
Position="Striker"
Margin="25,25,0,0"/>
</WrapPanel>
</Grid>
</Window>

Source code:  http://stevenhollidge.com/blog-source-code/WpfCustomUserControl.zip

Thursday, 16 June 2011

Consuming POX in Silverlight

We have the simple scenario of a web server exposing a menu data as plain old xml (POX).  Our Silverlight application would like to consume this data and display it in a simple list box. 

Here’s how it’s done:

ScreenShot010

Data file (directly exposed on web server)

Mapping (on client)

Service Call (on client)

Data Binding (on client)

It’s as easy as that.

You can download the source code here:

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

Tuesday, 24 May 2011

Dependency Objects vs INotifyPropertyChanged

WPF and Silverlight user interfaces are all about data binding. 

To allow your POCO (plain old CLR objects) to take advantage of two way data binding, your object needs to raise the change event.  This can be done by either specialising from DependencyObject or by implementing the INotifyPropertyChanged interface.

Here is a simple example of both in action.

DependencyObject

INotifyPropertyChanged

You can then bind your UI controls to your objects by setting the DataContext and ItemsSource of the control:

Now you may have noticed I’ve added some validation for the Age property.  In my next blog article I’ll show why I find INotifyPropertyChanged a more flexible solution and gives the ability for contextual validation.