WPF

WPF : New Threading Component

As many of you have know I write quite a lot about UI work and WPF technology, and I have in the past written articles about Threading and have also written a great lot about my own WPF MVVM Framework Cinch which includes a lot of stuff to get you up and running doing WPF the MVVM way.

Well one thing that has always bugged me while working with WPF is threading and doing something in the background and keeping my UI nice and free to do other stuff. Sure I can spawn a new Thread or use the ThreadPool and even use the funky BackgroundTaskManager class inside of Cinch which does help a lot in terms of fetching data on an internally held BackgroundWorker and is completely unit testable, as discussed in this Cinch article. I just felt that more improvements could be made.

For example what I would want to be able to do, is have a ViewModel (to allow Binding and testing) that was being used as a DataContext for some View, to be able to accept some parametised work delegate that would be performed on a background thread, and would show some busy animation or status while the threading operation was happening, and would then either show an error status in the View somewhere if the background threading operation failed, or would show me the actual data that the background thread fetched that I requested in the parametised work delegate, if there was NO failure while fetching the relevant data.

I should point out that the code contained here in, does spawn a new BackgroundWorker per threading operation, where each operation is expected to return some single definable bit of data, such as a single List<SomeClass> or even some other very expensive time consuming data to fetch. My intent was NEVER to have some global threading manager that fetches all and sundry in 1 hit, its more micro managing the data.

In lamens terms, think of it like this (where "I" and "Me" in this italic paragraph means the demo app code provided in this article ) :

You want me to show a List<Contact> that could take some time to fetch, fair enough, I’ll create a BackgroundWorker to do that and manage that background activity, and let you know how its going, and when I am done I’ll return you a List<Contact>, or some error message. Oh and you also want me to show a List<BankAccount> on the same View that will also take some time to fetch, well now, for that I will need to return another type of List. In fact it will be a list of List<BankAccount> so I am going to be needing another BackgroundWorker to do that, and return you your list of List<BankAccount>.

I know this could potentially spawn a few threads, but internally the BackgroundWorker makes use of the ThreadPool which is ace, so I do not feel it’s an issue, as the management of Threads is done for us by the .NET Framework. Obviously if you have 100nds of lists of data on one View this article may not be for you at all, and you should stop reading right here. If on the other hand you have a few lists of data or a few expensive bits of data to fetch this code could well be for you.

Anyway that is how I see it working, and after messing around for a while I think I have done just that.

adorners

There are a few assumptions that I have made, which are as follows:

  • That people are using WPF, and are pretty competent with it. This is NOT a beginner article at all
  • That people are using the MVVM pattern
  • That people think its a good idea to have parts of your View show error messages if the data that was supposed to be fetched failed to happen
  • That people are happy with the idea of a item of data (such as a List of data, or expensive bits of data, being fetched by a dedicated BackgroundWorker manager object, which we will get to later).

If you want to know more about how all this works you can read the full article and grab the source code from there:

http://www.codeproject.com/KB/WPF/ThreadingComponent.aspx

Advertisements
WPF

Better WPF Circular Progress Bar

A while back I posted a blog post about a simple Circular Progress Bar that I did for WPF. The original post is right here : http://sachabarber.net/?p=429

It turns out that was not the best thing to do, as the old approach used a never ending animation, that was even running when the controls Visibility changed. I did notice this pretty quickly, when we profiled our app, and noticed this hot spot exactly where the progress bar was. So what we did to fix that is just remove the control when it should stop showing progress. Anyway that was the old way.

I am pleased to announce that I have a new improved Circular Progress Bar that no longer uses a never ending animation, in fact it is a lot simpler and just uses a DispatcherTimer and some elementary trigonometry, and it actually looks more like the style of progress bar we are all used to seeing on the web. Without further ado here is the code:

The xaml for the CircularProgressBar.xaml

<UserControl x:Class="ThreadingComponent.CircularProgressBar"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="Auto" Width="Auto" Background="Transparent" 
             IsVisibleChanged="HandleVisibleChanged">
    <Grid x:Name="LayoutRoot" Background="Transparent" 
          ToolTip="Searching...."
          HorizontalAlignment="Center" 
          VerticalAlignment="Center">
        <Canvas RenderTransformOrigin="0.5,0.5" 
                HorizontalAlignment="Center" 
             VerticalAlignment="Center" Width="120" 
             Height="120" Loaded="HandleLoaded" 
                Unloaded="HandleUnloaded"  >
            <Ellipse x:Name="C0" Width="20" Height="20" 
                     Canvas.Left="0" 
                     Canvas.Top="0" Stretch="Fill" 
                     Fill="Black" Opacity="1.0"/>
            <Ellipse x:Name="C1" Width="20" Height="20" 
                     Canvas.Left="0"
                     Canvas.Top="0" Stretch="Fill" 
                     Fill="Black" Opacity="0.9"/>
            <Ellipse x:Name="C2" Width="20" Height="20" 
                     Canvas.Left="0" 
                     Canvas.Top="0" Stretch="Fill" 
                     Fill="Black" Opacity="0.8"/>
            <Ellipse x:Name="C3" Width="20" Height="20" 
                     Canvas.Left="0" 
                     Canvas.Top="0" Stretch="Fill" 
                     Fill="Black" Opacity="0.7"/>
            <Ellipse x:Name="C4" Width="20" Height="20" 
                     Canvas.Left="0" 
                     Canvas.Top="0" Stretch="Fill" 
                     Fill="Black" Opacity="0.6"/>
            <Ellipse x:Name="C5" Width="20" Height="20" 
                     Canvas.Left="0" 
                     Canvas.Top="0" Stretch="Fill" 
                     Fill="Black" Opacity="0.5"/>
            <Ellipse x:Name="C6" Width="20" Height="20" 
                     Canvas.Left="0" 
                     Canvas.Top="0" Stretch="Fill" 
                     Fill="Black" Opacity="0.4"/>
            <Ellipse x:Name="C7" Width="20" Height="20" 
                     Canvas.Left="0" 
                     Canvas.Top="0" Stretch="Fill" 
                     Fill="Black" Opacity="0.3"/>
            <Ellipse x:Name="C8" Width="20" Height="20" 
                     Canvas.Left="0" 
                     Canvas.Top="0" Stretch="Fill" 
                     Fill="Black" Opacity="0.2"/>
            <Canvas.RenderTransform>
                <RotateTransform x:Name="SpinnerRotate" 
                     Angle="0" />
            </Canvas.RenderTransform>
        </Canvas>
    </Grid>
</UserControl>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

And here is the CircularProgressBar.xaml.cs

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using System.Windows.Input;
using System.Windows.Shapes;

namespace ThreadingComponent
{
    /// <summary>
    /// A circular type progress bar, that is simliar to popular web based
    /// progress bars
    /// </summary>
    public partial class CircularProgressBar
    {
        #region Data
        private readonly DispatcherTimer animationTimer;
        #endregion

        #region Constructor
        public CircularProgressBar()
        {
            InitializeComponent();

            animationTimer = new DispatcherTimer(
                DispatcherPriority.ContextIdle, Dispatcher);
            animationTimer.Interval = new TimeSpan(0, 0, 0, 0, 75);
        }
        #endregion

        #region Private Methods
        private void Start()
        {
            Mouse.OverrideCursor = Cursors.Wait;
            animationTimer.Tick += HandleAnimationTick;
            animationTimer.Start();
        }

        private void Stop()
        {
            animationTimer.Stop();
            Mouse.OverrideCursor = Cursors.Arrow;
            animationTimer.Tick -= HandleAnimationTick;
        }

        private void HandleAnimationTick(object sender, EventArgs e)
        {
            SpinnerRotate.Angle = (SpinnerRotate.Angle + 36) % 360;
        }

        private void HandleLoaded(object sender, RoutedEventArgs e)
        {
            const double offset = Math.PI;
            const double step = Math.PI * 2 / 10.0;

            SetPosition(C0, offset, 0.0, step);
            SetPosition(C1, offset, 1.0, step);
            SetPosition(C2, offset, 2.0, step);
            SetPosition(C3, offset, 3.0, step);
            SetPosition(C4, offset, 4.0, step);
            SetPosition(C5, offset, 5.0, step);
            SetPosition(C6, offset, 6.0, step);
            SetPosition(C7, offset, 7.0, step);
            SetPosition(C8, offset, 8.0, step);
        }


        private void SetPosition(Ellipse ellipse, double offset, 
            double posOffSet, double step)
        {
            ellipse.SetValue(Canvas.LeftProperty, 50.0 
                + Math.Sin(offset + posOffSet * step) * 50.0);

            ellipse.SetValue(Canvas.TopProperty, 50 
                + Math.Cos(offset + posOffSet * step) * 50.0);
        }


        private void HandleUnloaded(object sender, RoutedEventArgs e)
        {
            Stop();
        }

        private void HandleVisibleChanged(object sender, 
            DependencyPropertyChangedEventArgs e)
        {
            bool isVisible = (bool)e.NewValue;

            if (isVisible)
                Start();
            else
                Stop();
        }
        #endregion
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

And to use it you can simply make it any size you like by putting it into a ViewBox like so:

<UserControl x:Class="ThreadingComponent.BusyUserControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ThreadingComponent"
    Height="Auto" Width="Auto" 
    HorizontalAlignment="Stretch" 
    VerticalAlignment="Stretch">

        
        <Viewbox Width="200" Height="200"
                HorizontalAlignment="Center" 
                VerticalAlignment="Center">
            <local:CircularProgressBar />
        </Viewbox>


    </Grid>

</UserControl>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

And here is what it looks like when its running

All the code is here is a cut and pastable format, so no ZIP file this time, just cut and paste this code, if you don’t know how to do that, step away from the XAML.

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

WPF

WPF : Simple 3D Charting

I have just published a new WPF article that demonstrates how to make a fairly simple 3D Chart using WPF and some of its 3D capabilities.

Its a 3D bar chart that allows uses to navigate through historical data. It has somewhat limited use, as it so closely matches my wifes business, business requirements, but could easily be adapted for someone else uses, and I do feel it is still a nice example of working with 3D and WPF.

This is what it looks like when it starts up:

chart1

And here is what is looks like when the user hover their mouse over a particular item within the chart.

chart2

It did take a little while to get right. And despite it somewhat limited use (as its tailored directly to suit my wifes requirements) there is still some rather cool 3D stuff you can learn by reading the articles text.

So if you appreciate the work I do, and the code that I publish, I sure would appreciate you taking the time to vote for this article.

The full articles description of how it all works, and the source code is available at :

http://www.codeproject.com/KB/WPF/Graph3D.aspx

Enjoy

MVVM, WPF

Cinch : More good news (again sorry)

I have just done another update to my Cinch MVVM Framework which I realise is probably a pain to those of you that are keeping up with the codebase.

I actually feel I am done with it for good now (well until the next time).

So what has changed this time. Well there are only 2 things:

1. Fixed NumericTextBoxBehavior so it now validates pasted contents to ensure its numeric

2. I replaced my own logging service with a more manly logging facade. Now I am a massive fan of Daniel Vaughans work, and he did an excellent job on his logging facade called Clog, But what I wanted for my Cinch MVVM Framework was something that would require hardly any changes. I was originally going to use Clog, but after a bit of a look, I decided to use 2 other WPF Disciples logging facade work called Simple Logging Facade (SLF).

Now this is not to say what Daniel did is bad, cos it sure aint, in fact SLF is really a subset of what Clog can do. Its just for Cinch MVVM Framework SLF seemed an easier (yes I am that lazy) fit.

So Cinch MVVM Framework now uses Simple Logging Facade (SLF), and makes use of the Log4Net facade, so the log entries use the Log4Net style, which can be configured through the App.Config.

Here is an example of the new App.Config required by my Cinch MVVM Framework

 

   1:  <?xml version="1.0" encoding="utf-8" ?>
   2:  <configuration>
   3:    <configSections>
   4:      <section name="log4net" 
   5:               type="log4net.Config.Log4NetConfigurationSectionHandler,log4net"/>
   6:      <section name="slf" 
   7:               type="Slf.Config.SlfConfigurationSection, slf"/>
   8:    </configSections>
   9:   
  10:    <slf>
  11:      <factories>
  12:        <!-- configure single log4net factory, 
  13:             which will get all logging output -->
  14:        <!-- Important: Set a reference to the log4net facade 
  15:             library to make sure it will be available at runtime -->
  16:        <factory type="SLF.Log4netFacade.Log4netLoggerFactory, 
  17:                 SLF.Log4netFacade"/>
  18:      </factories>
  19:    </slf>
  20:   
  21:   
  22:    <!-- configures log4net to write into a local file called "log.txt" -->
  23:    <log4net>
  24:      <!--  log4net uses the concept of 'appenders' to indicate where 
  25:            log messages are written to.
  26:            Appenders can be files, the console, databases, SMTP and much more
  27:      -->
  28:      <appender name="MainAppender" type="log4net.Appender.FileAppender">
  29:        <param name="File" value="log.txt" />
  30:        <param name="AppendToFile" value="true" />
  31:        <!--  log4net can optionally format the logged messages with a pattern. 
  32:              This pattern string details what information
  33:              is logged and the format it takes. 
  34:              A wide range of information can be logged, including message, 
  35:              thread, identity and more,
  36:              see the log4net documentation for details:
  37:              http://logging.apache.org/log4net/release/sdk/log4net.Layout.PatternLayout.html
  38:        -->
  39:        <layout type="log4net.Layout.PatternLayout">
  40:          <conversionPattern value="%logger: %date [%thread] 
  41:                             %-5level - %message %newline" />
  42:        </layout>
  43:      </appender>
  44:      <root>
  45:        <level value="ALL" />
  46:        <appender-ref ref="MainAppender" />
  47:      </root>
  48:    </log4net>
  49:   
  50:  </configuration>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

These new features will be documented in the Cinch MVVM Framework documentation over at www.codeproject.com

 

Enjoy.

 

PS : I really am done with Cinch now I feel, those were the last changes I wanted to get in there.

C#, WPF

Friction Scrolling Now An WPF Attached Behaviour Too

A while ago I wrote about how to create a scrollable design surface in WPF, and how you could also add friction into the mix.

My original post was called “Creating A Scrollable Control Surface In WPF” which can be found at  the following url:

http://sachabarber.net/?p=225

This original blog post proved to be quite popular and one of my fellow WPF Disciples my homeboy Jeremiah Morrill took it upon himself to rewrite my little control to be a content control for Silverlight, which you can get to at  “Scrollable Friction Canvas For Silverlight” which can be found at  the following url:

http://sachabarber.net/?p=481

I have been asked for my original code a lot, and another of my friends, and founder of the WPF Disciples, Marlon Grech took my code and has further improved it for WPF users, by making it an attached behaviour so all you have to do is hook up one property on your ScrollViewer and bingo its a Friction enabled surface. Neato I say.

Here is Marlons attached behaviour code:

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:  using System.Windows;
   6:  using System.Windows.Controls;
   7:  using System.Windows.Input;
   8:  using System.Windows.Threading;
   9:   
  10:  namespace ScrollableArea
  11:  {
  12:      public class KineticBehaviour
  13:      {
  14:          #region Friction
  15:   
  16:          /// <summary>
  17:          /// Friction Attached Dependency Property
  18:          /// </summary>
  19:          public static readonly DependencyProperty FrictionProperty =
  20:              DependencyProperty.RegisterAttached("Friction", typeof(double), typeof(KineticBehaviour),
  21:                  new FrameworkPropertyMetadata((double)0.95));
  22:   
  23:          /// <summary>
  24:          /// Gets the Friction property.  This dependency property 
  25:          /// indicates ....
  26:          /// </summary>
  27:          public static double GetFriction(DependencyObject d)
  28:          {
  29:              return (double)d.GetValue(FrictionProperty);
  30:          }
  31:   
  32:          /// <summary>
  33:          /// Sets the Friction property.  This dependency property 
  34:          /// indicates ....
  35:          /// </summary>
  36:          public static void SetFriction(DependencyObject d, double value)
  37:          {
  38:              d.SetValue(FrictionProperty, value);
  39:          }
  40:   
  41:          #endregion
  42:   
  43:          #region ScrollStartPoint
  44:   
  45:          /// <summary>
  46:          /// ScrollStartPoint Attached Dependency Property
  47:          /// </summary>
  48:          private static readonly DependencyProperty ScrollStartPointProperty =
  49:              DependencyProperty.RegisterAttached("ScrollStartPoint", typeof(Point), typeof(KineticBehaviour),
  50:                  new FrameworkPropertyMetadata((Point)new Point()));
  51:   
  52:          /// <summary>
  53:          /// Gets the ScrollStartPoint property.  This dependency property 
  54:          /// indicates ....
  55:          /// </summary>
  56:          private static Point GetScrollStartPoint(DependencyObject d)
  57:          {
  58:              return (Point)d.GetValue(ScrollStartPointProperty);
  59:          }
  60:   
  61:          /// <summary>
  62:          /// Sets the ScrollStartPoint property.  This dependency property 
  63:          /// indicates ....
  64:          /// </summary>
  65:          private static void SetScrollStartPoint(DependencyObject d, Point value)
  66:          {
  67:              d.SetValue(ScrollStartPointProperty, value);
  68:          }
  69:   
  70:          #endregion
  71:   
  72:          #region ScrollStartOffset
  73:   
  74:          /// <summary>
  75:          /// ScrollStartOffset Attached Dependency Property
  76:          /// </summary>
  77:          private static readonly DependencyProperty ScrollStartOffsetProperty =
  78:              DependencyProperty.RegisterAttached("ScrollStartOffset", typeof(Point), typeof(KineticBehaviour),
  79:                  new FrameworkPropertyMetadata((Point)new Point()));
  80:   
  81:          /// <summary>
  82:          /// Gets the ScrollStartOffset property.  This dependency property 
  83:          /// indicates ....
  84:          /// </summary>
  85:          private static Point GetScrollStartOffset(DependencyObject d)
  86:          {
  87:              return (Point)d.GetValue(ScrollStartOffsetProperty);
  88:          }
  89:   
  90:          /// <summary>
  91:          /// Sets the ScrollStartOffset property.  This dependency property 
  92:          /// indicates ....
  93:          /// </summary>
  94:          private static void SetScrollStartOffset(DependencyObject d, Point value)
  95:          {
  96:              d.SetValue(ScrollStartOffsetProperty, value);
  97:          }
  98:   
  99:          #endregion
 100:   
 101:          #region InertiaProcessor
 102:   
 103:          /// <summary>
 104:          /// InertiaProcessor Attached Dependency Property
 105:          /// </summary>
 106:          private static readonly DependencyProperty InertiaProcessorProperty =
 107:              DependencyProperty.RegisterAttached("InertiaProcessor", typeof(InertiaHandler), typeof(KineticBehaviour),
 108:                  new FrameworkPropertyMetadata((InertiaHandler)null));
 109:   
 110:          /// <summary>
 111:          /// Gets the InertiaProcessor property.  This dependency property 
 112:          /// indicates ....
 113:          /// </summary>
 114:          private static InertiaHandler GetInertiaProcessor(DependencyObject d)
 115:          {
 116:              return (InertiaHandler)d.GetValue(InertiaProcessorProperty);
 117:          }
 118:   
 119:          /// <summary>
 120:          /// Sets the InertiaProcessor property.  This dependency property 
 121:          /// indicates ....
 122:          /// </summary>
 123:          private static void SetInertiaProcessor(DependencyObject d, InertiaHandler value)
 124:          {
 125:              d.SetValue(InertiaProcessorProperty, value);
 126:          }
 127:   
 128:          #endregion
 129:   
 130:          #region HandleKineticScrolling
 131:   
 132:          /// <summary>
 133:          /// HandleKineticScrolling Attached Dependency Property
 134:          /// </summary>
 135:          public static readonly DependencyProperty HandleKineticScrollingProperty =
 136:              DependencyProperty.RegisterAttached("HandleKineticScrolling", typeof(bool), 
 137:              typeof(KineticBehaviour),
 138:                  new FrameworkPropertyMetadata((bool)false,
 139:                      new PropertyChangedCallback(OnHandleKineticScrollingChanged)));
 140:   
 141:          /// <summary>
 142:          /// Gets the HandleKineticScrolling property.  This dependency property 
 143:          /// indicates ....
 144:          /// </summary>
 145:          public static bool GetHandleKineticScrolling(DependencyObject d)
 146:          {
 147:              return (bool)d.GetValue(HandleKineticScrollingProperty);
 148:          }
 149:   
 150:          /// <summary>
 151:          /// Sets the HandleKineticScrolling property.  This dependency property 
 152:          /// indicates ....
 153:          /// </summary>
 154:          public static void SetHandleKineticScrolling(DependencyObject d, bool value)
 155:          {
 156:              d.SetValue(HandleKineticScrollingProperty, value);
 157:          }
 158:   
 159:          /// <summary>
 160:          /// Handles changes to the HandleKineticScrolling property.
 161:          /// </summary>
 162:          private static void OnHandleKineticScrollingChanged(DependencyObject d, 
 163:              DependencyPropertyChangedEventArgs e)
 164:          {
 165:              ScrollViewer scoller = d as ScrollViewer;
 166:              if ((bool)e.NewValue)
 167:              {
 168:                  scoller.MouseDown += OnMouseDown;
 169:                  scoller.MouseMove += OnMouseMove;
 170:                  scoller.MouseUp += OnMouseUp;
 171:                  SetInertiaProcessor(scoller, new InertiaHandler(scoller));
 172:              }
 173:              else
 174:              {
 175:                  scoller.MouseDown -= OnMouseDown;
 176:                  scoller.MouseMove -= OnMouseMove;
 177:                  scoller.MouseUp -= OnMouseUp;
 178:                  var inertia = GetInertiaProcessor(scoller);
 179:                  if (inertia != null)
 180:                      inertia.Dispose();
 181:              }
 182:              
 183:          }
 184:   
 185:          #endregion
 186:   
 187:          #region Mouse Events
 188:          private static void OnMouseDown(object sender, MouseButtonEventArgs e)
 189:          {
 190:              var scrollViewer = (ScrollViewer)sender;
 191:              if (scrollViewer.IsMouseOver)
 192:              {
 193:                  // Save starting point, used later when determining how much to scroll.
 194:                  SetScrollStartPoint(scrollViewer, e.GetPosition(scrollViewer));
 195:                  SetScrollStartOffset(scrollViewer, new 
 196:                      Point(scrollViewer.HorizontalOffset, scrollViewer.VerticalOffset));
 197:                  scrollViewer.CaptureMouse();
 198:              }
 199:          }
 200:   
 201:   
 202:          private static void OnMouseMove(object sender, MouseEventArgs e)
 203:          {
 204:              var scrollViewer = (ScrollViewer)sender;
 205:              if (scrollViewer.IsMouseCaptured)
 206:              {
 207:                  Point currentPoint = e.GetPosition(scrollViewer);
 208:   
 209:                  var scrollStartPoint = GetScrollStartPoint(scrollViewer);
 210:                  // Determine the new amount to scroll.
 211:                  Point delta = new Point(scrollStartPoint.X - currentPoint.X, 
 212:                      scrollStartPoint.Y - currentPoint.Y);
 213:   
 214:                  var scrollStartOffset = GetScrollStartOffset(scrollViewer);
 215:                  Point scrollTarget = new Point(scrollStartOffset.X + delta.X, 
 216:                      scrollStartOffset.Y + delta.Y);
 217:   
 218:                  var inertiaProcessor = GetInertiaProcessor(scrollViewer);
 219:                  if (inertiaProcessor != null)
 220:                      inertiaProcessor.ScrollTarget = scrollTarget;
 221:                  
 222:                  // Scroll to the new position.
 223:                  scrollViewer.ScrollToHorizontalOffset(scrollTarget.X);
 224:                  scrollViewer.ScrollToVerticalOffset(scrollTarget.Y);
 225:              }
 226:          }
 227:   
 228:          private static void OnMouseUp(object sender, MouseButtonEventArgs e)
 229:          {
 230:              var scrollViewer = (ScrollViewer)sender;
 231:              if (scrollViewer.IsMouseCaptured)
 232:              {
 233:                  scrollViewer.ReleaseMouseCapture();
 234:              }
 235:          }
 236:          #endregion
 237:   
 238:          #region Inertia Stuff
 239:   
 240:          /// <summary>
 241:          /// Handles the inertia 
 242:          /// </summary>
 243:          class InertiaHandler : IDisposable
 244:          {
 245:              private Point previousPoint;
 246:              private Vector velocity;
 247:              ScrollViewer scroller;
 248:              DispatcherTimer animationTimer;
 249:   
 250:              private Point scrollTarget;
 251:              public Point ScrollTarget { 
 252:                  get { return scrollTarget; } 
 253:                  set { scrollTarget = value; } }
 254:   
 255:              public InertiaHandler(ScrollViewer scroller)
 256:              {
 257:                  this.scroller = scroller;
 258:                  animationTimer = new DispatcherTimer();
 259:                  animationTimer.Interval = new TimeSpan(0, 0, 0, 0, 20);
 260:                  animationTimer.Tick += new EventHandler(HandleWorldTimerTick);
 261:                  animationTimer.Start();
 262:              }
 263:   
 264:              private void HandleWorldTimerTick(object sender, EventArgs e)
 265:              {
 266:                  if (scroller.IsMouseCaptured)
 267:                  {
 268:                      Point currentPoint = Mouse.GetPosition(scroller);
 269:                      velocity = previousPoint - currentPoint;
 270:                      previousPoint = currentPoint;
 271:                  }
 272:                  else
 273:                  {
 274:                      if (velocity.Length > 1)
 275:                      {
 276:                          scroller.ScrollToHorizontalOffset(ScrollTarget.X);
 277:                          scroller.ScrollToVerticalOffset(ScrollTarget.Y);
 278:                          scrollTarget.X += velocity.X;
 279:                          scrollTarget.Y += velocity.Y;
 280:                          velocity *= KineticBehaviour.GetFriction(scroller);
 281:                      }
 282:                  }
 283:              }
 284:   
 285:              #region IDisposable Members
 286:   
 287:              public void Dispose()
 288:              {
 289:                  animationTimer.Stop();
 290:              }
 291:   
 292:              #endregion
 293:          }
 294:   
 295:          #endregion
 296:      }
 297:  }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Which to use you would simply do this :

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

   1:  <Window x:Class="ScrollableArea.Window1"
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      xmlns:local="clr-namespace:ScrollableArea"
   5:      Title="Window1" Height="300" Width="300">
   6:      <Window.Resources>
   7:   
   8:          <!-- scroll viewer -->
   9:          <Style x:Key="ScrollViewerStyle"
  10:                 TargetType="{x:Type ScrollViewer}">
  11:              <Setter Property="HorizontalScrollBarVisibility" 
  12:                      Value="Hidden" />
  13:              <Setter Property="VerticalScrollBarVisibility" 
  14:                      Value="Hidden" />
  15:          </Style>
  16:   
  17:      </Window.Resources>
  18:   
  19:      <Grid Margin="0">
  20:          <ScrollViewer x:Name="ScrollViewer" 
  21:              Style="{StaticResource ScrollViewerStyle}" 
  22:              local:KineticBehaviour.HandleKineticScrolling="True">
  23:              <ItemsControl x:Name="itemsControl" 
  24:                            VerticalAlignment="Center">
  25:   
  26:                  <ItemsControl.ItemsPanel>
  27:                      <ItemsPanelTemplate>
  28:                          <!-- Custom Panel-->
  29:                          <StackPanel Orientation="Vertical"/>
  30:                      </ItemsPanelTemplate>
  31:                  </ItemsControl.ItemsPanel>
  32:   
  33:   
  34:              </ItemsControl>
  35:          </ScrollViewer>
  36:      </Grid>
  37:   
  38:  </Window>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

As always here is a small demo app:

http://dl.dropbox.com/u/2600965/ScrollableAreaAttachedBehaviour.zip

MVVM, WPF

Cinch : Major change alert

I have been quite busy with a few other ideas and research lately, but there has always been one thing that has bothered me about my Cinch MVVM Framework which was how the business rules were declared on the business objects.

Previously the rules were instance based, and due to the added complications of me using a specialized Decorater pattern for some of the core business object properties (which is cool as it allow the VM to dictate the editability of the data) I have been struggling to see how to fix this for a while now.

The other day I had a chat with my mate at work a extremely smart chap by the name of Fredrik Bornander who I admire a lot (he is way smarter than I will ever be), and he helped me come up with something cool.

So now what we have is the best of both worlds we declare the rule instances statically per Type, but we add the static rule declarations to instance based fields to validate against.

Here is what we do now:

using System;

using Cinch;
using MVVM.DataAccess;
using System.ComponentModel;
using System.Collections.Generic;

namespace MVVM.Models
{


    public class OrderModel : Cinch.EditableValidatingObject
    {
        #region Data
        private Cinch.DataWrapper<Int32> quantity;

        //rules
        private static SimpleRule quantityRule;

        #endregion

        #region Ctor
        public OrderModel()
        {
            #region Create DataWrappers

            Quantity = new DataWrapper<Int32>(this, quantityChangeArgs);


            #endregion

            #region Create Validation Rules

            quantity.AddRule(quantityRule);

            #endregion

        }

        static OrderModel()
        {
            quantityRule = new SimpleRule("DataValue", 
                 "Quantity can not be < 0",
                      (Object domainObject)=>
                      {
                          DataWrapper<Int32> obj = 
                           (DataWrapper<Int32>)domainObject;
                          return obj.DataValue <= 0;
                      });
        }


        #endregion

        #region Public Properties


        /// <summary>
        /// Quantity
        /// </summary>
        static PropertyChangedEventArgs quantityChangeArgs =
            ObservableHelper.CreateArgs<OrderModel>(x => x.Quantity);

        public Cinch.DataWrapper<Int32> Quantity
        {
            get { return quantity; }
            private set
            {
                quantity = value;
                NotifyPropertyChanged(quantityChangeArgs);
            }
        }



        #endregion

       
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

I am most pleased with this now, as it will sure decrease the amount of memory required by each business object, but it still allows the instance fields to be validated. Nice.

I will be updating the codebase and relevant articles that cover Cinch MVVM Framework some time today.

Thanks Fredrik, you crazy Swede. You rock