DCSIMG
December 2006 - Posts - Just code - Tamir Khason

December 2006 - Posts

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/directshow-and-windows-media/]


יש מישהו "משלנו" שמבין טוב בDIRECTSHOW ו-WINDOWS MEDIA SERVICES? לא יתכן שאין...

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/test-to-blog-with-onenote-2007/]


This is the test post to check OneNote 2007 capabilities to blog with. Sure, it use Word 2007 blogging capabilities, but it still really cool to blog directly from OneNote.

   

We'll try to add image clipped by OneNote (just for test)

   

   

Just code - Tamir Khason

http://blogs.microsoft.co.il/blogs/tamir/

Screen clipping taken: 12/26/2006, 11:12 AM

   

For some reason http posting for images not working with MS installation of Community Server, but I have ftp for it, so there is no problem with it (for me)

   

We'll try to add voice clip as well. I'm pretty sure, that this one will not work, but who knows…

Audio recording started: 11:47 AM Tuesday, December 26, 2006

 

Now we'll go to File->Send to->Blog

This one open new Word document with my post in it

   

Screen clipping taken: 12/26/2006, 11:48 AM

   

We'll click Manage account and set our blog

   

   

Screen clipping taken: 12/26/2006, 11:48 AM

   

For pictures (as I noticed earlier) I was unable to find right URL, so using ftp to post

   

   

Screen clipping taken: 12/26/2006, 11:49 AM

   

Now, the only thing we should do is to post it.

Hello, world from One Note 2007. LiveWriter may remain in the download folder.

   

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/new-firmware-for-e61-smartphones/]


My Nokia E61 got new firmware (3.0.633.09.04). Changes are:

  • Faster UI performance
  • Faster web browsing
  • If you accidentally press the voice record button on the side the application launches but doesn't start to automatically record like before!
  • SMS sending now faster
  • Changes to Internet Telephony Application
  • Better IMAP handling

Some of things you should be awared of

Backup all you have to memory card and to computer. Know, that non of your 3d party sis and java applications will remain

Don't be fool and use Nokia Phone Software Updater to update your phone. You do not need Nokia service provider

All password information (e.g. exchange push, blackberry, etc..) will lost most of settings. Be ware, that you should remember them

If you have problems, use *#7370# for hard reset

You should know your memory card password. After restart you'll have to enter it. Esli you lost all your information.

You should know default PINs for your bluetooth devices. Neither of them will remain paired.

That's it. Happy Symbian to all

UPD Almost forgot about lock code. It become 12345 (as by default). Remember, that default input method is ABC, so press the blue key to enter numbers.

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/ink-recognition-in-wpf/]


Tablets world is coming, but we, who even have no laptop (due my manager's greed), working with workstation. Our clients want top edge technology approaches, such as geometrical forms and inks recognition within WPF code in regular desktop computer. So, let's see how we can do it. Today we'll create hand drawing form with basic geometry and handwrited text analysis

First of all, we'll need to bare all ink-related assemblies. Let's put on Microsoft.Ink and Minrosoft.Ink.Analysis. It comes with IACore and IAWinFX in "foundation world". So we'll go to C:\Program Files\Reference Assemblies\Microsoft\Tablet PC\v1.7\ and take everything from there into our application.

Next thing is to create the place where we'll draw our input. InkCanvas looking promise for me

<InkCanvas Width="Auto" Height="Auto" Name="myInkCanvas"  />

Next we'll create InkAnalyzer to analyze our inking. We'll work asyncronous to get nice performance

InkAnalyzer m_analyzer;

        void onLoaded(object sender, RoutedEventArgs e)
        {
            m_analyzer = new InkAnalyzer();
            m_analyzer.AnalysisModes = AnalysisModes.AutomaticReconciliationEnabled;
            m_analyzer.ResultsUpdated += new ResultsUpdatedEventHandler(m_analyzer_ResultsUpdated);
        }

Fine. Now we should provide to our InkAnalyzer information about what it'll going to work for. So, start collection and erasing strokes will make my live easier. We'll have to tell background engine of ink analysis when we want to do anything. Sure, we'll do it after new stroke arrived.

void onStrokeErasing(object sender, InkCanvasStrokeErasingEventArgs e)
        {
            m_analyzer.RemoveStroke(e.Stroke);
        }
 
 
 
        void onStrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
        {
            m_analyzer.AddStroke(e.Stroke);
            m_analyzer.BackgroundAnalyze();
        }

 

Goody. Now let's see what we have. We can draw geometry and we can write text. Let's start with the text. We'll create little helper to draw our stings on the surface

class GistaFigure:FrameworkElement
    {
        FormattedText ft;
        Point cent;
        private GistaFigure() { }
        public GistaFigure(string name, Point center, double size, Brush color)
        {
            init(name, center, color, size);
        }
        public GistaFigure(string name, Point center, Brush color)
        {
            init(name,center,color, 12);
 
        }
 
        void init(string name, Point center, Brush color, double size)
        { 
            ft = new FormattedText(
                name, System.Globalization.CultureInfo.CurrentCulture,
               FlowDirection.LeftToRight,
               new Typeface(new FontFamily(), FontStyles.Normal, FontWeights.Normal, FontStretches.Normal), size, color);
 
 
            cent = center;
        }
 
        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);
            cent.X -= ft.Width / 2;
            cent.Y -= ft.Height / 2;
            drawingContext.DrawText(ft,cent);
 
        }
    }

Now we can analyze it. I want to color my result other colors, related to confidence of the detection

void m_analyzer_ResultsUpdated(object sender, ResultsUpdatedEventArgs e)
        {
            if (e.Status.Successful)
            {
                ContextNodeCollection nodes = ((InkAnalyzer)sender).FindLeafNodes();
                foreach (ContextNode node in nodes)
                {
                    if (node is InkWordNode)
                    {
                        InkWordNode t = node as InkWordNode;
                        Rect l = t.Location.GetBounds();
                        Point a = new Point(l.Left + l.Width / 2, l.Top + l.Height / 2);
                        double de = l.Height;
                        Brush be = Brushes.Blue;
 
                        switch (t.InkRecognitionConfidence)
                        { 
                            case InkRecognitionConfidence.Intermediate:
                                be = Brushes.Green;
                                break;
                            case InkRecognitionConfidence.Poor:
                                be = Brushes.Red;
                                break;
                            case InkRecognitionConfidence.Unknown:
                                be = Brushes.Brown;
                                break;
                        }
                        GistaFigure figure = new GistaFigure(t.GetRecognizedString(), a,de , be);
                        myInkCanvas.Children.Add(figure);
                    }

Guta! We have "HELLO" recognized, but I want to recognize geometry. Else, my client will throw me out 'cos the way I'm writing text really horrible with the mouse (I have no tablet, you remember?). Let's recognize geometry. Those are other nodes, named InkDrawingNodes. Do it

else if (node is InkDrawingNode)
                    {
                        InkDrawingNode d = node as InkDrawingNode;
                        GistaFigure figure = new GistaFigure(d.GetShapeName(), d.Centroid, Brushes.Red);
                        Shape shape = d.GetShape();
                        if (shape != null)
                        {
                            shape.Stroke = Brushes.Blue;
                            shape.StrokeThickness = 2;
                            myInkCanvas.Children.Add(shape);
                        }
                        myInkCanvas.Children.Add(figure);
                    }

Donno. I have nothing more to add here. So let's draw (for those, who know to do it with only mouse)

Source code for this article

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/running-action-script-under-wpf/]


Can we run Adobe ActionScript from WPF? YES! we can. Just look at this screenshot

Nice, let's see how we do it. First let's take out  our textbox with syntax hightlighing and use it within the project. We not really do it with ActionScript, but with MS JScript (which is seemed to be almost compatible to AS2.0).

So the next step (after we wrote JS) is to compile it. We'll use System.Reflection and System.Reflection.Emit namespaces. Let's take our source and compile it

public static CompilerResults Compile(string source)
        {
 
            CompilerParameters parameters = new CompilerParameters();
            parameters.GenerateInMemory = true;
 
            CompilerResults results = (new Microsoft.JScript.JScriptCodeProvider()).CompileAssemblyFromSource(parameters, source);

Fine, now we have results. If the result has errors we do not want to process so

if (results.Errors.HasErrors)
            {
                return results;
            }

In order to make the use know about errors, we'll build panel like in Visual Studio with all of our exceptions (if there are). We'll make XMLDataProvider resource for error list

<XmlDataProvider x:Key="myDebugData" XPath="/myXmlData">
      <x:XData>
        <myXmlData xmlns=""/>
      </x:XData>
    </XmlDataProvider>

and add there all of our erorrs

lstErrors.SetValue(ListView.HeightProperty, (double)0);
            errors = Resources["myDebugData"] as XmlDataProvider;
            errors.Document.SelectSingleNode(errors.XPath).RemoveAll();
 
            System.CodeDom.Compiler.CompilerResults results = JSCompiler.Compile(source);
            if (results.Errors.HasErrors)
            {
                WriteErrors(results.Errors);
                return;
            }
            //no errors. Show methods
            myMethods = Resources["myMethods"] as JSMethods;
            myMethods.Add(new JSMethod(JSCompiler.LastDelegate));

 

As you can see, I change Height dependency property of ListView in order to recieve the behaviour of VS error panel. This ListView is binded to our data source, so we do not need anything except it

<ListView DockPanel.Dock="Bottom" Height="0" Name="lstErrors" ItemsSource="{Binding Source={StaticResource myDebugData}, XPath=Error}">
      <ListView.View>
        <GridView x:Name="debugView">
          <GridViewColumn Header="Description" Width="300" DisplayMemberBinding="{Binding XPath=@Description}"/>
          <GridViewColumn Header="File" Width="120" DisplayMemberBinding="{Binding XPath=@File}"/>
          <GridViewColumn Header="Line" DisplayMemberBinding="{Binding XPath=@Line}"/>
          <GridViewColumn Header="Column" DisplayMemberBinding="{Binding XPath=@Column}"/>
          <GridViewColumn Header="Project" DisplayMemberBinding="{Binding XPath=@Project}"/>
        </GridView>
      </ListView.View>
    </ListView>

 

Let's make it fun!

void ShowErrors()
        {
            DoubleAnimation errAni = new DoubleAnimation(0, this.ActualHeight / 6, new Duration(TimeSpan.FromSeconds(0.5)));
            Storyboard.SetTargetName(errAni, "lstErrors");
            Storyboard.SetTargetProperty(errAni, new PropertyPath(ListView.HeightProperty));
            Storyboard errStory = new Storyboard();
            errStory.Children.Add(errAni);
            errStory.Begin(this);
        }

And usefull

void WriteError(System.CodeDom.Compiler.CompilerError error)
        {
            XmlElement item = errors.Document.CreateElement("Error");
            item.SetAttribute("Description", error.ErrorText);
            item.SetAttribute("File", error.FileName);
            item.SetAttribute("Line", error.Line.ToString());
            item.SetAttribute("Column", error.Column.ToString());
            item.SetAttribute("Project", currentFileName);
 
            errors.Document.SelectSingleNode(errors.XPath).AppendChild(item);
        }

 

If we have no errors, let's  process to our code compilation.

else
{
    Assembly result = results.CompiledAssembly;
    Type[] resultTypes = result.GetTypes();
    for (int j = 0; j < resultTypes.Length; j++)
    {
        if (resultTypes[j].BaseType != typeof(Microsoft.JScript.GlobalScope))
        {
            MethodInfo[] mi = resultTypes[j].GetMethods();
            for (int i = 0; i < mi.Length; i++)
            {
                //we do not want default object's methods
                if (mi[i].Name != "GetType" &
                    mi[i].Name != "ToString" &
                    mi[i].Name != "Equals" &
                    mi[i].Name != "GetHashCode")
                {
                    Type delegateType = CreateDelegate(mi[i]);
 
                    object instance = Activator.CreateInstance(resultTypes[j]);
 
                    if ((mi[i].Attributes & MethodAttributes.Static) == MethodAttributes.Static)
                    {
                        instance = null;
                    }
                    else
                    {
                        instance = Activator.CreateInstance(resultTypes[j]);
                    }
 
                    LastDelegate = Delegate.CreateDelegate(delegateType, instance, mi[i]);
 
                }
            }
        }
    }

We do not really want to compile our code into assemby and reference it to our binaries, so we'll have to create a list of delegates in order to be able to execute those IL methods. The tricky part is to know if the method, I want to create delegate for is not static, 'cos in this case we wont create an instance of hosing application. So let's create a delegate type from MethodInfo for our method.

static Type CreateDelegate(MethodInfo method)
        {
            AssemblyName assembly = Assembly.GetExecutingAssembly().GetName();
            AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assembly, AssemblyBuilderAccess.RunAndSave);
            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(method.Module.Name);
            TypeBuilder typeBuilder = moduleBuilder.DefineType(method.Name, TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.AnsiClass | TypeAttributes.AutoClass, typeof(System.MulticastDelegate));
            ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { method.ReturnType });
            constructorBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);
 
            ParameterInfo[] parameters = method.GetParameters();
            Type[] parameterTypes = new Type[parameters.Length];
 
            for (int i = 0; i < parameters.Length; i++)
            {
                parameterTypes[i] = parameters[i].ParameterType;
            }
 
            MethodBuilder methodBuilder = typeBuilder.DefineMethod("Invoke", MethodAttributes.PrivateScope | MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.VtableLayoutMask, method.ReturnType, parameterTypes);
            methodBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed | method.GetMethodImplementationFlags());
 
            Type type = typeBuilder.CreateType();
 
            return type;
        }

Fine, now the only thing we should do is create the instance of this delegate and execute it when needed. In order to make me able to bind and manage it and later use it for automatic executions and WPF code generation. I will want to compare those methods in order to know if the method I just created is not the method I already have. So overroding Method operators will really help me with it.

public class JSMethod
    {
        private Delegate executer;
        public Delegate Executer
        {
            get { return executer; }
            set { executer = value; }
        }
 
        private string methodName;
        public string MethodName
        {
            get { return methodName; }
            set { methodName = value; }
        }
 
        private JSCompiler.Parameter[] parameters;
        public JSCompiler.Parameter[] Parameters
        {
            get { return parameters; }
            set { parameters = value; }
        }
 
        private string className;
        public string ClassName
        {
            get { return className; }
            set { className = value; }
        }
        public JSMethod(Delegate source)
        {
            this.executer = source;
            if (this.executer.Target != null)
            {
                this.className = this.executer.Target.GetType().ToString();
            }
            else {
                this.className = "Static Class";
            }
            parameters = JSCompiler.GetParameters(source);
            string sps = "";
            for (int i = 0; i < parameters.Length; i++)
            {
                sps += parameters[i].ToString();
            }
            this.methodName = this.executer.Method.Name+"( "+sps+" )";
        }
        public JSMethod(string cName)
        {
            className = cName;
        }
 
        public static bool operator ==(JSMethod method1, JSMethod method2)
        {
            object o1 = method1;
            object o2 = method2;
            if (o1 == null & o2 == null)
                return true;
            else if (o1 == null | o2 == null)
                return false;
            else if (method1.ClassName == method2.ClassName &&
           method1.MethodName == method2.MethodName)
                return true;
            return false;
        }
        public static bool operator !=(JSMethod method1, JSMethod method2)
        {
            return !(method1 == method2);
        }
 
    }

The ObservableCollection of all methods will be later binded to hierarcical tree view to group it by classes

public class JSMethods : ObservableCollection<JSMethod>
    {
        public JSMethods()
        {
 
        }
 
 
        public new void Add(JSMethod method)
        {
            for (int i = 0; i < base.Count; i++)
            {
                if (this[i] == method)
                    return;
            }
            base.Add(method);
        }
    }

Ok, now how to group it? If my data source was XML data or any other hierarcical data, I was able just bind it to treeview, and smart Binding will parse it as needed, but my data is not hierarcical, so I'll have to make some woodoo in order to "tell" control how to show it. First of all I need a source and this is really simple touch.

<local:JSMethods x:Key="myMethods"/>

If I want to bing it as is, I would use something like this

<ObjectDataProvider x:Key="myMethods" ObjectType="{x:Type local:JSMethods}"/>

But, because of my data is hierarcical, I'll need something like this

<CollectionViewSource x:Key="methodsViewSource" Source="{Binding Source={StaticResource myMethods}}">
      <CollectionViewSource.GroupDescriptions>
        <PropertyGroupDescription PropertyName="ClassName"/>
      </CollectionViewSource.GroupDescriptions>        
    </CollectionViewSource>

Even if I told my data how to group, it's not so clear for the control, so I'll need additional templates to use

<HierarchicalDataTemplate x:Key="classTemplate"
      ItemTemplate="{StaticResource methodTemplate}"
      ItemsSource="{Binding Path=Items}">
      <TextBlock Text="{Binding Path=Name}" FontWeight="Bold" />
    </HierarchicalDataTemplate>

and one "regulat" template for items

<DataTemplate x:Key="methodTemplate" DataType="{x:Type local:JSMethod}">
      <ContentControl  MouseDoubleClick="onMethodClick">
        <TextBlock Text="{Binding Path=MethodName}"/>
      </ContentControl>
    </DataTemplate>

If I'd like to continue hierarcy, I'll need to use another Hierarchical data template for it, but in this case, what I have is prety enough for me. Bind 'em all

<TreeView Name="methodsTree" DataContext="{Binding Source={StaticResource methodsViewSource}}" ItemsSource="{Binding Path=Groups}" ItemTemplate="{StaticResource classTemplate}" Width="0">

That's all folks. I got a error list, delegates, bindings. Now I want to execute it. That mean, I'll have to build custom user interface, that will be built according my data types and all of my parameters for all methods. Do it.

While clicking on the method descriptor, I will exchange its content with new generated panel, incuding everything I know about the method and will pass it within right types into delegate and evaluate it

 

protected void onMethodClick(object sender, MouseButtonEventArgs e)
        {
            JSMethod method = methodsTree.SelectedItem as JSMethod;
            if (method == null)
                return;
            StackPanel sp = new StackPanel();
            sp.Width = this.Width - 50;
            sp.Height = method.Parameters.Length*40+50;
            sp.Orientation = Orientation.Vertical;
            for (int i = 0; i < method.Parameters.Length; i++)
            {
                StackPanel s = new StackPanel();
                s.Width = this.Width - 50;
                s.Height = 40;
                s.Orientation = Orientation.Horizontal;
                TextBox t = new TextBox();
                t.Height = 20;
                t.Width = this.Width / 2 - 50;
                t.Text = method.Parameters[i].Name+"("+method.Parameters[i].Type.ToString()+")";
 
                TextBox tb = new TextBox();
                tb.Height = 20;
                tb.Width = this.Width / 2 - 50;
                tb.Name=method.Parameters[i].Name;
 
                s.Children.Add(t);
                s.Children.Add(tb);
                sp.Children.Add(s);
            }
            Button b = new Button();
            b.VerticalAlignment = VerticalAlignment.Bottom;
            b.Click += new RoutedEventHandler(b_Click);
            b.Height = 50;
            b.Width = this.Width-50;
            b.Content = "Execute Method";
 

How, I have to return my content of what I had in this line after method execution. So I'll save it with tags

sp.Tag = method;
            sp.Children.Add(b);
 
            ContentControl cc = sender as ContentControl;
            object[] obs = {
                cc,
                cc.Content
            };
            b.Tag = obs;
            cc.Content = sp;

On click the execute button, I'll want to execute method and return all I had

void b_Click(object sender, RoutedEventArgs e)
        {
            Button b = sender as Button;
            StackPanel sp = b.Parent as StackPanel;
            if (sp != null)
            { 
                JSMethod method = sp.Tag as JSMethod;
                if(method != null)
                {
                    object[] obs = b.Tag as object[];
                    ContentControl cc = obs[0] as ContentControl;
                    sp = cc.Content as StackPanel;
 
                    for (int i = 0; i < method.Parameters.Length; i++)
                    { 
                        StackPanel ssp = sp.Children[i] as StackPanel;
                        if (ssp != null)
                        {
                            method.Parameters[i].Value = GetValueString(method.Parameters[i].Name, ssp, method.Parameters[i].Type);
                        }
                    }
                    object o = JSCompiler.ExecuteMethod(method);
 
                    MessageBox.Show("The result value of " + method.MethodName + " is " + o.ToString(), "Result value of method evaluation", MessageBoxButton.OK, MessageBoxImage.Information);
                    object obb = obs[1];
                    cc.Content = obb;
                }
            }
        }

 

The other thing I should do is to find what parametes the user entered. Using VisualTree it become very easy

object GetValueString(string elementName, Visual elementParent, Type elementType)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(elementParent); i++)
            {
                Visual v = (Visual)VisualTreeHelper.GetChild(elementParent, i);
                if(v is TextBox)
                {
                    TextBox t = v as TextBox;
                    if(t.Name == elementName)
                    {
                        return Convert.ChangeType(t.Text, elementType);
                    }
                }
            }
            return null;
        }

 

The final salute is just execute it. You don't believe me. It's just one line of code. Here it comes

public static object ExecuteMethod(Delegate source, out Type returnType, params Parameter[] parameters)
        {
            returnType = source.Method.ReturnType;
            object[] ps = new object[parameters.Length];
            for (int i = 0; i < parameters.Length; i++)
            {
                ps[i] = parameters[i].Value;
            }
            return source.DynamicInvoke(ps);
        }

That's really all now. Thank you reflection, emit and WPF to beat evil Adobe with their "Ultra dynamic and useful tools". We really dont need it. We can do everything ourself!

Source code for this article

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/wtfl/]


110101111001100111010111100101011101011010111001110101111001
110100100000110101111001010011010110101101111101011110101001
110101101011110011010111100000011101011010110100110101111010
100111010110101111001101011110000001110101101011010011010111
100110010010000011010111100101011101011010110111110101111001
100111010110101100001101011110011011110101101011101111010111
100111001101011010111100110101111001010111010110101111000010
000011010111100101001101011010110111110101111010100111010110
101111001101011110000001110101101011100011010111100111101101
011010110111110101111001100111010110101101001101011110011101
001000001101011110010101110101101011000011010111100101001101
011010111000110101111001000011010110101110001101011110101000
110101101011011011010111101001010010000011010111100101011101
011010110000110101111001101111010110101110001101011110011100
110101101011111011010111101001101101011010110000110101111001
000111010110101110001101011110010000110101101011100011010111
100111011101011110000011001000001101011110010101110101101011
011111010111100110011101011010110000110101111001101111010110
101101111101011110011100001000001101011110010000110101101011
000111010111100111001101011010111001110101111001010011010110
101101001101011110011001110101111001110100100000110101111001
000111010110101111001101011010110111110101111001100111010110
101111001101011110010101110101101011100111010111100111010010
000011010111100101001101011010110111110101111010100111010110
101111001101011110000001110101101011000011010111100100011101
011010110100110101111001100111010111101000101101011010110100
110101111001100100101100001000001101011110011110110101101011
000011010111100111001101011010110111110101111001000011010111
100110111101011010110000110101111010101011010110101111001101
011110010101110101101011100100100000110101111001000011010110
101100101101011110101001110101111000000111010110101101101101
011110101000001000001101011110100010110101101011100011010111
101010011101011110000010110101101011100011010111100101000011
101100100000110101111001010111010110101101111101011110011001
110101101011110011010110101101001101011110101001110101111000
000111010110101100001101011110010001110101101011110011010110
101110011101011110101010001000001101011110010001110101101011
110011010110101101111101011110011001110101101011110011010111
100101011101011010111001110101111001110100100000110101111001
010011010110101101111101011110101001110101101011110011010111
100000011101011010110000110101111001000111010110101101001101
011110011001110101111010001011010110101101001101011110011001
001011000010000011010111100111101101011010110100110101111001
101111010110101111001101011010111000110101111001110011010110
101111101101011110011110110101101011000011010111100111001101
011010110111110101111001000011010111100110111101011010110000
110101111010101011010110101111001101011110010101110101101011
100100100000110101111001000011010110101100101101011110101001
110101111000000111010110101101101101011110101000001000001101
011110100010110101101011100011010111101010011101011110000010
110101101011100011010111100101001101011110000011001000000010
000011010111100101011101011010110111110101111001100111010110
101100001101011110010001110101101011100011010111101010001101
011010110110110101111001101011010110101100000010000011010111
100100001101011010110001110101111001110011010110101110011101
011110010100110101101011010011010111100110011101011110011101
001000001101011110010000110101101011011011010111101010101101
011010111110110101111001100111010111100101011101011010111001
110101111001110100100000110101111001010011010110101101111101
011110101001110101101011110011010111100000011101011010110000
110101111001000111010110101101001101011110011001110101111010
001011010110101101001101011110011001001011000010000011010111
100101011101011010110111110101111001100111010110101100001101
011110100111110101101011011111010111100100111101011010111100
110101101011010111010111101010011101011110000001001000001101
011110010000110101101011100111010111101010101101011110010101
110101101011100100111011001000001101011110011011110101101011
110011010110101101001101011110011001001000001101011110010001
110101111001010111010110101110010010000011010111101010011101
011110000001110101101011100011010111100100011101011010110111
110101111010101000100000110101111001111011010110101101001101
011110011011110101101011110011010110101110001101011110011100
110101101011111011010111100111101101011010110000110101111001
110011010110101101111101011110010000110101111001101111010110
101100001101011110101010110101101011110011010111100101011101
011010111001001011000010000011010111100100001101011010110010
110101111010100111010111100000011101011010110110110101111010
100011010110101111101101011110010001110101101011110011010110
101110001101011110101000110101101011100011010111100100000010
000011010111100100001101011010110001110101111001110011010110
101110011101011110010100110101101011010011010111100110011101
011110011101001000001101011110011100110101101011011111010111
101000101101011010110010110101111010100111010111100000101101
011110010101110101101011100111010111101010100000110100001010
110101111001000011010111100100011101011110011100001000001101
011110011001110101111010100100100000110101111010001011010111
100101011101011110010011001000001101011110010000110101111010
000011010111101010011101011110011001110101111001110100100000
110101111010100111010111101000101101011110010101110101111001
000111010111101010001101011110011001110101111001110100101110
001011100010111000100000110101111001010111010111100111101101
011110010100001000001101011110010000110101111010101011010111
10011011110101111001110100111111

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/richtextbox-syntax-highlighting/]


Today we'll learn how to create RichTextBox with syntax highlight ing. Syntax highlighting is a feature of some text editors that displays text—especially source code—in different colors and fonts according to the category of terms. This feature eases writing in a structured language such as a programming language or a markup language as both structures and syntax errors are visually distinct.

The first thing we need is RichTextBox. So, do it. I did some trick on it in order to prevent from <P> (Paragraph) inserting on each enter (with is default behavior of RichTextBox). I want only one line down when I pressed my Enter key.

<RichTextBox ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Visible" Name="TextInput" AcceptsReturn="True" TextChanged="TextChangedEventHandler">
      <RichTextBox.Resources>
        <Style TargetType="{x:Type Paragraph}">
          <Setter Property="Margin" Value="0"/>
        </Style>
      </RichTextBox.Resources>
    </RichTextBox>

Ok, not I want only plaintext in my TextBox TextChanged event 'cos I'm going to color it myself.

private void TextChangedEventHandler(object sender, TextChangedEventArgs e)
        {
            if (TextInput.Document == null)
                return;
 
            TextRange documentRange = new TextRange(TextInput.Document.ContentStart, TextInput.Document.ContentEnd);
            documentRange.ClearAllProperties();

 

Now let's create navigator to go though the text and hightlight it

TextPointer navigator = TextInput.Document.ContentStart;
            while (navigator.CompareTo(TextInput.Document.ContentEnd) < 0)
            {
                TextPointerContext context = navigator.GetPointerContext(LogicalDirection.Backward);
                if (context == TextPointerContext.ElementStart && navigator.Parent is Run)
                {
                    CheckWordsInRun((Run)navigator.Parent);
 
                }
                navigator = navigator.GetNextContextPosition(LogicalDirection.Forward);
            }

 

So the only thing we have to do is to check each word within my navigator, compare it to Highlighting dictionary and color it. Huh, where is my dictionary? Here it comes. I'll do it for ActionScript for my client.

class JSSyntaxProvider
   {
       static List<string> tags = new List<string>();
       static List<char> specials = new List<char>();
       #region ctor
       static JSSyntaxProvider()
       {
           string[] strs = {
               "Anchor",
               "Applet",
               "Area",
               "Array",
               "Boolean",.....
tags = new List<string>(strs);
 
We also want to know all possible delimiters so adding this stuff.
 
            char[] chrs = {
                '.',
                ')',
                '(',
                '[',
                ']',
                '>',
                '<',
                ':',
                ';',
                '\n',
                '\t'
            };
            specials = new List<char>(chrs);
 

Now I should check statically if the string I passed is legal and constants in my dictionary

public static bool IsKnownTag(string tag)
        {
            return tags.Exists(delegate(string s) { return s.ToLower().Equals(tag.ToLower()); });
        }

Also, I'll do kind of Intellisense later, so I want to check the beginnings of my tags as well

public static List<string> GetJSProvider(string tag)
        {
            return tags.FindAll(delegate(string s) { return s.ToLower().StartsWith(tag.ToLower()); });
        }

Wow. Great. Now I should separate words, that equals to my tags. For this propose we'll create new internal structure named Tag. This will help us to save words and its' positions.

new struct Tag
        {
            public TextPointer StartPosition;
            public TextPointer EndPosition;
            public string Word;
 
        }

How, let's go through our text and save all tags we have to save.

int sIndex = 0;
            int eIndex = 0;
            for (int i = 0; i < text.Length; i++)
            {
                if (Char.IsWhiteSpace(text[i]) | JSSyntaxProvider.GetSpecials.Contains(text[i]))
                {
                    if (i > 0 && !(Char.IsWhiteSpace(text[i - 1]) | JSSyntaxProvider.GetSpecials.Contains(text[i - 1])))
                    {
                        eIndex = i - 1;
                        string word = text.Substring(sIndex, eIndex - sIndex + 1);
 
                        if (JSSyntaxProvider.IsKnownTag(word))
                        {
                            Tag t = new Tag();
                            t.StartPosition = run.ContentStart.GetPositionAtOffset(sIndex, LogicalDirection.Forward);
                            t.EndPosition = run.ContentStart.GetPositionAtOffset(eIndex + 1, LogicalDirection.Backward);
                            t.Word = word;
                            m_tags.Add(t);
                        }
                    }
                    sIndex = i + 1;
                }
            }

How this works. But wait. If the word is last word in my text I'll never hightlight it, due I'm looking for separators. Let's add some fix for this case

string lastWord = text.Substring(sIndex, text.Length - sIndex);
            if (JSSyntaxProvider.IsKnownTag(lastWord))
            {
                Tag t = new Tag();
                t.StartPosition = run.ContentStart.GetPositionAtOffset(sIndex, LogicalDirection.Forward);
                t.EndPosition = run.ContentStart.GetPositionAtOffset(eIndex + 1, LogicalDirection.Backward);
                t.Word = lastWord;
                m_tags.Add(t);
            }

How I have all my words and its' positions in list. Let's color it! Dont forget to unsubscribe! text styling fires TextChanged event.

TextInput.TextChanged -= this.TextChangedEventHandler;
 
            for(int i=0;i<m_tags.Count;i++)
            {
                TextRange range = new TextRange(m_tags[i].StartPosition, m_tags[i].EndPosition);
                range.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.Blue));
                range.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold);
            }
            m_tags.Clear();
 
            TextInput.TextChanged += this.TextChangedEventHandler;

That's all. We have nice RichTextBox, that understands and color any syntax we want. Maybe later we'll add Intellisense for it.

Source code of this article

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/consumers-application-in-wpf/]


You really want to learn WPF? Really want? Build consumer's application. Inspired of this BoingBoing post, I decided to build real-world application. Know what? This really small app will help you to understand WPF's power. So let's start.

 

In order to be able to convert text values into binary representation and be able to bind all this stuff to somewhere we'll have to write converter. So, let's do it. What is converter? Converter is class, that implements IValueConverter interface with two methods: Convert and ConvertBack. Stange, ah? :) In our case we got string and convert it into other string. So, follow the code to do it.

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

       {
           string origStr = value as string;
           string outStr = "";
           if (value != null)
           {
               foreach (char ch in origStr)
               { 
                   outStr += System.Convert.ToString((int)ch,2).ToString();
               }
           }
 
           return outStr;
       }

Now, when we can write somewhere and see our "binary" result somewhere else, we can tell our application, that the string source is textbox, and bind the results to some textblock. But first, we should annonce in our XAML, that we have converter and want to use it. The way is straight forward: our namespace declaration, static resource declaration, binding. Here the code:

<Window x:Class="BinBanner.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:m="clr-namespace:BinBanner" 
    Title="BinBanner" Height="600" Width="700" Style="{DynamicResource normal}" Name="myWindow"
    >
 
<Window.Resources>
     <m:Text2ValueConverter x:Key="conv"/>
 
<TextBox Name="SourceText" Width="400" MaxLength="30"/>
 
<TextBlock Name="tText" FontSize="12" Foreground="White" 
VerticalAlignment="Center" HorizontalAlignment="Left" Width="176" 
Height="240" TextWrapping="Wrap" FontFamily="Lucida Sans" 
TextAlignment="Center" Margin="40" 
Text="{Binding ElementName=SourceText, Path=Text, Converter={StaticResource conv}}"/>

That's it. Now we have simple program, to convert regular string into binary representation. But we want something "more spicy". Let's draw it on t-shirt!

In order to "draw" any WPF control on other control we have VisualBrush. What do you think, it's doing? Yeah, it takes Visual and convert it to Brush. So let put out TextBlock into VisualBrush and draw on T-Shirt image. How pathetic! We want it more "spicy". Let draw our text on 3D t-shirt! We'll put Viewport3D into our program and make 3D Geometry. In order to create 3d mesh of t-shirt we'll need 3d editor. I used Bruce3d to do it. You can use whatever you want. But how we'll convert the mesh to XAML? Oh, god, thank you, WPF Conversion Contest. Oky. Now we have our 3d t-shirt and dynamic text on it. You can put into VisualBrush anything. Even MediaElement. This one will play videos over your t-shirts ;)

<GeometryModel3D.Material>
                    <DiffuseMaterial>
                      <DiffuseMaterial.Brush>
                        <VisualBrush>
                          <VisualBrush.Visual>
                            <Grid Name="tGrid" Background="Black" Width="500" Height="400">
 
                              <TextBlock Name="tText" FontSize="12" Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Left" Width="176" Height="240" TextWrapping="Wrap" FontFamily="Lucida Sans" TextAlignment="Center" Margin="40" Text="{Binding ElementName=SourceText, Path=Text, Converter={StaticResource conv}}"/>
                            </Grid>
                          </VisualBrush.Visual>
                        </VisualBrush>
                      </DiffuseMaterial.Brush>
                    </DiffuseMaterial>
</GeometryModel3D.Material>

 

Good, now it's time to make our customer to be able to choose a color of our t-shirt. For this propose, well create smal "smart" enum (as like as Colors or Brushes class do). It'll have static constructors to get everything we need about the color.

public sealed class ColorPickColors
    {
        private ColorPickColors(){}
 
        static bool m_reverse = false;
        static Color m_color;
        static string m_name;
 
        public bool IsColorDark
        {
            get { return !m_reverse; }
        }
 
        public Color GetColor
        {
            get { return m_color; }
        }
 
        public string GetName
        {
            get { return m_name; }
        }
 
        public static ColorPickColors Black { get { ColorPickColors cpc = new ColorPickColors(); m_name = "Black"; m_color = Colors.Black; m_reverse = false; return cpc; } }
        public static ColorPickColors White { get { ColorPickColors cpc = new ColorPickColors(); m_name = "White"; m_color = Colors.White; m_reverse = true; return cpc; } }
        public static ColorPickColors Red { get { ColorPickColors cpc = new ColorPickColors(); m_name = "Red"; m_color = Colors.Red; m_reverse = false; return cpc; } }
        public static ColorPickColors Pink { get { ColorPickColors cpc = new ColorPickColors(); m_name = "Pink"; m_color = Colors.Pink; m_reverse = true; return cpc; } }
        public static ColorPickColors CityGreen { get { ColorPickColors cpc = new ColorPickColors(); m_name = "City Green"; m_color = Color.FromRgb(0x5C, 0x52, 0x39); m_reverse = false; return cpc; } }
        public static ColorPickColors HotPink { get { ColorPickColors cpc = new ColorPickColors(); m_name = "Hot Pink"; m_color = Color.FromRgb(0xDB, 0x00, 0x68); m_reverse = false; return cpc; } }
        public static ColorPickColors Turquoise { get { ColorPickColors cpc = new ColorPickColors(); m_name = "Turquoise"; m_color = Color.FromRgb(0x09, 0xAB, 0x9C); m_reverse = false; return cpc; } }
        public static ColorPickColors BabyBlue { get { ColorPickColors cpc = new ColorPickColors(); m_name = "Baby Blue"; m_color = Color.FromRgb(0x92, 0xBA, 0xDD); m_reverse = true; return cpc; } }
        public static ColorPickColors DeepHeather { get { ColorPickColors cpc = new ColorPickColors(); m_name = "Deep Heather"; m_color = Color.FromRgb(0x83, 0x7B, 0x79); m_reverse = true; return cpc; } }
        public static ColorPickColors Chocolate { get { ColorPickColors cpc = new ColorPickColors(); m_name = "Chocolate"; m_color = Color.FromRgb(0x3F, 0x2A, 0x25); m_reverse = false; return cpc; } }
        public static ColorPickColors LimeWedge { get { ColorPickColors cpc = new ColorPickColors(); m_name = "Lime Wedge"; m_color = Color.FromRgb(0xBC, 0xF6, 0xBC); m_reverse = true; return cpc; } }
 

Then we'll create the Visual to display it's values.

class ColorPick : FrameworkElement
{
    Color m_color = Colors.Black;
    Color m_fcolor = Colors.White;
    Brush fb, bb;
    Pen p;
    FormattedText m_name;
    ColorPickColors m_picker = ColorPickColors.Black;
 
    public Brush ElementBrush
    {
        get { return bb; }
    }
 
    public Brush TextBrush
    {
        get { return fb; }
    }
 
    public Color ElementColor
    {
        get { return m_color; }
    }
    public Color TextColor
    {
        get { return m_fcolor; }
    }
 
    public ColorPickColors SetColor
    {
        set
        {
            m_picker = value;
            m_color = m_picker.GetColor;
            bb = new SolidColorBrush(m_color);
            bb.Freeze(); 
            if (!m_picker.IsColorDark)
                m_fcolor = Colors.Black;
 
            fb = new SolidColorBrush(m_fcolor);
            fb.Freeze();
            p = new Pen(fb, 2);
            p.Freeze(); 
            m_name = new FormattedText(m_picker.GetName, System.Globalization.CultureInfo.CurrentCulture,
                FlowDirection.LeftToRight,
                new Typeface(new FontFamily(), FontStyles.Normal, FontWeights.Normal, FontStretches.Normal),
                12, fb);
        }
    }
 
    public ColorPickColors GetColor
    {
        get { return m_picker; }
    }
 
    protected override void OnRender(DrawingContext drawingContext)
    {
        base.OnRender(drawingContext);
        Rect r = new Rect(0,0,this.Width, this.Height);
        drawingContext.DrawRoundedRectangle(bb, p, r, 10, 10);
 
        if (m_name == null)
            SetColor = ColorPickColors.Black;
        drawingContext.DrawText(m_name,
            new Point(this.Width/2 - m_name.Width / 2, this.Height/2 - m_name.Height / 2)
            );
    }
 
}

 

I do not want manually add each of those controls for each color, so let's add some logic to ColorPickerColors to make it able to tell us about what colors it has. As well we'll add method to parse string input and return appropirate color.

public static string[] GetValues()
 {
     return GetValues(ColorPickColors.Black);
 }
 public static string[] GetValues(ColorPickColors obj)
 {
     Type t = obj.GetType();
     MemberInfo[] mi = t.GetMember("*",MemberTypes.Property, BindingFlags.Public | BindingFlags.Static);
     List<string> s = new List<string>();
     for (int i = 0; i < mi.Length; i++)
     {
         if (mi[i].DeclaringType == typeof(ColorPickColors) & mi[i].Name != "Parse")
         {
             s.Add(mi[i].Name);
         }
     }
 
     return s.ToArray();
 }
 
 public static ColorPickColors Parse(string name)
 {
     ColorPickColors res = ColorPickColors.Black;
     if (TryParse(name, out res))
     {
         return res;
     }
     else
     {
         throw new ArgumentOutOfRangeException();
     }
 }
 public static bool TryParse(string name, out ColorPickColors result)
 {
     MemberInfo[] mi = typeof(ColorPickColors).GetMember("*", MemberTypes.Property, BindingFlags.Public | BindingFlags.Static);
     for (int i = 0; i < mi.Length; i++)
     {
         if (mi[i].Name == name)
         {
             PropertyInfo prop = typeof(ColorPickColors).GetProperty(mi[i].Name);
             result = prop.GetValue(ColorPickColors.Black, null) as ColorPickColors;
             if (result != null)
             {
                 return true;
             }
             else 
             {
                 return false;
             }
 
         }
     }
     result = null;
     return false;
 }
 
Very good. The only thinkg I should do now is to add all my color controls to the application. How, when our classes are smarter, it's link a peace of cake
 
string[] _colors = ColorPickColors.GetValues();
            for (int i = 0; i < _colors.Length; i++)
            {
                ColorPick cp = new ColorPick();
                cp.SetColor = ColorPickColors.Parse(_colors[i]);
                cp.Width = 80;
                cp.Height = 30;
                cp.Margin = new Thickness(3);
                cp.HorizontalAlignment = HorizontalAlignment.Center;
                cp.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(cp_PreviewMouseLeftButtonDown);
                cp.PreviewMouseMove += new MouseEventHandler(cp_PreviewMouseMove);
                myColors.Children.Add(cp);
            }

I want to drag and drop those controls to my beautifull t-shirt to make my customer to be happy with UI. We'll implement it very easy way, without adorners and half opaqe controls, just standard darg and drop

void myViewport3D_DragOver(object sender, DragEventArgs e)
{
    if (!(e.Data.GetData("Color") is ColorPick))
    {
        DragDrop.DoDragDrop(this, e.Data, DragDropEffects.None);
    }
}
 
void myViewport3D_Drop(object sender, DragEventArgs e)
{
    mPick = ((ColorPick)e.Data.GetData("Color")).GetColor;
    tGrid.Background = ((ColorPick)e.Data.GetData("Color")).ElementBrush;
    tText.Foreground = ((ColorPick)e.Data.GetData("Color")).TextBrush;
    tDock.Background = ((ColorPick)e.Data.GetData("Color")).TextBrush;
 
}
 
void cp_PreviewMouseMove(object sender, MouseEventArgs e)
{
    if (e.LeftButton == MouseButtonState.Pressed)
    {
        DataObject _do = new DataObject();
 
        _do.SetData("Color", sender);
 
 
        DragDrop.DoDragDrop(this, _do, DragDropEffects.Copy | DragDropEffects.Move);
 
    }
}
 
 

The only think we should do now is to add this stuff to our application

<Viewbox>
      <DockPanel Name="myColors" ToolTipService.IsEnabled="True" ToolTipService.InitialShowDelay="0" ToolTipService.BetweenShowDelay="0" ToolTipService.HasDropShadow="True">
        <DockPanel.ToolTip>
          <TextBlock Text="Drag one of those color tags and drop it into t-shirt model to change it color"/>
        </DockPanel.ToolTip>
      </DockPanel>
    </Viewbox>

 

Fine, how the customer can choose the color, but what's about the size? We'll have to make him able to choose t-shirt size as well. Not all of our customers know what is "small,large,x-large" etc. Some of them want see bust size or the t-shirt length. So, in order to support it, let's create class for sizes.

class ApparelSize

    {
        string size;
        public string Size
        {
            get { return size; }
            set { size = value; }
        }
 
        int bust;
        public int Bust
        {
            get { return bust; }
            set { bust = value; }
        }
 
        int length;
        public int Length
        {
            get { return length; }
            set { length = value; }
        }
        public ApparelSize() { }
        internal ApparelSize(string size, int bust, int length)
        {
            this.Size = size;
            this.Bust = bust;
            this.Length = length;
 
        }
 
    }

Now, just add it into collection to create checkboxes

class ApparelsSizes : Collection<ApparelSize>
    {
       public ApparelsSizes()
        {
            this.Add(new ApparelSize("Small", 36, 26));
            this.Add(new ApparelSize("Medium", 40, 27));
            this.Add(new ApparelSize("Large", 44, 28));
            this.Add(new ApparelSize("X-Large", 48, 29));
            this.Add(new ApparelSize("2X-Large", 52, 30));
        }
    }
Fine.

After all this stuff we have two more resources to be added to our application. Sizes and Colors. As well as not all of our customers know what is Inch, so we'll have to support him to convert it into centimeters. Yap. another converter and static resource. Now, we'll add three comboboxes and two radiobuttons, each combo will show size in different way. Checkboxes will provide the customer way to convert inches to centimeters and vv.

<StackPanel DataContext="{StaticResource sizeSource}"  Orientation="Horizontal" ToolTip="Select the size of t-shirt you want to order. Notice, these items have a more relaxed fit.">
      <ComboBox Name="sSelection" Margin="10" ItemsSource="{Binding}" ItemTemplate="{StaticResource tSize}" IsSynchronizedWithCurrentItem="True" ToolTip="Size"/>
      <ComboBox Margin="10" ItemsSource="{Binding}" ItemTemplate="{StaticResource tBust}" IsSynchronizedWithCurrentItem="True" ToolTip="Bust"/>
      <ComboBox Margin="10" ItemsSource="{Binding}" ItemTemplate="{StaticResource tLength}" IsSynchronizedWithCurrentItem="True" ToolTip="Length"/>
      <StackPanel Margin="5" Orientation="Vertical">
        <RadioButton Name="inch" IsChecked="True">Inches</RadioButton>
        <RadioButton>Cm</RadioButton>
      </StackPanel>

Our datasource is static list of all sizes, but we have to write ItemTemplates for our combos to bind each of them to what we really want it to be binded.

<DataTemplate x:Key="tSize">
        <TextBlock Text="{Binding Path=Size}"/>
      </DataTemplate>
      <DataTemplate x:Key="tBust">
        <TextBlock Name="tbb"  Text="{Binding Path=Bust}"/>
        <DataTemplate.Triggers>
          <DataTrigger Binding="{Binding ElementName=inch, Path=IsChecked}" Value="False">
            <Setter Property="Text" Value="{Binding Path=Bust, Converter={StaticResource incm}}" TargetName="tbb" />
          </DataTrigger>
        </DataTemplate.Triggers>
      </DataTemplate>
      <DataTemplate x:Key="tLength">
        <TextBlock Name="tbl" Text="{Binding Path=Length}"/>
        <DataTemplate.Triggers>
          <DataTrigger Binding="{Binding ElementName=inch, Path=IsChecked}" Value="False">
            <Setter Property="Text" Value="{Binding Path=Length, Converter={StaticResource incm}}" TargetName="tbl" />
          </DataTrigger>
        </DataTemplate.Triggers>
</DataTemplate>

Done. Now, our customer can choose the color, size and even convert sizes from one unit to other. The only think we should add is, right! checkout! We really want to sell it, not to make the customer option to choose and print it. So, let's make ShoppingItem. It have to know what the customer want to write on the t-shirt, what color he want and what size he wearing. We maybe want to add the image of the t-shirt he build for himself, to refer it later, if he'll have problems with the order.

class ShoppingItem
    {
        ColorPickColors _color;
        public ColorPickColors Color
        {
            get { return _color; }
            set { _color = value; }
        }
        ApparelSize _size;
        public ApparelSize Size
        {
            get { return _size; }
            set { _size = value; }
        }
        string _text;
        public string Text
        {
            get { return _text; }
            set { _text = value; }
        }
 
        public System.Windows.Controls.Image _thumb;
        public System.Windows.Controls.Image Thumbnail
        {
            get { return _thumb; }
        }
 
        public ShoppingItem(ColorPickColors color,ApparelSize size, string text, System.Windows.Controls.Image thumb) 
        {
            _size = size;
            _color = color;
            _text = text;
            _thumb = thumb;
        }
    }

Wow, now we have another problem. Our 3d t-shirt is Visual, but I want it Image... Those are two different worlds, two different base classes. What do you think, let's try to render our FrameworkElement into Image? Let's do it.

Image Visual2Image(FrameworkElement elem)
        {
            RenderTargetBitmap rt = new RenderTargetBitmap((int)elem.ActualWidth, (int)elem.ActualHeight, 96, 96, PixelFormats.Pbgra32);
            rt.Render(elem);
            Image img = new Image();
            img.Source = BitmapFrame.Create(rt);
 
            return img;
        }

Now we'll add three buttons to add and remove items and check out the customer's order. We want to prevent the customer to remove or checkout items, when he have nothing in his shopping cart. So we have to know when the shopping cart is empty, and when it's full.

class ShopBox : ObservableCollection<ShoppingItem>
    {
        protected override void OnPropertyChanged(System.ComponentModel.PropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);
 
            if (e.PropertyName == "Count")
            { 
                this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("IsEmpty"));
                this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("IsFull"));
            }
 
        }
 
        public bool IsEmpty
        {
            get { return this.Count <= 0 ? true : false; }
        }
        public bool IsFull
        {
            get { return !IsEmpty; }
        }
    }

Let's add buttons with logic, binded to sopping cart? We have to remember, that the only way customer can press checkout is when he see our 3d model on the screen, so we'll have not only know, when the shopping cart is full, but also when he see whatever we want him to see. For this we'll use MultiBinding and some converter that tell us when viewport3d is visible.

<Button Margin="5" ToolTip="Click here to add your item to shopping basket" Click="onAdd" IsEnabled="{Binding Source={StaticResource shopBox}, Path=IsEmpty}">
  <Image Source="Images/add-to-basket.png"/>
</Button>
<Button Name="btnRemove" Margin="5" ToolTip="Click here to remove all items from shopping basket" Click="onRemove" IsEnabled="{Binding Source={StaticResource shopBox}, Path=IsFull}">
  <Image Source="Images/delete-from-basket.png"/>
</Button>
<Button Name="btnCheckout" Margin="5" ToolTip="Click here to check out" Click="onCheckout">
  <Image Source="Images/basket.png"/>
  <Button.IsEnabled>
    <MultiBinding Converter="{StaticResource widthToBoolConv}">
      <Binding Source="{StaticResource shopBox}" Path="IsFull"/>
      <Binding ElementName="myViewport3D" Path="Width"/>
      </MultiBinding>
    </Button.IsEnabled>
  </Button>
 
class WidthToBoolConverter : IMultiValueConverter
   {
 
       #region IMultiValueConverter Members
 
       public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
       {
           return ((double)values[1] == 0 ? false : true) & (bool)values[0];
       }

Another thing I don't like is grayed out buttons. It's not clear when I can and when I can not press it, if I have images. Let's fix it with styles

<Style TargetType="Button">
     <Style.Triggers>
       <Trigger Property="IsEnabled" Value="False">
         <Setter Property="Opacity" Value="0.5"/>
       </Trigger>
       <Trigger Property="IsMouseOver" Value="True">
         <Setter Property="Background" Value="White"/>
       </Trigger>
     </Style.Triggers>
   </Style>

Wow! now the customer can add and remove items and even checkout them. But, stop. We'll need to get some information regarding the customers. For example where to send and where to get payment. Let's make customer's class with all those information.

class CustomerInfo:INotifyPropertyChanged
    {
        #region properties
        private string firstName;
        public string FirstName
        {
            get { return firstName; }
            set { firstName = value; OnPropertyChanged("FirstName"); }
        }
 
        private string lastName;
        public string LastName
        {
            get { return lastName; }
            set { lastName = value; OnPropertyChanged("LastName"); }
        }
 
        private string email;
        public string Email
        {
            get { return email; }
            set { email = value; OnPropertyChanged("Email"); }
        }
 
 
        private DateTime? birthDay;
        public DateTime? BirthDay
        {
            get { return birthDay; }
            set { birthDay = value; OnPropertyChanged("BirthDay"); }
        }
 
        private string streetAddress;
        public string StreetAddress
        {
            get { return streetAddress; }
            set { streetAddress = value; OnPropertyChanged("StreetAddress"); }
        }
 
        private string city;
        public string City
        {
            get { return city; }
            set { city = value; OnPropertyChanged("City"); }
        }
 
        private Countries country;
        public Countries Country
        {
            get { return country; }
            set { country = value; OnPropertyChanged("Country"); }
        }
 
        private int? zip;
        public int? Zip
        {
            get { return zip; }
            set { zip = value; OnPropertyChanged("Zip"); }
        }
 
        private string creditCardNumer;
        public string CreditCardNumber
        {
            get { return creditCardNumer; }
            set { creditCardNumer = value; OnPropertyChanged("CreditCardNumber"); OnPropertyChanged("CreditCardType"); }
        }
 
        private DateTime? creditCardExp;
        public DateTime? CreditCardExp
        {
            get { return creditCardExp; }
            set { creditCardExp = value; OnPropertyChanged("CreditCardExp"); }
        }
 
        private CardType creditCardType = CardType.All;
        public CardType CreditCardType
        {
            get { return creditCardType; }
        }
        #endregion
 
 
 
        #region INotifyPropertyChanged Members
        void OnPropertyChanged(string name)
        { 
            if(PropertyChanged!=null)
                PropertyChanged(this,new PropertyChangedEventArgs(name));
        }
        public event PropertyChangedEventHandler PropertyChanged;
 
        #endregion
    }

How we need control to show and read it. I like Vista's approach of semi-texboxes. I want to know when my textbox has no text and render it different way. Setters, styles and ControlTemplated, what you think?

<Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type m:aTextBox}">
            <Grid Margin="0">
              <Border Name="background"
                              Background="{TemplateBinding Background}"
                              BorderBrush="{TemplateBinding BorderBrush}"
                              BorderThickness="{TemplateBinding BorderThickness}">
                <Decorator x:Name="PART_ContentHost" VerticalAlignment="Top" Margin="0"/>
              </Border>
              <Label Content="{TemplateBinding TextBoxInfo}"
                          x:Name="Message"
                          Foreground="Gray"
                          Opacity="0.9"
                          FontSize="{TemplateBinding FontSize}"
                          FontStyle="Italic"
                          HorizontalAlignment="Left"
                          VerticalAlignment="Top"
                          Margin="0"/>
 
            </Grid>
            <ControlTemplate.Triggers>
              <MultiTrigger>
                <MultiTrigger.Conditions>
                  <Condition Property="HasText" Value="False"/>
                  <Condition Property="IsFocused" Value="True"/>
                </MultiTrigger.Conditions>
              </MultiTrigger>
 
              <MultiTrigger>
                <MultiTrigger.Conditions>
                  <Condition Property="HasText" Value="False"/>
                  <Condition Property="IsFocused" Value="False"/>
                </MultiTrigger.Conditions>
                <Setter TargetName="background" Property="Visibility" Value="Hidden"/>
              </MultiTrigger>
 
              <Trigger Property="HasText" Value="True">
                <Setter TargetName="Message" Property="Visibility"  Value="Hidden"/>
              </Trigger>
 
              <Trigger Property="IsEnabled" Value="false">
                <Setter TargetName="background" Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
              </Trigger>
 
              <Trigger Property="Width" Value="Auto">
                <Setter Property="MinWidth" Value="100"/>
              </Trigger>
 
              <Trigger Property="Height" Value="Auto">
                <Setter Property="MinHeight" Value="20"/>
              </Trigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>
        </Setter.Value>
      </Setter>

 

See, doesn't it looks great?

 

Yes, this greate, but my country is Enum and I want it binded to Combobox. What to do? This what. We have class named ObjectDataProvider, that can get not only type of object, but it's method as well, so the binding will be really simple

<ObjectDataProvider MethodName="GetValues" ObjectType="{x:Type sys:Enum}" x:Key="ctrs">
      <ObjectDataProvider.MethodParameters>
        <x:Type TypeName="m:Countries"/>
      </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
<ComboBox ItemsSource="{Binding Source={StaticResource ctrs}}" IsSynchronizedWithCurrentItem="true"  />

Fine. Our happy use can choose whaever he want and even check out. Let's add some visual effects for controls' transition to make our costomer not just happy, but excited with new WPF technology. I like "scrolling animation". That means, that I have to add animation...

<Storyboard  x:Key="enterBuy">
      <ParallelTimeline>
        <DoubleAnimation
          Storyboard.TargetName="myViewport3D"
          Storyboard.TargetProperty="(Viewport3D.Width)"
          From="{Binding ElementName=myWindow, Path=ActualWidth}"
          To="0"
          AutoReverse="False"
          Duration="0:0:1" />
        <DoubleAnimation
          Storyboard.TargetName="myData"
          Storyboard.TargetProperty="(m:PersonalData.Width)"
          From="0"
          To="{Binding ElementName=myWindow, Path=ActualWidth}"
          AutoReverse="False"
          Duration="0:0:1" />
      </ParallelTimeline>
    </Storyboard>
    <Storyboard  x:Key="exitBuy">
      <ParallelTimeline>
        <DoubleAnimation
          Storyboard.TargetName="myData"
          Storyboard.TargetProperty="(m:PersonalData.Width)"
          From="{Binding ElementName=myWindow, Path=ActualWidth}"
          To="0"
          AutoReverse="False"
          Duration="0:0:1" />
        <DoubleAnimation
          Storyboard.TargetName="myViewport3D"
          Storyboard.TargetProperty="(Viewport3D.Width)"
          From="0"
          To="{Binding ElementName=myWindow, Path=ActualWidth}"
          AutoReverse="False"
          Duration="0:0:1" />
      </ParallelTimeline>
    </Storyboard>

...and when the animation occures

<Window.Triggers>
    <EventTrigger RoutedEvent="Button.Click" SourceName="btnCheckout">
      <EventTrigger.Actions>
        <BeginStoryboard Storyboard="{StaticResource enterBuy}"/>
      </EventTrigger.Actions>
    </EventTrigger>
    <EventTrigger RoutedEvent="Button.Click" SourceName="btnRemove">
      <EventTrigger.Actions>
        <BeginStoryboard Storyboard="{StaticResource exitBuy}"/>
      </EventTrigger.Actions>
    </EventTrigger>
  </Window.Triggers>

Pretty simple, don't you think? The only thing we should do is to connect it to our transactional SAP (why? someone knows for sure) web service (by using WCF, of couse) to fulfill the purchase, add the client's information to SAP CRM and send the items (by using SAP ERP) to our happy customer.

The bottom line. We build almost functional consumer application, that can run either as standalone Windows or as WPF/E control in browser. It has outstanding high-end UI, very functional and easy to use within only a couple of hours. Isn't is really great?

This article source code and executibles

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/crossbow-supported-scenarios/]


Very useful MSDN article about support and unsupported scenarios in WPF and Windows Forms Interop (Crossbow)

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/blogmycode-scrolling-anything/]


It seemed like people do not understand how to scroll any content. The answer is simple : ScrollViewer.

Just put anything inside it and scroll the word. Follow the example of something colorfull inside it.

 
<ScrollViewer CanContentScroll="True">
          <Grid Width="300" Height="800">
            <Grid.Background>
              <LinearGradientBrush>
                <GradientStop Color="#FFF" Offset="0.0"/>
                <GradientStop Color="#000" Offset="1.0"/>
              </LinearGradientBrush>
            </Grid.Background>
          </Grid>
</ScrollViewer>

You not have to move scrollers. You can just scroll the mouse wheel and everything inside this magic control will be scrolled.

There are some controls, that already have scrollers. The best useful example is TextBox. Just put any text inside it, set ScrollViewer.CanContentScroll="True" and here it come. Scrollers.

 

<TextBox TextWrapping="Wrap" ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.CanContentScroll="True" Width="600" Height="200">
      Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Vestibulum vestibulum felis tempor enim suscipit hendrerit. Integer a orci id lorem tristique varius. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Vestibulum fringilla interdum libero. Curabitur vitae metus. Nunc vel nisi sit amet tortor auctor consectetuer. Cras justo. Morbi accumsan quam in leo. In sem. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Quisque pharetra nibh ac ante. Quisque quis nunc. Vivamus justo.
 
      Morbi posuere nisl ut pede. In mattis justo eget mauris. Nam velit tortor, consequat in, porta et, accumsan vulputate, elit. Nunc id enim. Duis pretium sem. In augue purus, condimentum ac, bibendum eget, sodales at, orci. Nunc dapibus dictum tellus. Donec tempus bibendum ipsum. Nulla a libero. Nulla quis diam ut erat rhoncus congue.
 
      Sed quam. Proin imperdiet tempus mi. Cras tellus diam, accumsan a, commodo ac, sodales tincidunt, nisi. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Ut tincidunt dui sit amet ipsum. Suspendisse velit. Integer malesuada leo ac elit. Vivamus suscipit diam. Vivamus consequat lacinia erat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nunc velit tellus, interdum ut, commodo quis, malesuada nec, lectus. Nulla bibendum, est vel mollis dapibus, est ipsum ornare nisi, sed iaculis sapien velit eget ante. Donec ac sem. Duis lobortis metus a diam. Fusce velit eros, nonummy id, cursus et, tristique tincidunt, nisl. Ut porttitor dui sed felis.
 
      Donec blandit mattis neque. Phasellus pretium dapibus lectus. Aenean sapien. Nulla ultricies ipsum quis leo. Proin fringilla. Aliquam erat volutpat. Maecenas sit amet felis. Integer porta scelerisque tortor. Aenean imperdiet viverra purus. Aliquam metus ante, nonummy id, placerat eu, tempor et, nunc. Sed consequat. Pellentesque quis ante sit amet augue rhoncus mollis.
 
      Fusce nisi ligula, ultrices id, vehicula et, gravida in, nibh. Vivamus sagittis quam sed sapien. Phasellus tellus. Nam convallis, est id tincidunt tincidunt, lacus augue blandit risus, pretium tempus turpis erat sed elit. Quisque accumsan mollis nibh. Duis vulputate. Maecenas in nulla a ligula iaculis pellentesque. Vestibulum a arcu et enim sollicitudin auctor. Vivamus ut urna ut nisl posuere iaculis. Cras vitae erat nec tellus egestas tempus. Sed est. Donec venenatis imperdiet risus. Maecenas nulla magna, hendrerit a, rutrum quis, feugiat id, nunc. Aenean consectetuer lobortis sapien. Nullam lectus. Integer quis dolor in felis egestas cursus. Ut vitae justo eget velit feugiat porta. Aenean nonummy, purus ac facilisis dignissim, urna nisi scelerisque lectus, ut ullamcorper sapien orci sed purus. Integer a ligula.
    </TextBox>

But the smartest and really amazing thing that you have not do anything in order to make the ScrollViewer understand, that he have to scroll text inside it. While you select it and drag the mouse down, scrolles will scroll. Not only with buildin TextBox ScrollViewer, but with "hand-made" anlernative. Do this.

 

<ScrollViewer CanContentScroll="True">
  <TextBox TextWrapping="Wrap"  Width="300" Height="800">
    Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Vestibulum vestibulum felis tempor enim suscipit hendrerit. Integer a orci id lorem tristique varius. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Vestibulum fringilla interdum libero. Curabitur vitae metus. Nunc vel nisi sit amet tortor auctor consectetuer. Cras justo. Morbi accumsan quam in leo. In sem. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Quisque pharetra nibh ac ante. Quisque quis nunc. Vivamus justo.
 
    Morbi posuere nisl ut pede. In mattis justo eget mauris. Nam velit tortor, consequat in, porta et, accumsan vulputate, elit. Nunc id enim. Duis pretium sem. In augue purus, condimentum ac, bibendum eget, sodales at, orci. Nunc dapibus dictum tellus. Donec tempus bibendum ipsum. Nulla a libero. Nulla quis diam ut erat rhoncus congue.
 
    Sed quam. Proin imperdiet tempus mi. Cras tellus diam, accumsan a, commodo ac, sodales tincidunt, nisi. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Ut tincidunt dui sit amet ipsum. Suspendisse velit. Integer malesuada leo ac elit. Vivamus suscipit diam. Vivamus consequat lacinia erat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nunc velit tellus, interdum ut, commodo quis, malesuada nec, lectus. Nulla bibendum, est vel mollis dapibus, est ipsum ornare nisi, sed iaculis sapien velit eget ante. Donec ac sem. Duis lobortis metus a diam. Fusce velit eros, nonummy id, cursus et, tristique tincidunt, nisl. Ut porttitor dui sed felis.
 
    Donec blandit mattis neque. Phasellus pretium dapibus lectus. Aenean sapien. Nulla ultricies ipsum quis leo. Proin fringilla. Aliquam erat volutpat. Maecenas sit amet felis. Integer porta scelerisque tortor. Aenean imperdiet viverra purus. Aliquam metus ante, nonummy id, placerat eu, tempor et, nunc. Sed consequat. Pellentesque quis ante sit amet augue rhoncus mollis.
 
    Fusce nisi ligula, ultrices id, vehicula et, gravida in, nibh. Vivamus sagittis quam sed sapien. Phasellus tellus. Nam convallis, est id tincidunt tincidunt, lacus augue blandit risus, pretium tempus turpis erat sed elit. Quisque accumsan mollis nibh. Duis vulputate. Maecenas in nulla a ligula iaculis pellentesque. Vestibulum a arcu et enim sollicitudin auctor. Vivamus ut urna ut nisl posuere iaculis. Cras vitae erat nec tellus egestas tempus. Sed est. Donec venenatis imperdiet risus. Maecenas nulla magna, hendrerit a, rutrum quis, feugiat id, nunc. Aenean consectetuer lobortis sapien. Nullam lectus. Integer quis dolor in felis egestas cursus. Ut vitae justo eget velit feugiat porta. Aenean nonummy, purus ac facilisis dignissim, urna nisi scelerisque lectus, ut ullamcorper sapien orci sed purus. Integer a ligula.
  </TextBox>
</ScrollViewer>

Ici il vient, we scoll the world!

Source Sample

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/ballmer-got-excitement/]


Nasdaq opening via mikewalker