Wednesday, 10 December 2008
Universe SEQ function and .Net
This blog has lots of useful info regarding using Universe from .Net
GigaMegaTech
including this nugget about the SEQ() function.
The strings returned from Universe are encoded in ANSI format. So the SEQ function in Universe doesn't return the same values in the extended character set as .Net does. Therefore the need to use:
Encoding.Ansi.GetBytes();
Wednesday, 26 November 2008
WCF and Custom Exceptions
catch (BlockBookingInvalidFloorsException ex)
{
throw new FaultException(ex);
}
Despite the fact that both the custom exception and the contained classes are marked as [Serializable()] it results in a WCF CommunicationException.To resolve this we now add this new constructor:
public BlockBookingInvalidFloorsException(SerializationInfo serializationInfo, StreamingContext streamingContext)
: base(serializationInfo, streamingContext) { }
Martin found this blog with a fuller explanation: WCF and Custom ExceptionsClaus suggests that custom exceptions should derive from either CommunicationException or System.Exception. Our's derive from ApplicationException with no problem, indeed the ApplicationException already includes this constructor (protected).
Sub Classing Window and UserControl
public partial class PersonEditView : WindowBase
{
...
}
public class WindowBase : Window
{
...
}
The xaml then needs to reference the WindowBase class like this:<local:WindowBase x:Class="INPC2.PersonEditView"
...
xmlns:local="clr-namespace:INPC2"
>
<local:WindowBase.Resources>
...
</local:WindowBase.Resources>
...
</local:WindowBase>
There is no problem with the forward reference to xmlns:local
ObjectDataProvider
<ObjectDataProvider x:Key="DesignTimeDataProvider" d:IsDataSource="True" ObjectType="{x:Type Dummy_Application:PeopleDataProvider}"/>
However it is not possible to replace the ObjectType with an ObjectInstance at runtime.We got round that problem by using two ObjectDataProviders:
<ObjectDataProvider x:Key="DesignTimeDataProvider" d:IsDataSource="True" ObjectType="{x:Type Dummy_Application:PeopleDataProvider}"/>
<ObjectDataProvider x:Key="InnerDataProvider" d:IsDataSource="true" ObjectInstance="{StaticResource DesignTimeDataProvider}"/>
<ListBox ItemsSource="{Binding Path=DataProvider.PeopleList, Source={StaticResource InnerDataProvider}}"/>
Then in the code behind we replace the ObjectInstance with our freshly minted data source:ObjectDataProvider odpWindow = (ObjectDataProvider)this.FindResource("InnerDataProvider");
odpWindow.ObjectInstance= newPeopleDataProvider;
The problem with this approach is that at runtime a datasource is created (as part of InitializeComponent) that is then immediately discarded.To resolve this I created my own ObjectDataProvider which nulls the ObjectType at runtime. This is enough to allow the ODP to accept a change to the ObjectInstance without complaint.
public class MyObjectDataProvider : ObjectDataProvider
{
bool isInDesignMode = (bool)DesignerProperties.IsInDesignModeProperty.GetMetadata(typeof(DependencyObject)).DefaultValue;
public new Type ObjectType
{
get { return base.ObjectType; }
set
{
if (!isInDesignMode) value = null;
base.ObjectType = value;
}
}
}
public partial class PersonEditView : Window
{
public PersonEditView()
{
InitializeComponent();
((ObjectDataProvider)this.FindResource("DataModel")).ObjectInstance = model; }
}
The xaml then becomes simpler and cleaner:<local:MyObjectDataProvider x:Key="DataModel" d:IsDataSource="True" ObjectType="{x:Type local:PersonEditModel}" />
<TextBlock Text="{Binding Source={StaticResource DataModel}, Path=APerson.Surname}"/>
INotifyPropertyChanged
So I created a little project to test my assumptions.
1) Business Class implements INotifyPropertyChanged
public class Person : INotifyPropertyChanged
{
private string _surname = "";
public string Surname
{
get { return _surname; }
set
{
_surname = value;
OnPropertyChanged("Surname");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
2) Model exposing two instances of Person. Model does not implement INPCpublic class Model
{
public Person APerson { get; set; }
public Person AnotherPerson { get; set; }
public Model()
{
APerson = new Person();
AnotherPerson = new Person();
}
}
3) Bind to only one of the two instances of Person in the Model<TextBlock Text="{Binding Model.APerson.Surname}"></TextBlock>
<Button Click="Button_Click">Go</Button>
4) Wire it all uppublic partial class Window2 : Window
{
private Model _model = new Model();
public Model Model
{
get { return _model; }
set { _model = value; }
}
public Window2()
{
InitializeComponent();
DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Model.APerson.Surname = "Smith";
Model.AnotherPerson.Surname = "Jones";
}
}
Result:PropertyChanged is only hooked up for APerson and not for AnotherPerson because nothing is bound to AnotherPerson in the xaml.
Conclusion:
I might need to revisit the changes I made to AbstractModel hooking up PropertyChanged and DataError. Currently they are hooked up when the Model is instantiated, regardless of whether there is any binding in the xaml.
Quiz - guess the technology
I've been vocal at work about how stupid I think xxxxxx is.
Mike says:
Why is it stupid?
Jeremiah says:
Because it looks snazzy 4 seconds after you've put in a feature but then 4 months later it still isn't quite right; takes forever to make minor tweaks; doesn't have anything to offer over simple HTML (other than the RAD) but has less features.
Wednesday, 19 November 2008
Custom Control Part 2
This needs a couple of private member vars (OldText & Initialised) to hold the state, a new DependencyProperty (HasChangedProperty), a standard CLR property (HasChanged) and some logic added to the TextPropertyChanged event handler.
public class DemoTextBox : TextBox
{
static DemoTextBox()
{
// Create a callback for when the TextProperty changes
PropertyChangedCallback callback = new PropertyChangedCallback(TextPropertyChanged);
// Override the TextProperty metadata in order to link up the callback routine
TextProperty.OverrideMetadata(
typeof(DemoTextBox),
new FrameworkPropertyMetadata(callback));
}
static void TextPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
// Cast the Sender to an object of this class
DemoTextBox textBox = (DemoTextBox)sender;
// Store the OldText value when this event is
// fired for the first time
if (!textBox.Initialised)
{
textBox.Initialised = true;
textBox.OldText = textBox.Text;
}
// Set the HasChangedProperty to indicate whether the text has changed
textBox.SetValue(HasChangedProperty, (textBox.OldText != textBox.Text));
}
private bool Initialised = false;
private string OldText = "";
// The new DependencyProperty
static readonly DependencyProperty HasChangedProperty = DependencyProperty.Register(
"HasChanged",
typeof(bool),
typeof(DemoTextBox)
);
// The new CLR Property
public bool HasChanged
{
get { return (bool)GetValue(HasChangedProperty); }
}
}
In the Generic.xaml we'll add a Trigger to turn the textbox background Red when the Text value has changed.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DepProp">
<Style TargetType="{x:Type local:DemoTextBox}">
<Style.Triggers>
<Trigger Property="HasChanged" Value="true">
<Setter Property="Background" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
Tuesday, 18 November 2008
Custom Control Part 1
Starting with a new class that inherits from TextBox:
public class DemoTextBox : TextBox
{
static DemoTextBox()
{
// Create a callback for when the TextProperty changes
PropertyChangedCallback callback = new PropertyChangedCallback(TextPropertyChanged);
// Override the TextProperty metadata in order to link up the callback routine
TextProperty.OverrideMetadata(
typeof(DemoTextBox),
new FrameworkPropertyMetadata(callback));
}
static void TextPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
// Cast the Sender to an object of this class
DemoTextBox textBox = (DemoTextBox)sender;
}
}
Add a ResourceDictionary called Generic.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DepProp>
<Style TargetType="{x:Type local:DemoTextBox}">
<Style.Triggers>
<!-- create a trigger here -->
</Style.Triggers>
</Style>
</ResourceDictionary>
And a reference to the new control in the Window:
<Window x:Class="DepProp.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DepProp"
Title="Window2" Height="300" Width="300">
<Window.Resources>
<ResourceDictionary Source="Generic.xaml"/>
</Window.Resources>
<StackPanel>
<local:DemoTextBox x:Name="TextBoxV1" Text="Original Value" />
</StackPanel>
</Window>
ActiveSharp and StackTrace speed
All the following times are for one million iterations.
Technique | Milliseconds |
ActiveSharp | 400 |
StackWalk | 120000 |
MethodBase.GetCurrentMethod() | 2000 |
"string" | 20 |
const string | 10 |
The speed of ActiveSharp is a little unrealistic because it caches the Property Name for each field in the class. A class with more properties or accessing many different objects would reduce the speed advantage.
Since the StackWalk was so slow I decided to check whether it was my code (examining the stack) or the creation of the stack in the first instance.
private void Button_Click_2(object sender, RoutedEventArgs e)
{
DateTime start = DateTime.Now;
for (int i = 0; i <= 1000000; i++)
{
StackTrace trace = new StackTrace();
}
DateTime end = DateTime.Now;
MessageBox.Show(end.Subtract(start).TotalMilliseconds.ToString());
}
One million iterations took 118,000 milliseconds to complete. There is little point trying to improve my code further.
MultiBinding
There is a nice simple example from MSDN here: MultiBinding Class
Monday, 17 November 2008
ActiveSharp
Since we already have a abstract class that implements INPC I simply used the PropertyMap.GetProperty(this, ref target) method to get the property name and avoid having to hardcode the name as a string in each Property.
I ran into a slight problem because we have some classes that expose the same field in multiple properties (not necessarily the right thing to do but that's what we've got) and ActiveSharp can't deal with this situation.
My proposed solution was to change ActiveSharp to walk the Stack when it encountered these duplicates. John was kind enough to explain the potential pitfalls and the alternative technique of using marking all Properties with this attribute:
[System.Runtime.CompilerServices.MethodImpl(
System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
The primary issue is around "inlining" methods when the Release code is built. This can result in fewer entries being available on the stack. Therefore a modified GetProperty might work during development but not live.
I read that there are some situations that prevent inlining:
- Methods that are greater than 32 bytes of IL will not be inlined.
- Virtual functions are not inlined.
- Methods that have complex flow control will not be in-lined. Complex flow control is any flow control other than if/then/else; in this case, switch or while.
- Methods that contain exception-handling blocks are not inlined, though methods that throw exceptions are still candidates for inlining.
- If any of the method's formal arguments are structs, the method will not be inlined.
For now this appears to be working OK but I might need to revisit when we move to 64bit Windows Release IS NOT Debug: 64bit Optimizations and C# Method Inlining in Release Build Call Stacks
Important Note:Running the Release version from within the IDE does not give the same results as running the application as a standalone .exe
Speed of Exceptions
In reality the best solution will depend on the likely hit rate of duplicates but I thought I'd write the following code to test the speed differential when the hit rate is 100%.
Using ContainsKey
private void Button_Click(object sender, RoutedEventArgs e)
{
DateTime start = DateTime.Now;
for (int i = 0; i <= 1000; i++)
{
if (dummy.ContainsKey("Sunday"))
{
dummy["Sunday"]++;
}
}
DateTime end = DateTime.Now;
MessageBox.Show(end.Subtract(start).TotalMilliseconds.ToString());
}
Using Exceptions
private void Button_Click_1(object sender, RoutedEventArgs e)
{
DateTime start = DateTime.Now;
for (int i = 0; i <= 1000; i++)
{
try
{
dummy.Add("Sunday", 7);
}
catch
{
dummy["Sunday"]++;
}
}
DateTime end = DateTime.Now;
MessageBox.Show(end.Subtract(start).TotalMilliseconds.ToString());
}
Results
Using ContainsKey: 2 milliseconds
Using Exceptions: 5338 milliseconds
The ContainsKey adds about 0.04 milliseconds for the 1000 iterations - the rest is the down to the increment of the Dictionary int value.
Monday, 3 November 2008
DelegateCommand vs RoutedCommand vs EventHandler
DelegateCommand
SimplePresenter.cs
public DelegateCommand<string> GetCustomerByDC { get; private set; }
GetCustomerByDC = new DelegateCommand(ExecuteGetCustomer, CanExecuteGetCustomer);
private void ExecuteGetCustomer(string parameter)
{
try
{
int customerId = int.Parse(parameter);
GetCustomer(customerId);
}
catch { }
}
}
private bool CanExecuteGetCustomer(string parameter)
{
try
{
int customerId = int.Parse(parameter);
return true;
}
catch
{
return false;
}
}
SimpleView.xaml.cs
ObjectDataProvider odpPresenter = (ObjectDataProvider)this.FindResource("MyPresenter");
Presenter = (SimplePresenter)odpPresenter.ObjectInstance;
Presenter.Model = newModel as SimpleModel;
Presenter.View = this;
private void txtCustomerId_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
Presenter.GetCustomerByDC.RaiseCanExecuteChanged();
}
SimpleView.xaml
xmlns:Presenters="clr-namespace:Sandstorm.Documents.Patterns.Presenters"
<ObjectDataProvider x:Key="MyPresenter" d:IsDataSource="True" ObjectType="{x:Type Presenters:SimplePresenter}"/>
<Button
x:Name="btnGetByDC"
Command="{Binding Source={StaticResource MyPresenter}, Path=GetCustomerByDC}"
CommandParameter="{Binding ElementName=txtCustomerId, Path=Text}"
Width="170"
>Get By DelegateCommand</Button>
<TextBox
TextChanged="txtCustomerId_TextChanged"
/>
RoutedCommand
SimpleView.xaml.cs
private void GetCustomer_Executed(object sender, System.Windows.Input.ExecutedRoutedEventArgs e)
{
if (e.Parameter != null)
{
try
{
int customerId = int.Parse(e.Parameter as string);
Presenter.GetCustomer(customerId);
}
catch { }
}
}
private void GetCustomer_CanExecute(object sender, System.Windows.Input.CanExecuteRoutedEventArgs e)
{
try
{
int customerId = int.Parse(e.Parameter as string);
e.CanExecute = true;
}
catch
{
e.CanExecute = false;
}
}
SimpleView.xaml
<UserControl.CommandBindings>
<CommandBinding
Command="local:LeisureCommands.GetCustomerCommand"
Executed="GetCustomer_Executed"
CanExecute="GetCustomer_CanExecute"
/>
</UserControl.CommandBindings>
<Button
x:Name="btnGetByRC"
Command="local:LeisureCommands.GetCustomerCommand"
CommandParameter="{Binding ElementName=txtCustomerId, Path=Text}"
Width="170"
>Get By Routed Command</Button>
LeisureCommands.cs
public class LeisureCommands : RoutedUICommand
{
public readonly static RoutedUICommand GetCustomerCommand;
static LeisureCommands()
{
GetCustomerCommand = new RoutedUICommand("Get Customer...", "GetCustomerCommand", typeof(LeisureCommands));
GetCustomerCommand.InputGestures.Add(new KeyGesture(Key.C, ModifierKeys.Alt));
}
}
Event
SimpleView.xaml.cs
private void btnGetByClick_Click(object sender, RoutedEventArgs e)
{
try
{
int customerId = int.Parse(txtCustomerId.Text);
Presenter.GetCustomer(customerId);
}
catch { }
}
SimpleView.xaml
<Button
x:Name="btnGetByClick"
Click="btnGetByClick_Click"
CommandParameter="{Binding ElementName=txtCustomerId, Path=Text}"
Width="170"
>Get By Click Event</Button>
Lines of code
Type | Raw | OneOff | Per Command | Can Execute | Total |
DelegateCommand | 40 | 6 | 34 | 12 | 22 |
RoutedCommand | 45 | 7 | 38 | 12 | 26 |
Event | 15 | 0 | 15 | n/a | 15 |
Conclusion
The RoutedCommand is a little more expensive in LOC terms than the DelegateCommand but the DelegateCommand does not support all the features of the RoutedCommand, in particular: Gestures.
If the "CanExecute" facility is not required the Event is by far the cheapest / easiest to implement.
I guess my conclusion was: "What are the DelegateCommands bringing to the party?"
Thursday, 30 October 2008
Routed Commands
The Command Pattern In Windows Presentation Foundation
DelegateCommand<T> class implements ICommand rather than subclassing RoutedCommand. The problem with this is that CanExecute is fired only once. RoutedCommands listen to a number of events and fire CanExecute quite frequently.
DelegateCommand<T> offers the RaiseCanExecuteChanged() method which can be fired manually when some "significant" event happens.
Alternatively a timer could be used to call RaiseCanExecuteChanged() at regular intervals. The following code sample uses a DispatcherTimer to raise the event on the UI thread.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_aTimer.Tick += new EventHandler(_aTimer_Tick);
_aTimer.Interval = new TimeSpan(0, 0, 1);
_aTimer.IsEnabled = false;
}
void _aTimer_Tick(object sender, EventArgs e)
{
_model.MyCommand.RaiseCanExecuteChanged();
}
These are the standard supplied commands:
ApplicationCommands—Close, Copy, Cut, Delete, Find, Help, New, Open, Paste, Print, PrintPreview, Properties, Redo, Replace, Save, SaveAs, SelectAll, Stop, Undo, and more
ComponentCommands—MoveDown, MoveLeft, MoveRight, MoveUp, ScrollByLine, ScrollPageDown, ScrollPageLeft, ScrollPageRight, ScrollPageUp, SelectToEnd, SelectToHome, SelectToPageDown, SelectToPageUp, and more
MediaCommands—ChannelDown, ChannelUp, DecreaseVolume, FastForward, IncreaseVolume, MuteVolume, NextTrack, Pause, Play, PreviousTrack, Record, Rewind, Select, Stop, and more
NavigationCommands—BrowseBack, BrowseForward, BrowseHome, BrowseStop, Favorites, FirstPage, GoToPage, LastPage, NextPage, PreviousPage, Refresh, Search, Zoom, and more
EditingCommands—AlignCenter, AlignJustify, AlignLeft, AlignRight, CorrectSpellingError, DecreaseFontSize, DecreaseIndentation, EnterLineBreak, EnterParagraphBreak, IgnoreSpellingError, IncreaseFontSize, IncreaseIndentation, MoveDownByLine, MoveDownByPage, MoveDownByParagraph, MoveLeftByCharacter, MoveLeftByWord, MoveRightByCharacter, MoveRightByWord, and more
Tuesday, 28 October 2008
Sunday, 26 October 2008
Accessing Properties from abstract class
I use reflection to get all the properties of the derived class then check that they implement the INotifyPropertyChanged interface before adding the event handler.
The Property must be defined in the "traditional" way rather than using the auto-property syntax otherwise the InitializeProperties method in the abstract class has no object to add the event handlers to.
public class Model : AbstractModel
{
private Till _till = new Till();
public Till Till
{
get { return _till; }
set { _till = value; }
}
}
public abstract class AbstractModel : INotifyPropertyChanged
{
public AbstractModel()
{
InitializeProperties();
}
private void InitializeProperties()
{
PropertyInfo[] propertyInfoList = this.GetType().GetProperties();
foreach (PropertyInfo propertyInfo in propertyInfoList)
{
object observedObject = propertyInfo.GetValue(this, null);
if (observedObject is INotifyPropertyChanged)
{
(observedObject as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(BusinessPropertyChanged);
}
}
}
...
}
Saturday, 25 October 2008
Generic Constraints
Constraint | Meaning in Life |
where T : struct | The type parameter <T> must have System.ValueType in its chain of inheritance. |
where T : class | The type parameter <T> must not have System.ValueType in its chain of inheritance (e.g., <T> must be a reference type). |
where T : new() | The type parameter <T> must have a default constructor. This is very helpful if your generic type must create an instance of the type parameter, as you cannot assume the format of custom constructors. Note that this constraint must be listed last on a multiconstrained type. |
where T : NameOfBaseClass | The type parameter <T> must be derived from the class specified by NameOfBaseClass. |
where T : NameOfInterface | The type parameter <T> must implement the interface specified by NameOfInterface. |
The syntax for defining both inheritance and constraints is:
class FSM: AbstractFSM, INotifyPropertyChanged
where TState : struct
where TEvent : struct
{
To define multiple constraints for a single type parameter separate them with commas. It seems like "new()" has to be the last in the list.
private void CollectionConstructorTest()
where TVCollection : VCollection, new()
where TVClass : VClass, new()
{
WPF Cross-Thread Collection Binding
The "grand solution" can be found here: WPF Cross-Thread Collection Binding - Part 4 - The Grand Solution
Features
-Allows WPF to bind to a collection that is modified from any thread
-Allows WPF to bind to properties of items in the collection that are modified from any thread
-No lock required if used from a single worker thread
-Relatively simple (compared to some other attempts)
-Non-blocking
Downsides
-Requires two lists internally
-The ObservableCollection list should not be modified
-Disposable or DependencyObjects should not be used
I've implemented the ObservableList in the test project InABind but we haven't confirmed the overhead involved of using ObservableList in place of ObservableCollection.
Memory Usage
I ran a little test app and its RAM footprint topped out at 39mb when run inside the VS2008 IDE. Running as a standalone exe it used a more "modest" 23mb. Interestingly it used no CPU time when idle.
Finding memory leaks in WPF based applications
Avoiding WPF Memory leak with Databinding
A memory leak may occur when you use data binding in Windows Presentation Foundation
Martin and I concluded that we need to implement INotifyPropertyChanged on any class that will be involved in Databinding. The most likely route will be to derive all the relevant classes from a base class that implements INPC.
This tool has a "visual profiler" which shows a graph of CPU usage.
WPF Performance Tool
Tuesday, 21 October 2008
INotifyPropertyChanged
[field:NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;
That attribute prevents the delegate field which the compiler emits to implement the PropertyChanged event from being serialized. Since a delegate internally stores hard references to its listeners, you need to prevent that delegate from being serialized. If the delegate were serialized then it would attempt to serialize the objects to which it points.
Expression Blend - with Diagnostics
http://blogs.msdn.com/johngossman/archive/2008/10/16/expression-architecture.aspx
Sunday, 19 October 2008
ObservableCollection<T>
Caveats:
The business object must implement INotifyPropertyChanged.
There is no "undo" mechanism to set the HasChanges flag back to false
If an object of this type is bound directly to the DataContext (or ObjectDataProvider) the HasChanges property can be referenced directly in the xaml.public class MyObservableCollection
: ObservableCollection
{
public new void Add(T item)
{
// If the class represented by T implements the INotifyPropertyChanged
// interface hook up the PropertyChanged event handler
if ((item as INotifyPropertyChanged) != null)
{
(item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(OnItemPropertyChanged);
}
base.Add(item);
}
public bool HasChanges { get; set; }
public event PropertyChangedEventHandler ItemPropertyChanged;
public MyObservableCollection()
{
HasChanges = false;
}
protected void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
HasChanges = true;
OnPropertyChanged("HasChanges");
}
protected void OnPropertyChanged(string propertyName)
{
if (ItemPropertyChanged != null)
{
ItemPropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
If the collection object is intantiated by a Model class the Model will need to implement INotifyPropertyChanged and expose its own HasChanges property with its value set by wiring up the ItemPropertyChanged event:
public MyObservableCollection
Abc { get; set; }
public Model()
{
Abc = new MyObservableCollection();
Abc.ItemPropertyChanged += new PropertyChangedEventHandler(BusinessPropertyChanged);
}
public bool HasChanges
{
get { return Abc.HasChanges; }
}
protected void BusinessPropertyChanged(object sender, PropertyChangedEventArgs e)
{
OnPropertyChanged("HasChanges");
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Friday, 17 October 2008
ObservableCollection<T>.CollectionChanged
However it does not occur if a property on an object in the collection changes, even if that object implements the INotifyPropertyChanged.
The new value of the object's property is reflected in the UI automatically (as you would expect of an ObservableCollection).
I wanted to know that something inside the collection had changed so that I could set a HasChanges property on the Model that the UI is bound to via its ObjectDataProvider. The intention was then to have a visual clue on the UI that was bound to the HasChanges property.
This posting doesn't have a solution, its simply a reminder not to try to use this event for this purpose in the future.
Tuesday, 14 October 2008
Walking the VisualTree
AddHandlers(this);
public void AddHandlers(DependencyObject dependencyObject)
{
int childCount = VisualTreeHelper.GetChildrenCount(dependencyObject);
for (int childIndex = 0; childIndex < childCount; childIndex++)
DependencyObject childObject = VisualTreeHelper.GetChild(dependencyObject, childIndex);
// In this example we're only interested in TextBoxes
if (childObject is TextBox)
{
if ((childObject as TextBox).GetBindingExpression(TextBox.TextProperty).ParentBinding.UpdateSourceTrigger == UpdateSourceTrigger.PropertyChanged)
{
(childObject as TextBox).TextChanged += new TextChangedEventHandler(TextBox_TextChanged);
}
}
AddHandlers(childObject);
}
}
Monday, 13 October 2008
Page / Window Constructor
"Putting code in a Page/Window constructor is bad. Yes, I know it is the “C# way”, but it is bad. The “VB way” of putting code in the Loaded event handler is better.
Why?
Because any exceptions thrown in the constructor prevent the page/window from loading at all, and you have to catch those exceptions in the code that is creating the page/window. In many navigation scenarios you can’t catch them, so the user gets an ugly WPF exception dialog.
However, if you do all your init work in the Loaded event handler, the page/window instance already exists. Navigation has already happened, so the “calling code” is no longer responsible for your page/window going haywire. Instead, you can actually handle the exception and show a nice dialog, explaining to the user what happened, and you can (if desired) navigate somewhere else or whatever."
Monday, 6 October 2008
Sorting a collection using Linq and 'SortExpression' string
This is how it can be done:
public static IEnumerable Sort(this IEnumerable source, string sortExpression)
{
string[] sortParts = sortExpression.Split(' ');
var param = Expression.Parameter(typeof(T), string.Empty);
try
{
var property = Expression.Property(param, sortParts[0]);
var sortLambda = Expression.Lambda>(Expression.Convert(property, typeof(object)), param);
if (sortParts.Length > 1 && sortParts[1].Equals("desc", StringComparison.OrdinalIgnoreCase))
{
return source.AsQueryable().OrderByDescending(sortLambda);
}
return source.AsQueryable().OrderBy(sortLambda);
}
catch (ArgumentException)
{
return source;
}
}
Just drop it in a static class, and you will be able to sort any collection that implement the interface IEnumerable.
Lets say you have a class 'User':
public class User
{
public int ID { get; set; }
public string Name { get; set; }
}
and a List
IEnumerable sortedUsersIEnumerable = users.Sort("ID desc");
Or
List sortedUsersList = users.Sort("Name").ToList();
Sunday, 5 October 2008
WPF Keyboard Focus
Part 1: It's Basically Focus
Part 2: Changing WPF Focus in Code
Part 3: Shifting focus to the first available element in WPF
Exceptions in Converters
It is vital that your value converters not throw or be allowed to bubble exceptions because the data binding pipeline does not catch, swallow or handle these exceptions. Instead your users will get those messy exceptions messages because there is no method to catch and handle exceptions in XAML markup. Ensure that if your converter fails that you gracefully handle this conversion or convert back operation.
Validation and Conversion
This order can be changed by specifying the ValidationStep.
<TextBox
MinWidth="100"
Style="{StaticResource tbStyle}"
>
<Binding Path="Dob" Converter="{StaticResource dateFormattingConverter}">
<Binding.ValidationRules>
<app:DobValidationRule ValidationStep="ConvertedProposedValue"/>
</Binding.ValidationRules>
</Binding>
</TextBox>
Resource inheritance
If dictionaries being merged have a duplicate key, the last one wins (unlike the case of having duplicate keys in a single dictionary). Thus inheritance can be handled by the order in which the MergeDictionaries are loaded in the ResourceDictionary section… Start by loading the BASE, then the next down etc.. and it’ll override every time it meets a duplicate.
For reference – it’s on page 250ish in Martin's Unleashed book
Dynamic vs Static Resources :
DR means the resource is reapplied every time it changes.
The main difference between DR and SR is that any subsequent updates to the resource are reflected only to those elements that use DR.
SR and DR have different performance characteristics. DR requires more overhead than SR because of the extra overhead of tracking, while DR can potentially improve load time because SR are always loaded when the Window or Page loads, whereas the DR reference is only loaded when it’s actually used.
DR can also only set dependency properties whereas SR can be used almost anywhere
How are StaticResource and DynamicResource different
This was copied from http://wpfxaml.spaces.live.com/blog/cns!97DD5FD32788695B!142.entry for my own easy reference
Everyone one knows that StaticResource let’s one set a property of an element once.
If the Desktop Color is changed while the element’s application is running, the element keeps its original color:
<Button>
<Button.Background>
<SolidColorBrush Color="{StaticResource {x:Static SystemColors.DesktopColorKey}}" />
</Button.Background>
Hello
</Button>
On the other hand, if the element’s color is set using a DynamicResource, it changes when the Desktop Color changes:
<Button>
<Button.Background>
<SolidColorBrush Color="{DynamicResource {x:Static SystemColors.DesktopColorKey}}" />
</Button.Background>
Hello
</Button>
Why is that? The answer comes from the way these two Resource finders work:
1. StaticResource – Finds the resource given in by the ResourceDictionary key, and keeps the resource value;
2. DynamicResource – Finds the resource in the ResourceDictionary and keeps the key.
So, it is simple, really.
Since DynamicResource keeps the resource Key instead of the resource value, every event change fired from the resource lets the DynamicResource know that its value has changed.
This is why the DynamicResource reacts to the resource changes, while the StaticResource can’t know the resource has changed, since it only keeps the resource’s final value.
C# String Formatting
UPDATE: I show alternate ways to use string formats in WPF in this post:WPF String Formatting
This entry has been copied from SteveX (http://blog.stevex.net/index.php/string-formatting-in-csharp/) for my own easy reference.
The text inside the curly braces is {index[,alignment][:formatString]}. If alignment is positive, the text is right-aligned in a field the given number of spaces; if it’s negative, it’s left-aligned.
Strings
There really isn’t any formatting within a string, beyond it’s alignment. Alignment works for any argument being printed in a String.Format call.
Sample | Generates |
String.Format(”->{1,10}<-”, “Hello”); | -> Hello<- |
String.Format(”->{1,-10}<-”, “Hello”); | ->Hello <- |
Numbers
Basic number formatting specifiers:
Specifier | Type | Format | Output (Passed Double 1.42) | Output (Passed Int -12400) |
c | Currency | {0:c} | $1.42 | -$12,400 |
d | Decimal (Whole number) | {0:d} | System.FormatException | -12400 |
e | Scientific | {0:e} | 1.420000e+000 | -1.240000e+004 |
f | Fixed point | {0:f} | 1.42 | -12400.00 |
g | General | {0:g} | 1.42 | -12400 |
n | Number with commas for thousands | {0:n} | 1.42 | -12,400 |
r | Round trippable | {0:r} | 1.42 | System.FormatException |
x | Hexadecimal | {0:x4} | System.FormatException | cf90 |
Custom number formatting:
Specifier | Type | Example | Output (Passed Double 1500.42) | Note |
0 | Zero placeholder | {0:00.0000} | 1500.4200 | Pads with zeroes. |
# | Digit placeholder | {0:(#).##} | (1500).42 | |
. | Decimal point | {0:0.0} | 1500.4 | |
, | Thousand separator | {0:0,0} | 1,500 | Must be between two zeroes. |
,. | Number scaling | {0:0,.} | 2 | Comma adjacent to Period scales by 1000. |
% | Percent | {0:0%} | 150042% | Multiplies by 100, adds % sign. |
e | Exponent placeholder | {0:00e+0} | 15e+2 | Many exponent formats available. |
;
Group separator
see below
The group separator is especially useful for formatting currency values which require that negative values be enclosed in parentheses. This currency formatting example at the bottom of this document makes it obvious:
Dates
Note that date formatting is especially dependant on the system’s regional settings; the example strings here are from my local locale.
Specifier | Type | Example (Passed System.DateTime.Now) |
d | Short date | 10/12/2002 |
D | Long date | December 10, 2002 |
t | Short time | 10:11 PM |
T | Long time | 10:11:29 PM |
f | Full date & time | December 10, 2002 10:11 PM |
F | Full date & time (long) | December 10, 2002 10:11:29 PM |
g | Default date & time | 10/12/2002 10:11 PM |
G | Default date & time (long) | 10/12/2002 10:11:29 PM |
M | Month day pattern | December 10 |
r | RFC1123 date string | Tue, 10 Dec 2002 22:11:29 GMT |
s | Sortable date string | 2002-12-10T22:11:29 |
u | Universal sortable, local time | 2002-12-10 22:13:50Z |
U | Universal sortable, GMT | December 11, 2002 3:13:50 AM |
Y | Year month pattern | December, 2002 |
The ‘U’ specifier seems broken; that string certainly isn’t sortable.
Custom date formatting:
Specifier | Type | Example | Example Output |
dd | Day | {0:dd} | 10 |
ddd | Day name | {0:ddd} | Tue |
dddd | Full day name | {0:dddd} | Tuesday |
f, ff, … | Second fractions | {0:fff} | 932 |
gg, … | Era | {0:gg} | A.D. |
hh | 2 digit hour | {0:hh} | 10 |
HH | 2 digit hour, 24hr format | {0:HH} | 22 |
mm | Minute 00-59 | {0:mm} | 38 |
MM | Month 01-12 | {0:MM} | 12 |
MMM | Month abbreviation | {0:MMM} | Dec |
MMMM | Full month name | {0:MMMM} | December |
ss | Seconds 00-59 | {0:ss} | 46 |
tt | AM or PM | {0:tt} | PM |
yy | Year, 2 digits | {0:yy} | 02 |
yyyy | Year | {0:yyyy} | 2002 |
zz | Timezone offset, 2 digits | {0:zz} | -05 |
zzz | Full timezone offset | {0:zzz} | -05:00 |
: | Separator | {0:hh:mm:ss} | 10:43:20 |
/ | Separator | {0:dd/MM/yyyy} | 10/12/2002 |
Enumerations
Specifier | Type |
g | Default (Flag names if available, otherwise decimal) |
f | Flags always |
d | Integer always |
x | Eight digit hex. |
Some Useful Examples
String.Format(”{0:$#,##0.00;($#,##0.00);Zero}”, value);
This will output “$1,240.00? if passed 1243.50. It will output the same format but in parentheses if the number is negative, and will output the string “Zero” if the number is zero.
String.Format(”{0:(###) ###-####}”, 8005551212);
This will output “(800) 555-1212?.
WPF ComboBox Data Binding
This property provides the ItemsSource for the combobox by building a dictionary of enum (key) and enum.ToString (value).
public enum PaymentFrequency
{
DayOfPlay,
Monthly,
None,
Quarterly,
Weekly,
}
Expose this as a Property and Dictionary for Binding purposes: public PaymentFrequency Frequency { get; set; }
public IDictionary<PaymentFrequency, string> FrequencyList
{
get
{
IDictionary<PaymentFrequency, string> frequencyList = new Dictionary<PaymentFrequency, string>();
foreach (PaymentFrequency pf in Enum.GetValues(typeof(PaymentFrequency)))
{
frequencyList.Add(pf, pf.ToString());
}
return frequencyList;
}
}
The corresponding xaml (not forgetting the UpdateSourceTrigger=PropertyChanged if the ComboBox is inside a DataGrid as discussed here): <ComboBox ItemsSource="{Binding Path=FrequencyList, Mode=OneTime}"
SelectedValuePath="Key"
DisplayMemberPath="Value"
SelectedValue="{Binding Path=Frequency, UpdateSourceTrigger=PropertyChanged}">
</ComboBox>
For Internationalisation purposes it would be better to use Resource Strings to provide the descriptions for the combobox. I used a prefix of "PF_" on all the strings to ensure they are unique:
PF_DayOfPlay Day of Play
PF_Monthly Monthly
PF_None None
PF_Quarterly Quarterly
PF_Weekly Weekly
Then modify the FrequencyList getter: public PaymentFrequency Frequency { get; set; }
public IDictionary<PaymentFrequency, string> FrequencyList
{
get
{
IDictionary<PaymentFrequency, string> frequencyList = new Dictionary<PaymentFrequency, string>();
foreach (PaymentFrequency pf in Enum.GetValues(typeof(PaymentFrequency)))
{
frequencyList.Add(pf, ResourceStrings.ResourceManager.GetString("PF_" + pf.ToString()));
}
return frequencyList;
}
}
A final tweak is to sort the descriptions displayed in the combobox into alphabetic order based on the translated strings. public PaymentFrequency Frequency { get; set; }
public IDictionary<PaymentFrequency, string> FrequencyList
{
get
{
IDictionary<PaymentFrequency, string> frequencyList = new Dictionary<PaymentFrequency, string>();
foreach (PaymentFrequency pf in Enum.GetValues(typeof(PaymentFrequency)))
{
frequencyList.Add(pf, ResourceStrings.ResourceManager.GetString("PF_" + pf.ToString()));
}
return frequencyList.OrderBy(f => f.Value).ToDictionary(f => f.Key, f => f.Value);
}
}
The ComboboxDataBinding example project includes this code and can be downloaded from GoogleCode.
CompareTo method
int result = instance.CompareTo(obj);
Result | Meaning |
Less than zero | instance is less than obj |
Zero | instance is equal to obj |
Greater than zero | instance is greater than obj |
For this | Use this |
if (instance < obj) | if (instance.CompareTo(obj) < 0) |
if (instance <= obj) | if (instance.CompareTo(obj) <= 0) |
if (instance == obj) | if (instance.CompareTo(obj) == 0) |
if (instance > obj) | if (instance.CompareTo(obj) > 0) |
if (instance >= obj) | if (instance.CompareTo(obj) >= 0) |
Sorting Generic Collections
(Note: For a simpler technique using LINQ see: Sorting Generic Collections - Redux)
List persons = new List();
persons.Add( new Person("Tom",30) );
persons.Add(new Person("Harry", 55));
// sort in ascending order
persons.Sort( delegate(Person person0, Person person1)
{
return
person0.FirstName.CompareTo(person1.FirstName);
} );
// sort in descending order
persons.Sort( delegate(Person person0, Person person1)
{
return
person1.FirstName.CompareTo(person0.FirstName);
} );