Wednesday, March 05, 2008

Binding to current item, external collection management, DataTemplates and DataTriggers

Today, we'll learn how to manage your collection, binded to ItemsControl externally, how to bind to the current item of the collection and how to use data templates with data triggers to make your UI data driven. So let's start.

image

First of all, we should create the items for the collection and the collection itself. IT's pretty straight-forward by now.

public class RedBlackItem : DependencyObject
    {
        public RedBlackItem(bool isRed)
        {
            this.IsRed = isRed;
        }

        public bool IsRed
        {
            get { return (bool)GetValue(IsRedProperty); }
            set { SetValue(IsRedProperty, value); }
        }
        public static readonly DependencyProperty IsRedProperty =
            DependencyProperty.Register("IsRed", typeof(bool), typeof(RedBlackItem), new UIPropertyMetadata(default(bool)));

    }

public class RedBlackCollection : ObservableCollection<RedBlackItem>
    {
        public RedBlackCollection()
        {
            Random r = new Random();
            for (int i = 0; i < 10; i++)
            {
                base.Add(new RedBlackItem(r.NextDouble() < 0.5));
            }
        }
    }

Next, let's create the instance of our collection in XAML and connect it to the view

<Window.Resources>
    <l:RedBlackCollection x:Key="collection"/>
    <CollectionViewSource Source="{StaticResource collection}" x:Key="data"/>

Actually, if you'll have at least one ItemsControl in your project, you not have to create CollectionView, due to fact, that Items is, actually will be the view. But we want it absolutely UI independent and data-driven, so we'll use CollectionViewSource to manage it.

Now, let's bind the collection view to our Item control

<ListBox ItemsSource="{Binding Source={StaticResource data}}"/>

Running the program right now, will display strings, reflecting the item type. Now we want to know what item was selected and display it in other place. But how to do it? We, actually, have no SelectedItem dependency property. Let try to make WPF to do all the work and bind the same collection to the control, that knows to display only one item. The best candidate for it is ContentControl

<ContentControl Content="{Binding Source={StaticResource data}}"/>

Compile and run. What's the magic. The control displays one item in our case it will be string, 'cos we have no template. Let's do it.

<DataTemplate DataType="{x:Type l:RedBlackItem}" x:Key="OtherTemplate">
    <Ellipse Width="100" Height="100">
            <Ellipse.Style>
                <Style>
                    <Setter Property="Ellipse.Fill" Value="Black"/>
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding Path=IsRed}" Value="True">
                            <Setter Property="Ellipse.Fill" Value="Red"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Ellipse.Style>
        </Ellipse>
</DataTemplate>

Fair enough. Simple data template, that uses ellipse with color fill, according the property of the item. Now, all we have to do is connect the template and the control. ContentTemplate property of ContentControl looks very good candidate for it.

<ContentControl ContentTemplate="{StaticResource OtherTemplate}" Content="{Binding Source={StaticResource data}}"/>

Now, let's make another data template (implicit in this case) to show our items in ListBox. We'll use DataTriggers to defer the color of each item.

<DataTemplate DataType="{x:Type l:RedBlackItem}">
            <Border BorderBrush="Blue" BorderThickness="1">
                <Border.Resources>
                    <Style TargetType="Rectangle">
                        <Setter Property="Fill" Value="Black"/>
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Path=IsRed}" Value="True">
                                <Setter Property="Fill" Value="Red"/>
                            </DataTrigger>
                         </Style.Triggers>
                    </Style>
                </Border.Resources>
                <Rectangle Width="100" Height="30"/>
            </Border>
        </DataTemplate>

Another thing we want to do is to detect selected item and put another filler for it. Smile in our case. Once, the items displayed by ListBoxItem, which is, actually ancestor of  our shape and have IsSelected dependency property, we can use relative source in DataTrigger to understand whether it selected or not. So fixing code like this will do the work

<DataTemplate DataType="{x:Type l:RedBlackItem}">
            <Border BorderBrush="Blue" BorderThickness="1">
                <Border.Resources>
                    <Style TargetType="Rectangle">
                        <Setter Property="Fill" Value="Black"/>
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Path=IsRed}" Value="True">
                                <Setter Property="Fill" Value="Red"/>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}, Path=IsSelected}" Value="True">
                                <Setter Property="Fill">
                                    <Setter.Value>
                                        <VisualBrush>
                                            <VisualBrush.Visual>
                                                <Path Fill="Black" Stroke="Yellow" StrokeThickness="1">
                                                    <Path.Data>
                                                        <GeometryGroup>
                                                            <EllipseGeometry Center="50,15" RadiusY="10" RadiusX="10"/>
                                                            <EllipseGeometry Center="45,13" RadiusY="3" RadiusX="3"/>
                                                            <EllipseGeometry Center="55,13" RadiusY="3" RadiusX="3"/>
                                                            <LineGeometry StartPoint="50,15" EndPoint="50,20"/>
                                                        </GeometryGroup>
                                                    </Path.Data>
                                                </Path>
                                            </VisualBrush.Visual>
                                        </VisualBrush>
                                    </Setter.Value>
                                </Setter>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Border.Resources>
                <Rectangle Width="100" Height="30"/>
            </Border>
        </DataTemplate>

Now, when we can select items inside ItemsControl, we want to take it a bit far and select it by using control buttons. Let's make two RoutedCommand for this purpose and bind it by using command binding to our Window.

public static RoutedCommand MoveToPreviousCommand;
public static RoutedCommand MoveToNextCommand;

static Window1()
        {
            MoveToPreviousCommand = new RoutedCommand("MoveToPrevious", typeof(Window1));
            MoveToNextCommand = new RoutedCommand("MoveToNext", typeof(Window1));

            CommandManager.RegisterClassCommandBinding(typeof(Window1), new CommandBinding(MoveToPreviousCommand, ExecutedMoveCommand));
            CommandManager.RegisterClassCommandBinding(typeof(Window1), new CommandBinding(MoveToNextCommand, ExecutedMoveCommand));

        }

Inside the handler, we'll check what to do and use MoveToPreviouse/MoveToNext methods of collection view to manage it. Also, we want our collection to select first member, when we reach the end of it and last member, when we want one item before the first one. We might have all necessary information inside CollectionView to do it

static void ExecutedMoveCommand(object s, ExecutedRoutedEventArgs e)
        {
            Window1 w = s as Window1;
            if (e.Command == MoveToPreviousCommand)
            {
                w.data.View.MoveCurrentToPrevious();
                if (w.data.View.IsCurrentBeforeFirst)
                {
                    w.data.View.MoveCurrentToLast();
                }
            }
            else if (e.Command == MoveToNextCommand)
            {
                w.data.View.MoveCurrentToNext();
                if (w.data.View.IsCurrentAfterLast)
                {
                    w.data.View.MoveCurrentToFirst();
                }
            }
        }

All you have to do now is to create two RepeatButtons with nice vector arrows for back and forward to execute two of our commands and manage the collection

<RepeatButton Command="{x:Static l:Window1.MoveToPreviousCommand}"><Path Data="M1,1 L2,2 2,0 1,1"/></RepeatButton>
<RepeatButton Command="{x:Static l:Window1.MoveToNextCommand}"><Path Data="M2,1 L1,2 1,0 2,1"/></RepeatButton>

We done. A little facelifting and it's ready. Now we can know what item is selected by differing it inside it's visual presentation and data behind and viewing it as current item independently.

Thank you WPF for easy life. Be good people and learn a lot in Mix today.

Stay tuned for next days to my blog, In spite of the fact, that I'm not in Mix this year, I'm going to write about new beta version of Silverlight 2.0 with non-commercial go-life licence, small talk about XNA and my TechEd presentation, a bit about mobile development and other very cool things. Be sure to subscribe to my RSS feed for all this information.

Source code for this article

Monday, March 03, 2008

Quick tip: How to open popup or ContextMenu in XBAP application

If you want to use ContextMenu in XBAP application this will work, only if your XBAP is in full trust mode. Else you'll get "Request for the permission of type 'System.Security.Permissions.UIPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed". But even with Full Trust application, if you'll try to open context menu explicitly it wont. The error, you'll get will be "Cannot create a top-level child window". This is right. You cannot use layered windows without explicitely set parent inside browser sandbox.

Why this happens? Honestly, as for me, this is bug inside System.Windows.Controls.PopupControlService internal class (tnx to Shared .NET source code). For some reason PopupControlService set parent of the context menu while it raises ToolTipOpenning event. More, then this, it uses internal dependency property OwnerProperty to do it. So pity.

_currentToolTip.SetValue(OwnerProperty, o);

And if it is not enough it probably has memory leak, I described earlier.

_currentToolTip.Closed += OnToolTipClosed;
private void OnToolTipClosed(object sender, EventArgs e)
        {
            ToolTip toolTip = (ToolTip)sender;
            toolTip.Closed -= OnToolTipClosed;

Never mind. The question is how to take care on it, when we have no public Owner property and Parent is read only? Fortunately, dev team leave us PlacementTarget property to explicitly set the owner of ContextMenu. So, all you have to do is to add one line before setting IsOpen property to true.

private void OnMouseDown(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                contextMenu.PlacementTarget = sender as UIElement;
                contextMenu.IsOpen = true;
            }
        }

 

That's all, folks. Now you can safely open ContextMenu in XBAP application not only with right, but also with Left mouse button as well as with keyboard event or any other code you want to.

Have a nice day.

Sunday, March 02, 2008

How to: High performance graphics in WPF

Microsoft DPE: "WPF is ever best super performance oriented technology for creating hyper multi point graphs, using parallelism and huge in-memory persistence vector scrounged math distributed calculations... And ever more with branded new Microsoft Windows Vista 7.

Client: Errr, well.... Let's try to apply it for our VB program...

DPE: You can easily do it yourself, but it'd be better to call someone from Microsoft Consulting Services.

Client: Let it be...

MCS: Well. It's too huge for WPF to scale... WPF uses a retained rendering system. It saves every little pixel and make you able scale and repaint very often without the composition system blocking on callbacks to your code. However, 1,000,000 retained pixels is too huge to scale...

Client: I want it scale. They promised... They told, it'll scale. Make it to do what I want it to do!!!

MCS: Errr, well. Let it be!

This is very common dialog between DPE, MCS and clients. Sales men want it to do, what he need it to do. Client want it to do what sales men promised to do and Services men should make it to do what they both want it to do. Today we'll speak about retained, lazy and loose models to produce large scale graphics.

First problem: multithreading

Even before we start to work, you should know, that we cannot create dependency objects in thread other, then current UI thread. We can use locks, mutexes, semaphores, however we still can not create Dependency Objects in other thread. In order to get rid of it, we'll have to use INofityPropertyChanged implementation, instead of Dependency Objects. This means, no Dependency Properties.

So, we'll start with following code (I'll reuse nice code written by Luis Diego Fallas to create Mandelbrot Fractal set)

class FractsCollection : INotifyPropertyChanged
    {

Second problem: rendering thread

Well, the problem is knows. There is only one UI thread. We wont it be only one, so we'll use our own HostVisual by Dwayne Need to enhance the performance.

Third problem: Retained objects

Actually, this is not problem. This is feature. And it can be extremely useful if you want to retain layout. Let's create a simple example: Dragon curve fractal. It has limited number of points, generated by well known final algorithm. So, we'll create our own geometry, derived from Shape class. The fasted geometry is StreamGeometry, so let's use it. First of all let's create the class and save the array of points.

public class DragonShape:Shape
    {
StreamGeometry dragonGeometry;
        double _angle;
        List<Point> Points;

Then we'll generate the pattern

void GeneratePattern()
        {
            ThreadPool.QueueUserWorkItem(delegate
            {
Move(5);
Turn(GetNextPoint * System.Math.PI / 180.0);

Then, by overriding DefiningGeometry property, create the fractal

protected override System.Windows.Media.Geometry DefiningGeometry
        {
            get { 
                using (StreamGeometryContext context = dragonGeometry.Open())
                {
                    context.BeginFigure(Points[0], false, false);
                    context.PolyLineTo(Points, true, false);
                }
                return (Geometry)dragonGeometry.GetAsFrozen();}
        }

Don't forget to tell the shape, that geometry was changed

this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Background, (SendOrPostCallback)delegate
                    {
                        this.InvalidateVisual();
                    }, null);

Now, run it and we'll have very nice vector fractal generated that can be easily resized and scaled. Here the result.

image image

This method will work fine for 1,000, even 10,000 points. But after a while you'll experience performance degradation. What to do? The client wants 10,000,000 (!) points (and in Winforms GDI+ it works for him)

Let's try to understand why. Because it is not retain. It's image! So, let's use image to make the play fair.

The fastest BitmapSource is InteropBitmap. It has an ability to update itself from the memory section. That's exactly what we'll use

format = PixelFormats.Bgr32;
            max = format.BitsPerPixel;
            uint count = (uint)(sWidth * sHeight * format.BitsPerPixel / 8);
            section = CreateFileMapping(new IntPtr(-1), IntPtr.Zero, 0x04, 0, count, null);
            map = MapViewOfFile(section, 0xF001F, 0, 0, count);
            pixels = new byte[count];
            Marshal.Copy(pixels, 0, map, (int)count);
            source = System.Windows.Interop.Imaging.CreateBitmapSourceFromMemorySection(section, (int)sWidth, (int)sHeight, format, (int)(sWidth * format.BitsPerPixel / 8), 0) as InteropBitmap;
            ThreadPool.QueueUserWorkItem(delegate
            {
                while (true)
                {
                    Generate();
                }
            });

To get the source to bind to we'll get frozen image. Call Invalidate first to reread the source.

InteropBitmap source;
        public BitmapSource Source
        {
            get
            {
                source.Invalidate();
                return (BitmapSource)source.GetAsFrozen();
            }
        }

Now, when we ready to display we can just put pixels simultaneously (by using Parallel extension and PLINQ) and tell the WPF that our count and ImageSource property updated upon each pixel.

unsafe
            {
                uint* pBuffer = (uint*)map;
                Parallel.For(0, (int)sHeight, delegate(int yi)
                {
                    foreach (var p in from xi in Enumerable.Range(0, (int)sWidth).AsParallel()
                                      let mappedX = xF(xi)
                                      let mappedY = yF(yi)
                                      let p0 = new TranslatePoint(xF(xi), yF(yi))
                                      let function = constructor(p0)
                                      select new
                                      {
                                          x = xi,
                                          y = yi,
                                          xD = mappedX,
                                          yD = mappedY,
                                          i = apply(function, p0)
                                                  .TakeWhile(
                                                     (x, j) => j < max && x.NormSquared() < 4.0)
                                                  .Count()
                                      })
                    {
                        pBuffer[(int)(p.x + p.y * sWidth)] = (uint)(uint)((uint)0xFF << 24) |
                             (uint)(p.i << 16) |
                             (uint)(5*p.i << 8) |
                             (uint)(15*p.i); ;
                        count++;
                        FireUpdate();
                    }
                });
            }

void FireUpdate()
        {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("PixelCount"));
                    PropertyChanged(this, new PropertyChangedEventArgs("Source"));
                }
        }

We done. Now let's see how fast it can be to generate and display 10,000,000 live pixels (it's about 16,000x16,00x32bit image) in screen. The maximum, I was able to get with my Dell Latitude D820 was 1,200,000 x 1,200,000 pixels indexed 4 bit image (it's about 100,000,000 points) and my memory is over :)

Not bad, ah? So WPF scales and DPE are right? Not quite right, but let them to do their work and we'll be behind to come all client's dreams (and DPE's promises) true.

Have a nice day and be good people. Now you can use WPF for drawing big number of points.

Thursday, February 21, 2008

7 XNA community games for XBOX are free now!

Xbox Live Community Games is new program, announced by XNA Creators Club. It gives you a chance to download for free seven games for XBox absolutely free.  So, what are you waiting for? Download it now from http://creators.xna.com website.

What's included?

  • The Dishwasher: Dead Samurai
  • JelloCar
  • Little Gamers
  • Proximity HD
  • Rocketball
  • TriLinea
  • Culture

You can either watch an "XBox insider" video to learn how to download and run those games.

However, the most interesting news, is that Microsoft deciding be become an authority for distributing community games via Xbox Live Marketplace, thus now, you can write not only for yourself, but wider your game distribution with Microsoft.

So what are you waiting for? Download and install XNA Game Studio 2.0, come to my presentation in TechEd '08 to learn how to do it and begin to make your own gaming business (or do it just for your fun).

image

Wednesday, February 20, 2008

WPF (.NET 3.5) - what's next? The future roadmap of Windows Presentation Foundation

Today, Scott Guthrie, Microsoft's VP of .NET Developer Platform, blogged about the future plans of WPF.

Upcoming changes will mostly concentrated around LOB applications development, startup times and workset, text, bitmap effects and media performance improvements. As well there are plans about new WPF controls, such as Grid, Ribbon and Date Picker.

Read the whole article >>

Sunday, February 17, 2008

Sound, tone and DTMF generation by using managed DirectSound and C# and sine tone detection with pure managed Goertzel algorithm implementation

Well, today, we'll speak about math. A lot of math. We have a number of challenges today.

  • Generate sine, square or dual sine tone (DTMF - sounds, that your phone keyboard produces)
  • Playing it in managed code
  • Detection of base frequencies by using Goertzel (Fast Fourier) algorithm

Let's start with generation of tone. All we need is DirectSound. First of all, we should create format of wave sound

using DXS = Microsoft.DirectX.DirectSound;

Now, let's create our Device

Microsoft.DirectX.DirectSound.Device m_device = new Microsoft.DirectX.DirectSound.Device();
SourceHost = App.Current.MainWindow.Content as FrameworkElement;

m_device.SetCooperativeLevel(((HwndSource)PresentationSource.FromVisual(SourceHost)).Handle, DXS.CooperativeLevel.Priority);

As you can see, it, actually Control, thus we have to give him handle to sit on. 90%, that when you'll run it, you'll get strange exception: "DLL 'C:\Windows\assembly\GAC\Microsoft.DirectX\1.0.2902.0__31bf3856ad364e35\Microsoft.DirectX.dll' is attempting managed execution inside OS Loader lock. Do not attempt to run managed code inside a DllMain or image initialization function since doing so can cause the application to hang.". In order to get rid of it, just disable handling of LoaderLock exceptions under Managed Debugging Assistants section in Visual Studio Debug menu. Not very clear why it happens, but it is. After doing it, we can continue with in memory wave generation.

DXS.WaveFormat format = new DXS.WaveFormat();
            format.BitsPerSample = BitsPerSample;
            format.Channels = Channels;
            format.BlockAlign = BlockAlign;
            format.FormatTag = DXS.WaveFormatTag.Pcm;
            format.SamplesPerSecond = SampleRate;
            format.AverageBytesPerSecond = format.SamplesPerSecond * format.BlockAlign;

            DXS.BufferDescription desc = new DXS.BufferDescription(format);
            desc.DeferLocation = true;
            desc.BufferBytes = sample.Length;         

Now, when we'll create SecondaryBuffer to play with the Device

CurrentSample = sample;
CurrentBuffer = new DXS.SecondaryBuffer(desc, m_device);
CurrentBuffer.Write(0, sample, DXS.LockFlag.EntireBuffer);
CurrentBuffer.Play(0, DXS.BufferPlayFlags.Default);           

We done. Now let'sw create buffer to play. We'll start from the simplest sine wave. This type of wave looks like this

image

Not too hard to understand how to generate it

int length = (int)(SampleRate * BlockAlign * duration);
byte[] buffer = new byte[length];

double A = frequency * 2 * Math.PI / (double)SampleRate;
for (int i = 0; i < length; i++)
{
     //buffer[i] = (byte)(Math.Sin(i*A));
}

However, phone tones (DTMF) have double wave format. It looks like this

 image

As you can see, we take two waves and create it's composition. Here the table of tones to be used for DTMF creation.

     

Upper

Band

 
    1209 Hz 1336 Hz 1477 Hz 1633 Hz

 

697 Hz

1

2

3

A

Lower

770 Hz

4

5

6

B

Band

852 Hz

7

8

9

C

941 Hz

*

0

#

D

 

There are also events in telephony. They build on two frequencies as well

  Lower Band Upper Band
Busy signal 480 Hz 620 Hz
Dial tone 350 Hz 440 Hz
Flash (ringback) 440 Hz 480 Hz

So, in order to create such waves, we'll have to use following function (the simple one)

(128 + 63 * Math.Sin(n * 2 * Math.PI * LowFreq / rate) + 63 * Math.Sin(n * 2 * Math.PI * HighFreq / rate))

Actually, dual (or triple) sines should be generated by using something like this

int length = (int)(SampleRate * BlockAlign * duration);
            byte[] buffer = new byte[length];

            double A = frequency * 2 * Math.PI / (double)SampleRate;
            for (int i = 0; i < length; i++)
            {
                if (i > 1) buffer[i] = (byte)(2 * Math.Cos(A) * buffer[i - 1] - buffer[i - 2]);
                else if (i > 0) buffer[i] = (byte)(2 * Math.Cos(A) * buffer[i - 1] - (Math.Cos(A)));
                else buffer[i] = (byte)(2 * Math.Cos(A) * Math.Cos(A) - Math.Cos(2 * A));
            }

Done. We can generate and play sounds, tones and DTMF. How to detect what has been played? For this purpose, we should use Fourier transform, but it's very slow, thus Dr' Gerald Goertzel developed fast Fourrier Transform algorithm, that used almost in every DSP related product. A little theory about it can be found in Wikipedia (implementation there rather bad).

I simplified it, due to fact, that we actually know source possible frequencies, thus all we have to do is to check the result with well-known values

public double CalculateGoertzel(byte[] sample, double frequency, int samplerate)
{
    double Skn, Skn1, Skn2;
    Skn = Skn1 = Skn2 = 0;
    for (int i = 0; i < sample.Length; i++)
    {
        Skn2 = Skn1;
        Skn1 = Skn;
        Skn = 2 * Math.Cos(2 * Math.PI * frequency / samplerate) * Skn1 - Skn2 + sample[i];
    }

    double WNk = Math.Exp(-2 * Math.PI * frequency / samplerate);

    return 20* Math.Log10(Math.Abs((Skn - WNk * Skn1)));
}

This method checks it will steps of 5

public int TestGoertzel(int frequency, byte[] sample)
       {
           int stepsize = frequency / 5;
           Dictionary<int,double> res = new Dictionary<int,double>();
           for (int i = 0; i < 10; i++)
           {
               int freq = stepsize * i;
               res.Add(freq,CalculateGoertzel(sample,freq,SampleRate));
           }

And the actual result is rather clear

1200 - -272.273842954695
2400 - -198.538262992751
3600 - -214.476846236253
4800 - -224.242925995697
6000 - 87.1653837206957 <- here result
7200 - -225.52220053475
8400 - -222.836254116698
9600 - -230.526410368965
10800 - -220.587695185849

Have a fun with sines, waves and math and be good people. BTW, you can implement full telephone keypad, that actually working with one of my previous posts. To test it, just call any service, that required you to press any of phone buttons and sound the phone you just generated - it will accept it :)

Friday, February 15, 2008

Internet safety day and Red Fish Soup

A couple of days age, there was the Internet Safety Day in Israel (for some reason nothing about it in official government website, so last year news). I begun to think, what websites can be useful for my kids? Hop tv-channel web site is pretty nice, but it teaches nothing our kids. Tipo is absolutely unuseful. There are more? Not exactly...

After some research, I found great french web site, named Poisson Rouge (Red Fish Soup). This how should build web for kids. Shame to all locals...

image