Monday, May 26, 2008

How to protect your Intellectual Property or some words about next generation obfuscation

Not once customers asking me about protecting their IP (not address). The regular answer was “obfuscation”. You want more, then this? Use third party unmanaged hash providers (such as RemoteSoft, which is the best so far). However it not enough. the best we can do is to provide sensitive code as remote service. The only way to “hack” such protection is by hacking remote server, which is much more complex mission, in comparison to hacking managed or even native code.

image
© Don Farrall

For a long time Microsoft has no ultimate answer to IP issues of managed code. Sun experienced the same problems with Java. what to do? The answer is to provide comprehensive solution for server side code encoding. This is exactly what was done in Microsoft Software Licensing and Protection Services. This is not free, however it can provide you with comprehensive solution for IP protection, licence management and secure software distribution.

After applying to this service, your code will looks like this

return SLMRuntime.SVMesecMethod(a);

Where SVMesecMethod is server side method, rather then

return MyEncriptor.DecodeString(a);
string DecodeString(string a) {
for ( long offset = 0; offset < a.Length ; offset++ )
                {
                    i = ( i + 1 ) % a.Length ;
                    j = ( j + a[i] ) %  a.Length ;
                    byte temp =  a[i];
                    a[i] = a[j];
                    a[j] = temp;
                    byte a = a[offset];
                    byte b = a[(a[i]+a[j])% a.Length ];
                    a[offset] = (byte)((int)a^(int)b);   
                }   
}

Where DecodeString is maybe complicated (this code actually does not work), but easy reflected source.

SLP service is not new approach. It widely used by different companies to protect their software. Also it’s very common way to distribute Smart Client applications. However this is first time Microsoft provides such service widely for ISVs.

You can request evaluation of this service today, by visiting SLP website.

Quick Silverlight tip: How to open new window or tab by using HyperlinkButton and Ctrl-Shift modifiers

Regular behavior of any HTML anchor element is to open new Window if you’re clicking the link with Shift key and new Tab, if you’re clicking with Ctrl key or middle mouse button. Unfortunately, Silverlight HyperlinkButton does not support opening new window or tab. It also does not support key modifiers. What to do?

image
(rotary reading desk – first tabbed browser, invented in 1588. via Athanasius Kircher society)

First of all, request this feature. This is regular behavior and there is no reason not to support it in Silverlight. Next, we should do something to fix it by now. So let’s start.

We should understand what happens under the hoods of HyperlinkButton. It does not generate html anchor, however it uses window.navigate to move. One of HyperlinkButton properties is TargetName – this one is actually html ”target” attribute. So we can just change it according the keystrokes. This is acceptable, but ugly solution. The better one is to subclass HyperlinkButton and check KeyModifiers. Then void Click event and navigate

if(Keyboard.Modifiers == ModifierKeys.Control | Keyboard.Modifiers == ModifierKeys.Shift)
            {
                this.TargetName=”_blank”;
            }
            base.OnClick();

So far so good, but not good enough. What will happen if user explicitly set TargetName Value? We’ll override it and this is something we wont to do. Also, following approach will work differently between browsers. For example in IE it will open new window, while in FF – new tab by default. So how to avoid it?

We can use HtmlPage.Window.Navigate method to assure opening of new window. Navigate method receives three parameter, Uri for navigation address, target frame and “target features”. What are those target features? Actually, it parameters we’re using in JavaScript for window.open method. Thus HtmlPage.Window.Navigate(new Uri(“http://blogs.microsoft.co.il/blogs/tamir”,null,”menubar=1,resizable=1,width=350,height=250”) will open resizable window with menubar and size of 350x250.

Very well. Now we almost finished, but how to assure, that I opened new tab? Actually, we cannot until all browsers will support CSS3. In CSS3 we have style attributes, which can target into tab or window. By now, the only workaround is to use IWebBrowser2::Navigate2 method for IE and pass 0x1000 (navOpenInBackgroundTab) as second parameter. This “heck” can only be done in Full trust mode. For Mozilla (FireFox or any Geco + ancestors) we should use nsIBrowserWindow::OPEN_NEWTAB override for nsIBrowserWindow::OPEN_NEWWINDOW. There is no known way to do it for Safari.

So, the best thing can be done is inheritance of build-in feature of any supported browser to handle Ctrl/Shift modifiers for force opening links in other tabs or new windows.

Have a nice week and be good people.

Friday, May 23, 2008

Brightness and contrast manipulation in WPF 3.5 SP1

While being in flight, I had to learn new features, introduced in .NET 3.5 SP1. So, let’s start from image manipulation. I want to perform contrast and brightness manipulation in GPU over displayed image. In order to begin, you should download and install .NET 3.5 SP1 and Visual Studio 2008 SP1. Meanwhile (it’s about 500 MB of download) we’ll learn how to write custom shader effect.

image

In order to build custom Shader Effer, we have to use HLSL (High Level Shading Language). This is programming language, introduces in DirectX 9.0 and supports the shader construction with C-like syntax, types, expressions and functions. If you know C – it will be very easy for you to learn it.

What is shader? Shader is consists of vertex shader and pixel shader. Any 3D model flows from application to the vertex shader, then pixel shader frames buffer. So we’ll try from simple matrix transformation. First we should build the struct of the position. It is float4 type and has POSITION inheritance. Also we have to get matrix, which is regular float4x4 object. Then all we have to to is to translate inpos by matrix and return new position. That’s exactly what following code does.

float4 main(float4 inpos : POSITION, uniform float4x4 ModelViewMatrix) : POSITION
  {
     return mul(inpos, ModelViewMatrix);
  }

So by using HLSL we can play freely with vertexes, but what’s happen with pixel shader? This works exactly the same way. We have pixel, which is TEXCOORD in input and COLOR in output. So, here it comes

float4 main(float2 uv : TEXCOORD, float brightness, float contrast) : COLOR
{
    float4 color = tex2D(input, uv); 
    return (color + brightness) * (1.0+contrast)/1.0;
}

For more information about HLSL, please visit MSDN. As for us, we already have our shader effect and how we have to compile it into executable filter. In order to do it, we’ll use directx shader effect compiler. Let’s say, that we have our source in effect.txt file and our output file will be effect.ps. Small tip insert following line into pre-build event, and have your shader effect script ready and up-to-day with each compilation.

fxc /T ps_2_0 /E main /Fo"$(ProjectDir)effect.ps" "$(ProjectDir)effect.txt"

Mode information about FX compiler command switches, can be found here. How we should actually wrap our effect in manage code. But wait. We have to pass parameters into shader effect. How to register external parameters within FX file? Simple. Exactly as input params. Note, the tag inside register method will actually be used within our managed wrapper.

sampler2D input : register(s0);
float brightness : register(c0);
float contrast : register(c1);

float4 main(float2 uv : TEXCOORD) : COLOR
{
    float4 color = tex2D(input, uv);
    float4 result = color;
    result = color + brightness;
    result = result * (1.0+contrast)/1.0;
    return result;
}

Well done.  Let’s build wrapper. Of cause you should inherit from ShaderEffect object and register your input param

public class BrightContrastEffect : ShaderEffect
    {

public Brush Input
        {
            get { return (Brush)GetValue(InputProperty); }
            set { SetValue(InputProperty, value); }
        }

        public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(BrightContrastEffect), 0);

Then load pixel shader from application resources (you should compile ps file as “Resource”)

private static PixelShader m_shader = new PixelShader() { UriSource = new Uri(@"pack://application:,,,/CustomPixelRender;component/bricon.ps") };

Then parameters (they are regular dependency objects) with additional special PixelShaderConstantCallback, that received the numeric id of registered properties from pixel shader effect.

public float Brightness
        {
            get { return (float)GetValue(BrightnessProperty); }
            set { SetValue(BrightnessProperty, value); }
        }

        public static readonly DependencyProperty BrightnessProperty = DependencyProperty.Register("Brightness", typeof(double), typeof(BrightContrastEffect), new UIPropertyMetadata(0.0, PixelShaderConstantCallback(0)));

        public float Contrast
        {
            get { return (float)GetValue(ContrastProperty); }
            set { SetValue(ContrastProperty, value); }
        }

A couple of updates and we done with code behind.

public BrightContrastEffect()
        {
            PixelShader = m_shader;
            UpdateShaderValue(InputProperty);
            UpdateShaderValue(BrightnessProperty);
            UpdateShaderValue(ContrastProperty);

        }

Next step is XAML. Each UI element in .NET 3.5 SP1 got new property, named Effect, that designed to hold your custom shader effects (exactly as it was with transformations in 3.0 and 3.5). I want to perform a transformation over image.

<Image Source="img.jpg">
           <Image.Effect>
               <l:BrightContrastEffect

Now we should build two sliders to manage brightness and contrast level

<UniformGrid Grid.Row="1">
           <TextBlock Text="Brightness"/>
           <Slider Maximum="1" Minimum="-1" Name="bVal"/>
           <TextBlock Text="Contrast"/>
           <Slider Maximum="1" Minimum="-1" Name="cVal"/>
       </UniformGrid>

And bind to its values from our pixel shader effect

<Image Source="img.jpg">
            <Image.Effect>
                <l:BrightContrastEffect
                    Brightness="{Binding ElementName=bVal, Path=Value}"
                    Contrast="{Binding ElementName=cVal, Path=Value}"/>
            </Image.Effect>
        </Image>

That’s all, folks. Please note, that everything, done with shader effects, done in GPU. Also, the effect applies on rendered object (you can set the same effect not only to image, but to any UIElement in your system. Thus from performance point of view it’s the best method to work with your output. Let’s take for example very big image (3000x3000 pixels), rendered with low quality to 300x300 size. Perform per-pixel transformation (what we done here) will take 300X300Xdpi loops. While if you’ll perform the same operating over source image or memory section, used to create it, you’ll have to do 3000x3000xdpi loops, which is x10^2 more times.

Have a nice day and be good people.

Source code for this article.

Thursday, May 22, 2008

Action required: Smart client users group

Recently I browsed INETA to seek for some group and was really surprising. There is no Smart Client user group registered there. Maybe there is a reason? Let’s understand what Smart Client is?

According wikipedia, the term "Smart Client" is meant to refer to simultaneously capturing the benefits of a "thin client" (zero-install, auto-update) and a "fat client" (high performance, high productivity). However, I think, that this term is much wider. It is not only thin-fat client application, it’s also most of applications we’re using today.

image

Thick Client

We always want to provide our users with best experience and increase their performance. However we are (as developers) want to avoid complicated development and deployment. This why we should know as much as possible about user’s system, when users do not want to have real footprint in their systems. That’s dilemma. Is it possible to solve it? Let’s look deeper…

Are we really need installation? Most of old software installations put things in registry. It because you were never sure what client has in his system and were our application can put files or temporary data. Today, when we have local application or user isolated storage, so we not really need to use registry. Maybe only for our own ego – this is cool to have something like “HKLM/Software/MYNAME” in 1,000,000 user’s computers…

No installation is good, but what to me with maintenance. We want our system connected…

Thin Client

Could you imagine your user to visit product site twice a week to see what’s going on? I can not. However I know, that if I’ll ask him first about automatic updates and he’ll agree to forget about application maintenance, his experience will be much better.

So,we are connected. What now? I want to make time reporting system. Web service? Maybe some kind of distributed application. Maybe, even Twitter? This way we can be sure, that our data is safe and if user reinstall whole system, he do not really need to care about backups.

But users not always have internet access. Sometimes they are offline. How to solve the problem of occasionally connected users? I do not want him every lunch want for two minutes, until I realize, that there is no internet connection and will not even give him a chance to use the application?

image

So, we also want our system to be useful offline. But what’s up with Web 10.0? We want millions. We want very broad reach for our application. Also we want to be able to manage application updates remotely?

Let’s take a look into other pan of application development. Do you like JavaScript? I do not! I think it’s too complicated to develop things for web. We should invest into at least 50% of coverage and integration tests, while giving customers pretty poor user experience. What is we want to provide the same look and feel everywhere? In web, desktop, mobile and other devices? Our customers want the application everywhere?

Summery

This is exactly what Smart client designed for. Technologies such as .NET, WPF, Silverlight from Microsoft, Flex, Thermo from Adobe and others tries to make you to be there with your application. But how to do it? How to answer all those hard questions, I asked?

I want to announce new (currently virtual) user group, dedicated to Smart Client development. I do not want to restrict this group geographically, due to fact, that current infrastructures allows us to forget about distances and be together. Join today “Smart Client development” user group.

In order to join, just send me an email to tamir [at] khason.biz with information about you. I put the request to create this user group in INETA. Once it will be opened, I’ll send everyone email to register and connect them selves to this group.

Be in touch.

Wednesday, May 21, 2008

New Microsoft R&D center in Israel

Just come back from new Microsoft R&D center house warming. Got some photos and  (how not?) some comments. Steve Ballmer (CEO of Microsoft), Shimon Peres (the President of Israel), Moshe Lichtman (local R&D center corporate VP) and Danny Yamin (local Microsoft subsidiary GM) were here.

The event begun from short introduction of Moshe and Steve about the importance of local R&D and it’s size relative to country (we’re second place in the world with 0.015% of Microsofties per total country population). Yu-hu we’re on map!

DSC08454

Then journalists begun to ask questions. Of cause the first question was Yahoo or not and the answer was “not”

DSC08446

Then, there were two Hebrew localization questions (one about XBOX [just in case Steve saw the money there] and another about Windows Mobile). Both remains actually unanswered with doubtful remark of Danny (Yamin) about timelines of some day in the future. I heard the same answer before. About a year ago, but in practice nothing happed yet.

Side note: Silverlight will not support RTL neither in this nor in next version.

I asked Steve about legacy code of Microsoft. Actually, I, personally, think, that walking dead MSN, crappy Explore.exe (Windows shell) and “always-single-user-ever” Office is bad legacy, that prevent Microsoft from being really responsive and innovative company. He (Ballmer) wants to beat Web? He just have to kill MSN and do it.

However, his response was, that Microsoft loves its legacy and this why Microsoft so powerful. I agree with him, but Microsoft is strong in user’s desktop, not in the web. So with this agenda has not many chances to win there. One day they’ll see themselves hugging with Office COM objects, when the industry movers uses online rich client alternatives.

I believe, that Moshe (Lichtman). Got the point, thus he has absolutely Microsoft independent research center, that looking for innovative not through legacy prism. However, who knows…

DSC08449

Then we moved to central event with Shimon (Peres), Steve (Ballmer) and others. Another short “what was done” by Moshe and we moved toward Shimon.

DSC08478

This was the most impressive speech all over the event. He was able to switch (as always) people attention to “peace all over the world” instead of “thanks Microsoft for investment in Israel”.

DSC08495

Also, he found him young girl (Herzeliya Mayor, Yael German) to kiss while Danny envying him. The big brother is watching? Probably, let’s see what happened later…

DSC08501

Later, Steve told, that he does not like and does not understand technology innovations, thus he’s using regular yellow paper to take notes (BTW, my question)

DSC08518

After his speech the real action begun. It become clear, that Moshe saw Shimon kissing Yael. So he wish Steve to kiss him, but Ballmer did not want to do it, while Danny tightly spying them together with all other kissing people.

DSC08526

Thus, he asked Shimon to do it. When Steve recognized his fault, he was amused to Moshe. “Can't you see. It all makes perfect sense. Expressed in dollars and cents. Pounds, shillings, and pence”.

DSC08528

After all UFO come and took all celebrities out of the event.

DSC08530

The last part of the event was the Buffet. All attendees were really happy and enjoyed.

DSC08541

At the end staff begun to remove all decorations and everyone dissipated back to their homes.

DSC08545

Have a nice day and be good people.

Tuesday, May 20, 2008

What’s new in MSDN Downloads?

Just look how many new releases those days in MSDN Download web site.

Too much – too cool. Turn your download managers on

image

Have a nice day

Friday, May 16, 2008

Gas Price Windows Vista SideBar gadget – new version is available for download

This post is dedicated to some people in Microsoft and it’s subsidiaries. It begun about year ago, when I developed Gas Price information gadget (do not download it there). Before I started, I sent some personal email to those in MSN, who maintains it’s Auto section. I wait two days and got no response, so wrote this post about HTML scrapping and then I finished the gadget, that uses this technology.

image
© Christopher Robbins

Couple of weeks later, Senior Channel Manager of MSN Marketplace replayed to me. He asked whether I want to convert this gadget to “legal” one by gifting all rights to Microsoft. I asked about my benefits of doing it (my time costs money) and the conversation ended – he even did not responded. I was waiting for “YES” or “NO”, but got only silent.

Year after he mailed me again with warning, that they going to “protect” Auto section in order to prevent unauthorized content grabbing. He asked again about possibility to “legalize” the gadget – I told, that they can do with this gadget whatever they want, so handed it off to MSN team. Nothing happened. No one took care on this.

A month later, I asked again by proposing to allow Windows Vista SideBar referrer too aside with affiliate sites for MSN Auto images, thus the gadget can continue to work and MSN remains protected from other “grabbers”. But he demand to completely remove any reference to MSN from the gadget. The same time I got some proposals of using another data for this very popular gadget and populize other resources instead of very unpopular crappy MSN.

I decided to build new version of the gadget (here you can download) and did it today (my spare time – not work [this is for my manager]). This version even better, then previous one. It contains more information, that updates more frequently. I also includes distance from station and gas stations in Canada. So, this how it looks today

image

As you can see this one is much better and uses Automotive.com information. So what I have to do? Submit it instead of old one, right? This the response, I got from automatic system upon submission.

Your item appears to be either missing a valid signature or a valid certificate. You may also want to check the signature to make sure that it includes the date

Just to make things clear, I signed the code with private signature. They want me to sign it with Trusted Authority. This is very smart request, however I do not want to pay $200-$400 to make their sidebar better! There is neither ROI, nor benefit for me to pay money for something, I’m giving for free to anyone.

Just in case, signing code with certificate, trusted by authority even do not removes regular live gallery end-user warning.

Unverified submission.

Only install applications from developers you trust. This is a third-party application, and it could access your computer's files, show you objectionable content, or change its behavior at any time.

So why me to pay? Only because I want to be nice to Microsoft and replace my old gadget by new one to serve dozen thousands of people, who using Windows Vista with SideBar and my gadget?

NO WAY! I will not submit it there. I will never contribute anything for free to Windows Vista Live Gallery. They want me (and million of other developers) to submit it to Google or Yahoo? I’ll do it! I’ll force my customers to use 3rd party addons and visit 3rd party websites to get the information they want to get without paying anyone. At least their marketing guys know how to make developer not to suffer from his own good wish.

Thank you and good buy! You want to win web? You just impossible to do it.

Download Gas Price gadget for Windows Vista SideBar >> (it signed with personal certificate, so do it for your own risk :) )

P.S. Next week, I have a meeting with Steve Ballmer and I’m going to ask him all those questions. If you have any questions and want me to ask him, please send it to me or leave a comment.

Tuesday, May 13, 2008

How to AddRange/RemoveRange in Silverlight ObservableCollection<T>?

Someone in Silverlight forum asked for interesting question: “Is there any way that I can add/remove items in bulk from an ObservableCollection object?”. The “formal” answer is: “No, AddRange RemoveRange operators are supported for List<T> only collections, thus each time you want to add or remove items from ObservableCollection, you should iterate through all items, thus CollectionChanged event will be fired each time you add or remove anything”. By the way, we have the same problem with WPF ObservableCollection<T>

Is there way to fix it? Yes, it is. However, not sure, that it is very efficient method.

image

We can subclass ObservableCollection and defer OnCollectionChanged method from firing Collectionchanged event for each add or remove items. Here how to do it

First of all create our own class, that inherits ObservableCollection<T>

public class BulkObservableCollection<T>:ObservableCollection<T>
    {

then create two methods and one member for bulk update. At the end of the update we should "tell” our collection, that it dramatically changed, thus we’ll class OnCollectionChanged with NotifyCollectionChangedAction.Reset argument.

bool deferNotification = false;
        public void AddRange(IEnumerable<T> collection)
        {
            deferNotification = true;
            foreach (T itm in collection)
            {
                this.Add(itm);
            }
            deferNotification = false;
            OnCollectionChanged(new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Reset));
        }

        public void RemoveRange(IEnumerable<T> collection)
        {
            deferNotification = true;
            foreach (T itm in collection)
            {
               this.Remove(itm);
            }
            deferNotification = false;
            OnCollectionChanged(new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Reset));
        }

Now, the only thing to do is to override OnCollectionChanged method to involve deferNotification flag

protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
       {
           if (!deferNotification)
           {
               base.OnCollectionChanged(e);
           }
       }

We done, so for 500 items bulk update this method works very fast, however what’s happen with 1000 items?

image

Not so good. When collection Dramatically changed renderring engine regenerates all items in bounded controls. Thus it will be almost the same time as for regular one-by-one method.

Things even worth with more, then 2000 items. If we already have 1500 items in our collection and adding another 500 items, we’ll regenerate all 2000 items by calling OnCollectionchnaged with Reset params. So it twice slower, then adding items one-by-one.

image

What to do? Work smart – add bulks when requires and single items when the collection is big. Have a nice day and be good people.

Source code for this article

Monday, May 12, 2008

Search and highlight any text on WPF rendered page

Today we’ll speak about how to search and select text on WPF page. This is not about how to search data sources, or how to search for data. This is visual search. Like this one

image

Let’s see how XAML looks like

<Grid Name="root">

<StackPanel Grid.ColumnSpan="2" Grid.Row="1" Name="panel">
            <TextBlock Name="tb" Text="Lorem ipsum dolor

<RichTextBox>
                <FlowDocument>
                    <Paragraph>
                        Lorem ipsum dolor

<ContentControl >
                <ContentControl.ContentTemplate>
                    <DataTemplate>
                        <TextBlock>
              <Run Text="Lorem ipsum

<ContentControl Content="{Binding Path=Lorem}"/>
            <DocumentPageView DocumentViewerBase.IsMasterPage="True" Grid.Row="1" Grid.ColumnSpan="2" Name="viewer"/>
        </StackPanel>

As you can see it’s various controls. Some with hard coded text in it, some with content, some with binding and some, even, with Fixed or Flow documents, loaded from external source. So how to search for some text all over the WPF application?

First attempt: Reflection and AttachedProperties

My first attempt was to use attached properties. It looks like very good way to provide such functionality. I can “attach” my property to those controls, I want to search in and then, just test and compare string of well-known control in well-known property. For example if I want to search inside Text property of TextBox, I’ll use following syntax:

<TextBlock Name="tb" l:TextualSearch.IsEnabled="True" l:TextualSearch.SearchPath="Text" Text="Lorem ipsum d

Then in code-behind, I can test if it’s dependency or CLR property. We can use it, by using DependencyPropertyDescriptor

FrameworkElement fe = o as FrameworkElement;
            if (fe != null && searchTargets.ContainsKey(fe))
            {
                Type tt = fe.GetType();
                string pn = e.NewValue.ToString();
                DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromName(pn, tt, tt);
                //this is Dependency property
                if (dpd != null)
                {
                    searchTargets[fe] = dpd.DependencyProperty;
                }
                //this is CRL property
                else
                {
                    searchTargets[fe] = tt.GetProperties(BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance).SingleOrDefault(p => p.Name == pn);
                }
            }

After we have all sources and all targets, we can attach to Text changed event externally

TextBox tb = o as TextBox;
            if (tb != null)
            {
                if (!searchSources.Contains(tb) && ((bool)e.NewValue))
                {
                    tb.TextChanged += OnSourceTextChanged;
                    searchSources.Add(tb);
                }
                else if (searchSources.Contains(tb) && !((bool)e.NewValue))
                {
                    tb.TextChanged -= OnSourceTextChanged;
                    searchSources.Remove(tb);
                }
            }

And search

ICollection<FoundItem> results = new List<FoundItem>();
            foreach (KeyValuePair<FrameworkElement, object> o in searchTargets)
            {
                object tso = null;
                if (o.Value is DependencyProperty)
                {
                    tso = o.Key.GetValue((DependencyProperty)o.Value);
                }
                else if(o.Value is PropertyInfo)
                {
                    tso = ((PropertyInfo)o.Value).GetValue(o.Key,null);
                }
                if (tso is string && tso.ToString().Contains(text))
                {
                    //got it!
                    FoundItem fe = new FoundItem(o.Key);
                    Rect cb = VisualTreeHelper.GetContentBounds(o.Key);
                    results.Add(fe);
                }
                else
                {
                    //TODO: What can it be? FlowDocument, FixedDocument? Handle it!
                }

But this is not very nice method and it have a lot of problems. For example, how I know what the coordinate of text I found. How to select it? How to treat all possible types of controls? We should try another way

Second attempt: Glyphs and Visuals

If you look into VisualTreeHelper, you’ll see GetDrawing method. It returns actual drawing, processed by WPF rendering engine. So, what WPF doing with text? Make it be fixed by using GlyphRuns inside GlyphRunVisual. So we can seek for all GlyphRuns in our application, enumerate it and search inside Characters array of the glyph to compare to required string. This methods looks much better, then the previous one. Let’s get all element in our application. In order to do it, we should enumerate all visuals in visual tree. Simple recursive method bring us flat list of all DependencyObjects in our visual tree

static void FillVisuals(DependencyObject current, ref List<DependencyObject> objects)
        {
            objects.Add(current);
            int vcc = VisualTreeHelper.GetChildrenCount(current);

            for (int i = 0; i < vcc; ++i)
            {
                DependencyObject vc = VisualTreeHelper.GetChild(current, i);
                FillVisuals(vc, ref objects);
            }
        }

Next, we have to get all Drawings and seek inside it for all GlyphRunDrawings

static List<GlyphRunVisual> GetAllGlyphsImp(FrameworkElement root)
        {
            List<GlyphRunVisual> glyphs = new List<GlyphRunVisual>();

            List<DependencyObject> objects = new List<DependencyObject>();
            FillVisuals(root, ref objects);

            for (int i = 0; i < objects.Count; i++)
            {
                DrawingGroup dg = VisualTreeHelper.GetDrawing((Visual)objects[i]);
                if (dg != null)
                {
                    for (int j = 0; j < dg.Children.Count(); j++)
                    {
                        if (dg.Children[j] is DrawingGroup)
                        {
                            DrawingGroup idg = dg.Children[j] as DrawingGroup;
                            if (idg!= null)
                            {
                                for (int k = 0; k < idg.Children.Count(); k++)
                                {
                                    if (idg.Children[k] is GlyphRunDrawing)
                                    {

                                        glyphs.Add(new GlyphRunVisual((idg.Children[k] as GlyphRunDrawing).GlyphRun, (Visual)objects[i], (idg.Children[k] as GlyphRunDrawing).Bounds));
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return glyphs;
        }

Now we have list of all Glyph runs together with their Drawings and Bounds. Actually, this is all we need in order to search and select text. How to do it? Simple. First get all chars of required string, then compare it with GlyphRun.Characters array to figure whether the required characters are exist in GlyphRun. After it, just build rectangle of found sequence and return it

public static List<Rect> SelectText(this List<GlyphRunVisual> glyphs, string text)
        {
            if (glyphs == null)
                return null;
            List<Rect> rects = new List<Rect>();
            char[] chars = text.ToCharArray();
            for (int i = 0; i < glyphs.Count; i++)
            {
                int offset = 0;
                for (int c = offset; c < glyphs[i].GlyphRun.Characters.Count - offset - chars.Length; c++)
                {
                    bool wasfound = true;
                    double width = 0;
                    CharacterHit ch = new CharacterHit();
                    for (int cc = 0; cc < chars.Length; cc++)
                    {
                        wasfound &= glyphs[i].GlyphRun.Characters[c + cc] == chars[cc];
                        width += glyphs[i].GlyphRun.AdvanceWidths[c + cc];
                        if(cc==0)
                            ch = new CharacterHit(c+cc,chars.Length);

                    }
                    if (wasfound)
                    {

                        Rect ab = glyphs[i].Bounds;
                        Rect box = new Rect(
                            glyphs[i].Visual.PointToScreen(new Point(glyphs[i].GlyphRun.GetDistanceFromCaretCharacterHit(ch), 0)),
                            new Size(ab.Width, ab.Height)
                            );

                        box.Width = width;
                        rects.Add(box);
                    }
                    offset++;
                }
            }
            return rects;
        }

How, we have everything we need to select, so let’s create adorners to highlight found sequences

public class HighLightAdorner : Adorner
    {
        Brush b;
        Pen p;
        public HighLightAdorner(UIElement parent, Rect bounds) : base(parent) {
            b = new SolidColorBrush(Colors.Yellow);
            b.Opacity = .7;
            p = new Pen(b, 1);
            b.Freeze();
            p.Freeze();
            Bounds = bounds;
        }

        public Rect Bounds
        {
            get { return (Rect)GetValue(BoundsProperty); }
            set { SetValue(BoundsProperty, value); }
        }
        public static readonly DependencyProperty BoundsProperty =
            DependencyProperty.Register("Bounds", typeof(Rect), typeof(HighLightAdorner), new UIPropertyMetadata(default(Rect)));

        protected override void OnRender(DrawingContext drawingContext)
        {
            drawingContext.DrawRectangle(b, p, Bounds);
        }
    }

And draw them on root panel

public static void DrawAdorners(this AdornerLayer al, UIElement parent, List<Rect> rects)
        {
            Adorner[] ads = al.GetAdorners(parent);
            if (ads != null)
            {
                for (int i = 0; i < ads.Length; i++)
                {
                    al.Remove(ads[i]);
                }
            }

            if (rects != null)
            {
                for (int i = 0; i < rects.Count; i++)
                {
                    Rect rect = new Rect(parent.PointFromScreen(rects[i].TopLeft), parent.PointFromScreen(rects[i].BottomRight));
                    al.Add(new HighLightAdorner(parent, rect));
                }
            }
        }

We done. Happy coding and be good people.

Source code for this article

Tuesday, May 06, 2008

DrawingBrush and deep clone in Silverlight

Today, we’ll say “They did not put it there” as lot. And start with DrawingBrush. Yes, there is no Drawing Brush in Silverlight, thus you cannot create neither hatch brush nor pattern brush in Silverlight. But we want it to be there. What to do? To enter into deep reflection

image

first thing to do is to look into Reflector. How they did another brushes… What’s the mess?

DependencyProperty.RegisterCoreProperty(0x5ef4, typeof(double));

Now very helpful. At least we know that we have TileBrush (one helpful property – Tile). What’s next? Let’s try to understand how DrawingBrush should work. Actually, it should draw our control on other surface. We cannot do this – drawing in Silverlight uses internal unmanaged methods from core dll. But we can try copy actual controls. what’s the problem? Let’s do it.

First of all, we should get the content of our UserControl – WPF, Content property of UserControl is internal! Why? “They did not put it there” (second time) Also we do not know externally when all UIElement loaded. Why? you know, ‘cos no Loaded accessible externally. “They did not put it there”. I do not want to write every time the same code, so the only way to get the content is to seek by name. But we do not know what the name of controls! Reflection! We’ll get all fields of our root control and then look for every panel

How to get and set Content property of Page (UserControl) externally.

FrameworkElement root = Application.Current.RootVisual as FrameworkElement;


FieldInfo[] fields = root.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
            for (int i = 0; i < fields.Length; i++)
            {
                if (fields[i].FieldType.IsSubclassOf(typeof(Panel)))
                {
                    Panel p = fields[i].GetValue(root) as Panel;
                    SetPattern(p);
                }
            }

Well, now we can get the panel and just replace Background property (the usual place of brushes) and Children property (to add actual controls inside the panel

if (p.Background == this)
            {
                p.Background = null;

                SetPatternImpl(p.ActualWidth, p.ActualHeight);

                p.Children.Insert(0, Pattern);
            }
            else if (this.Pattern!=null && p.Children.Contains(this.Pattern))
            {
                SetPatternImpl(p.ActualWidth, p.ActualHeight);
            }

Good. Now we should multiply the control. Let’s go old good WPF way and clone by using XamlReader/XamlWriter solute. WTF? There is no XamlWriter in Silverlight - “They did not put it there”. What to do? Clone ‘em all!

How to clone objects in Silverlight without XamlWriter

We need reflection. A lot of reflection, so we’ll create extended method for DependencyObject type, that clone for us the objects without XamlWriter. First of all we should create new instance of target class

public static T Clone<T>(this T source)  where T : DependencyObject
        {
            Type t = source.GetType();
            T no = (T)Activator.CreateInstance(t);

Then travel recursively inside the type get all DependencyProperties and DependencyObjects. We need DependencyProperty to use system setter and DependencyObjects to clone them too. What is DependencyProperty from reflection point of view? It’s Static, Public and not Public ReadOnly Field. So, we’ll look for those.

Type wt = t;
            while (wt.BaseType != typeof(DependencyObject))
            {
                FieldInfo[] fi = wt.GetFields(BindingFlags.Static | BindingFlags.Public);
                for (int i = 0; i < fi.Length; i++)
                {
                    {
                        DependencyProperty dp = fi[i].GetValue(source) as DependencyProperty;

 

Now all values of Dependency Properties are Dependency Objects or it’s ancestors, so we should be very smart (also we do not want NameProperty of cause)

if (dp != null && fi[i].Name != "NameProperty")
                        {
                            DependencyObject obj = source.GetValue(dp) as DependencyObject;
                            if (obj != null)
                            {
                                object o = obj.Clone();
                                no.SetValue(dp, o);
                            }
                            else
                            {

Also, there are some DependencyProperties, that we cannot set. How to detect them? Inside DependencyProperty class there are only two methods (Register and RegisterAttached) and no fields. Why there is no IsReadOnly (or something) property? “They did not put it there”. As well as we cannot know what the target type of the property. Another “They did not put it there”. So, we should do it manually

else
                            {
                                if(fi[i].Name != "CountProperty" &&
                                    fi[i].Name != "GeometryTransformProperty" &&
                                    fi[i].Name != "ActualWidthProperty" &&
                                    fi[i].Name != "ActualHeightProperty" &&
                                    fi[i].Name != "MaxWidthProperty" &&
                                    fi[i].Name != "MaxHeightProperty" &&
                                    fi[i].Name != "StyleProperty")
                                {
                                    no.SetValue(dp, source.GetValue(dp));
                                }
                            }

And recursive call at the end

wt = wt.BaseType;
            }

Now we have all Dependency Properties. What’s about regular CLR properties? We need them too. Let’s grab it

PropertyInfo[] pis = t.GetProperties();
            for (int i = 0; i < pis.Length; i++)
            {

                if (
                    pis[i].Name != "Name" &&
                    pis[i].Name != "Parent" &&
                    pis[i].CanRead && pis[i].CanWrite &&
                    !pis[i].PropertyType.IsArray &&
                    !pis[i].PropertyType.IsSubclassOf(typeof(DependencyObject)) &&
                    pis[i].GetIndexParameters().Length == 0 &&
                    pis[i].GetValue(source, null) != null &&
                    pis[i].GetValue(source,null) == (object)default(int) &&
                    pis[i].GetValue(source, null) == (object)default(double) &&
                    pis[i].GetValue(source, null) == (object)default(float)
                    )
                    pis[i].SetValue(no, pis[i].GetValue(source, null), null);

This will work fine for regular properties, but not for lists. There we should Add()/Get()/Remove() Items we cannot just set them. what’s the problem?

else if (pis[i].PropertyType.GetInterface("IList", true) != null)
                {
                    int cnt = (int)pis[i].PropertyType.InvokeMember("get_Count", BindingFlags.InvokeMethod, null, pis[i].GetValue(source, null), null);
                    for (int c = 0; c < cnt; c++)
                    {
                        object val = pis[i].PropertyType.InvokeMember("get_Item", BindingFlags.InvokeMethod, null, pis[i].GetValue(source, null), new object[] { c });

                        object nVal = val;
                        DependencyObject v = val as DependencyObject;
                        if(v != null)
                            nVal = v.Clone();

                        pis[i].PropertyType.InvokeMember("Add", BindingFlags.InvokeMethod, null, pis[i].GetValue(no, null), new object[] { nVal });
                    }
                }

Very well. Now we have our brand new clones ready for reuse. All we have to do is to add and layout them.

void SetPatternImpl(double width, double height)
        {
            Pattern = new WrapPanel();
            Pattern.Width = width;
            Pattern.Height = height;
            Pattern.HorizontalAlignment = HorizontalAlignment.Stretch;
            Pattern.VerticalAlignment = VerticalAlignment.Stretch;

            double xObj = (1 / this.Viewport.Width);
            double yObj = (1 / this.Viewport.Height);

            for (int i = 0; i < Math.Ceiling(xObj*yObj); i++)
            {
                Shape ns = this.Drawing.Clone();
                ns.Stretch = this.TileMode == TileMode.None?Stretch.None:Stretch.Fill;
                ns.Width = Pattern.Width / xObj;
                ns.Height = Pattern.Height / yObj;
                ScaleTransform st = new ScaleTransform();
                st.ScaleX = this.TileMode == TileMode.FlipX | this.TileMode == TileMode.FlipXY ? -1 : 1;
                st.ScaleY = this.TileMode == TileMode.FlipY | this.TileMode == TileMode.FlipXY ? -1 : 1;
                ns.RenderTransform = st;
                Pattern.Children.Add(ns);
            }
        }

We done. How to use our bush? Simple, with regular Xaml syntax

<Grid x:Name="LayoutRoot" Width="300" Height="300">
        <Grid.Background>
            <l:DrawingBrush Viewport="0,0,0.25,0.25" TileMode="Tile">
                <l:DrawingBrush.Drawing>
                    <Path Stroke="Black" Fill="Red" StrokeThickness="3">
                        <Path.Data>
                            <GeometryGroup>
                                <EllipseGeometry RadiusX="20" RadiusY="45" Center="50,50" />
                                <EllipseGeometry RadiusX="45" RadiusY="20" Center="50,50" />
                            </GeometryGroup>
                        </Path.Data>
                    </Path>
                </l:DrawingBrush.Drawing>
            </l:DrawingBrush>
        </Grid.Background>
        <Canvas Width="150" Height="150" x:Name="canvas">
            <Canvas.Background>
                <l:DrawingBrush Viewport="0,0,0.1,0.1" TileMode="FlipX">
                    <l:DrawingBrush.Drawing>
                        <Polygon Fill="Blue" Points="0,0 1,1 1,0 0,1"/>
                    </l:DrawingBrush.Drawing>
                </l:DrawingBrush>
            </Canvas.Background>
            <TextBox Foreground="Yellow" Background="#AA000000" Text="Hello, World!" Height="30"/>

        </Canvas>
    </Grid>

I used my WrapPanel within this sample. It’s easy to build custom controls in Silverlight, much easier, then Brushes. Why? Because “They did not put it there”.

How to build layout control in Silverlight

Really simple. 1 – subclass panel

public class WrapPanel : Panel
   {

Override MeasureOverride

protected override Size MeasureOverride(Size availableSize)
{
    foreach (UIElement child in Children)
    {
        child.Measure(new Size(availableSize.Width, availableSize.Height));
    }

    return base.MeasureOverride(availableSize);
}

Then ArrangeOverride and you done!

protected override Size ArrangeOverride(Size finalSize)
        {

            Point point = new Point(0, 0);
            double maxVal = 0;
            int i = 0;

            if (Orientation == Orientation.Horizontal)
            {
                double largestHeight = 0.0;

                foreach (UIElement child in Children)
                {

                    child.Arrange(new Rect(point, new Point(point.X + child.DesiredSize.Width, point.Y + child.DesiredSize.Height)));

                    if (child.DesiredSize.Height > largestHeight)
                        largestHeight = child.DesiredSize.Height;

                    point.X = point.X + child.DesiredSize.Width;

                    if ((i + 1) < Children.Count)
                    {
                        if ((point.X + Children[i + 1].DesiredSize.Width) > finalSize.Width)
                        {
                            point.X = 0;
                            point.Y = point.Y + largestHeight;
                            maxVal += largestHeight;
                            largestHeight = 0.0;
                        }
                    }

                    i++;

                }
                if (AllowAutosizing)
                {
                    finalSize.Height = maxVal;

                    //this is ugly workaround, 'cos ScrollViewer uses Height property instead of ActualHeight
                    if (this.Height != maxVal)
                        SetValue(HeightProperty, maxVal);
                }
            }
            else
            {
                double largestWidth = 0.0;

                foreach (UIElement child in Children)
                {
                    child.Arrange(new Rect(point, new Point(point.X + child.DesiredSize.Width, point.Y + child.DesiredSize.Height)));

                    if (child.DesiredSize.Width > largestWidth)
                        largestWidth = child.DesiredSize.Width;

                    point.Y = point.Y + child.DesiredSize.Height;

                    if ((i + 1) < Children.Count)
                    {
                        if ((point.Y + Children[i + 1].DesiredSize.Height) > finalSize.Height)
                        {
                            point.Y = 0;
                            point.X = point.X + largestWidth;
                            maxVal += largestWidth;
                            largestWidth = 0.0;
                        }
                    }

                    i++;
                }
                if (AllowAutosizing)
                {
                    finalSize.Width = maxVal;

                    //this is ugly workaround, 'cos ScrollViewer uses Width property instead of ActualWidth
                    if (this.Width != maxVal)
                        SetValue(WidthProperty, maxVal);
                }
            }

            return base.ArrangeOverride(finalSize);
        }

We done. Here how it looks like. Have a nice day and be good people

This is not final control, there are some limitations

  • It works with Panels only
  • It does not layout (thus you cannot use it with StackPanel for example)
  • You should name hosting control (I explained why)
  • For drawings inside DrawingBrush you can use only Shape derived classes (e.g. Line, Polygon, Ellipse, Path etc)

You are more, then welcome to enhance this control, ‘cos it does not looks like Microsoft going to have DrawingBrush in RTM of Silverlight. The only request is – submit and share your enhancements to help all other developers and make their live easier with this necessary control. Source code for this article