DCSIMG
March 2007 - Posts - Just code - Tamir Khason

March 2007 - Posts

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/asynchronous-data-templates-data-providers-and-data-binding/]


DataBinding, templates and providers and really cool things in WPF, but have you tried to load something really heavy? How time it's take to load 20 images? A couple of seconds? And what about if each image is 3000X3000 px? Huh, your HMI thread will be locked until all those images will be downloaded and displayed. In this post, I'll explain how to build asynchronous XMLDataProvider, that load RSS from Flickr, asynchronous DataTemplate, that loaded image by image without locking your application and asynchronous Binding, that provides an ability to separate images and not to load all of them, if they not visible. So, let's start.

First of all we have to retrieve Flickr RSS feed. So, we'll create XmlDataProvider for this

<XmlDataProvider  
                 Source="http://api.flickr.com/services/feeds/photos_public.gne?tags=wallpaper&amp;format=rss_200" 
                 XPath="/rss/channel/item" x:Key="src"/>

This the first and really easy part. Nice, now we'll create data template and the host for it

<DataTemplate x:Key="img">
      <Image Source="{Binding XPath=media:content/@url}" Stretch="Uniform"/>
</DataTemplate>
 
<ListBox Grid.Row="1" ItemsSource="{Binding Source={StaticResource src}}"
            ItemTemplate="{StaticResource img}"/>

Fine, run it to see nothing. Why? because of XmlNamespace media, found in RSS 2.0. We should tell XmlDataProvider what's the namespace, so we have to add Namespace manager to point to http://search.yahoo.com/mrss/ URI, that actually describes media namespace. To do it, all you have to do it to add XmlNamespaceMapping to your resources and point XmlDataProvider to see it.

 

<XmlNamespaceMappingCollection x:Key="map">
      <XmlNamespaceMapping Uri="http://search.yahoo.com/mrss/" Prefix="media"/>
</XmlNamespaceMappingCollection>
 
<XmlDataProvider XmlNamespaceManager="{StaticResource map}" 
                     Source="http://api.flickr.com/services/feeds/photos_public.gne?tags=wallpaper&amp;format=rss_200" 
                     XPath="/rss/channel/item" x:Key="src"/>

So far so good, but now, when you'll run your application, it become frozen for 15 minutes, until all images will be loaded, then it force your CPU to 100% for 2 minutes to render all those images. Hm, well, you have really fast internet and your computer has Four Quad Core CPU. In this case, your application freeze for only three minutes. Really bad, isn't it?

So what to do? Let try first of all convert our data source to asynchronous. In order to do it, we'll set IsAsynchronous property to"True". Additional fix we'll add is to force the data provider to work, even before, we'll need it. Set IsInitialLoadEnabled="True". Compile.

Now it's better. We just saved the interface from freezing for 10 seconds (while RSS loaded). Still bad, really bad! The actual problem is not in DataProvider, but in all those urls and large images in it. Let make our DataBinding to work asynchronous. Let's make it load images one by one.

We'll add another property to the binding expression of our hosting control. And now it'll looks like this

 

<ListBox ItemsSource="{Binding IsAsync=True, Source={StaticResource src}}"
             ItemTemplate="{StaticResource img}"/>

Very well. Now our UI is released and all images loaded one-by-one without holding STAThread, which actually run it.

The next challenge is to force an application not to load all those images, which are not visible and see how many images already downloaded. For this, we'll add eventhandler Loaded event of image in the template. Add read only Dependency Property to your window and bind it to textblock to see.

int loadedTotalCounter = 0;
        void onImgLoaded(object sender, RoutedEventArgs e)
        {
SetValue(ImagesTotalPropertyKey, ++loadedTotalCounter);
        }
public static readonly DependencyPropertyKey ImagesTotalPropertyKey = DependencyProperty.RegisterReadOnly("ImagesTotal",
    typeof(int),
    typeof(Window1),
    new PropertyMetadata(0));
 
public static readonly DependencyProperty ImagesTotalProperty = ImagesTotalPropertyKey.DependencyProperty;
 
public int ImagesTotal
{
    get { return (int)GetValue(ImagesTotalProperty); }
}

Wow, if we'll put our ListView into grid, all invisible items will not being loaded, until we'll reveal them. Nice feature. Really nice, but why our counter of loaded images grows, even if the images are invisible?

The reason is simple, ImageSource is asynchronous property, so when we even got the source, this does not means, that the image is really loaded, so how to figure the actually number of loaded images. Let's dive deeper into ImageSource.

The object behind the ImageSource is BitmapFrame and it's (as we already tell) asynchronous object, so it has Download-related events such as DownloadCompleted, DownloadProgress and DownloadFailed. Let's put another readonly DP to our window, to cound actually downloaded frames

 

public static readonly DependencyPropertyKey ImagesLoadedPropertyKey = DependencyProperty.RegisterReadOnly("ImagesLoaded",
            typeof(int),
            typeof(Window1),
            new PropertyMetadata(0));
 
        public static readonly DependencyProperty ImagesLoadedProperty = ImagesLoadedPropertyKey.DependencyProperty;
 
        public int ImagesLoaded
        {
            get { return (int)GetValue(ImagesLoadedProperty); }
        }
 
        public static readonly DependencyPropertyKey ImagesTotalPropertyKey = DependencyProperty.RegisterReadOnly("ImagesTotal",
            typeof(int),
            typeof(Window1),
            new PropertyMetadata(0));

Now, let's get actual source and subscribe it to counter

 

void onDownloadCompleted(object sender, EventArgs e)
        {
            SetValue(ImagesLoadedPropertyKey, ++loadedCounter);
        }
 
 
 
        int loadedCounter = 0;
        int loadedTotalCounter = 0;
        void onImgLoaded(object sender, RoutedEventArgs e)
        {
            Image img = sender as Image;
            if (img != null)
            {
                BitmapFrame bimage = img.Source as BitmapFrame;
                if (bimage != null)
                {
                    bimage.DownloadCompleted += new EventHandler(onDownloadCompleted);
                }
            }
            SetValue(ImagesTotalPropertyKey, ++loadedTotalCounter);
        }

That's works great, but so some reason, DownloadCompleted event not always being fired. Recently, when the framework "skips" it, the image appears too fast. Is it bug? Not really. The real source for image is not network, but local cache, so not always BitmapFrame downloaded. And if it is not, why to fire event. Add handler for this, set break point and see.

 

void onDownloadCompleted(object sender, EventArgs e)
        {
            SetValue(ImagesLoadedPropertyKey, ++loadedCounter);
        }
 
 
 
        int loadedCounter = 0;
        int loadedTotalCounter = 0;
        void onImgLoaded(object sender, RoutedEventArgs e)
        {
            Image img = sender as Image;
            if (img != null)
            {
                BitmapFrame bimage = img.Source as BitmapFrame;
                if (bimage != null)
                {
                    //why this?
                    //Actually, the images are loaded from cache, so we have to check if it's downloading before, we'll want it to complete an action :)
                    if (bimage.IsDownloading)
                    {
                        bimage.DownloadCompleted += new EventHandler(onDownloadCompleted);
                    }
                    else
                    {
                        SetValue(ImagesLoadedPropertyKey, ++loadedCounter);
                    }
                }
            }
            SetValue(ImagesTotalPropertyKey, ++loadedTotalCounter);
        }

Dunno. Now we have everything working asynchronous way and our UI is free for action. Does WPF architecture is really genius to think about all those cases?

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/textbox-for-high-contrast-very-small-text/]


 The most common question about text capabilities in WPF is about small fonts. By default all texts rendered with presentation layer are anti-aliased,  wich means, that we will get great text experience with large fonts, and blurry texts with small and extra small fonts. 

As you, probably know, the common usage of big fonts is in entertainment-related application, however most of business-related applications using small fonts. There is impossible to solve "blurry" problem with WPF texts, so today, we'll solve it with WPF geometry. Today, we'll create custom textblock, that holds texts, which visible and readable on any background, even with very small fonts. 

 

 So, let the show start. First of all, we'll create new control, derived from FrameworkElement. We'll add a couple of dependency properties for Text, Font, FontSize, fore and background color. But, were the trick, you'll ask... Instead of drawing text, onRender event, we'll draw geometry of the text and stroke it with contrast color.

 

protected override void OnRender(DrawingContext drawingContext)
       {
           drawingContext.DrawGeometry(Fill, m_pen, m_textg);
       }

The next question is - where to get the geometry of the text? FormattedText class exposes method named BuildGeomerty, that converts text into geometrical representation. So, in order to build a text and it's geometry, we'll build internal method, for text generation, based on control's DPs

 

private void GenerateText()
        {
            if (Font == null)
                Font = new FontFamily("Arial");
 
            FormattedText fText = new FormattedText(
               Text,
               CultureInfo.CurrentCulture,
               FlowDirection.LeftToRight,
               new Typeface(
                   Font,
                   FontStyles.Normal,
                   FontWeights.Heavy,
                   FontStretches.Normal),
               FontSize,
               Brushes.Black
               );
 
            m_textg = fText.BuildGeometry(new Point(0, 0)).GetAsFrozen() as Geometry;
        }

So now, after compilation, we'll get the text stroked with contrast pen, but it will be anti-aliased. What to do? Framework, can not cancel anti-aliasing for text, but it can do it for geometry. In order to do it, just call the framework to cancel it.

RenderOptions.SetEdgeMode(this, EdgeMode.Aliased);

There are a couple of other tips you'll have to do. One is snap your graphics to device pixels. Why to do it? Read MSDN :)

this.SnapsToDevicePixels = true;

Additional tip is set linejoin to Round and MeterLimit to 1 for line "pike" prevention. One other trick is to freeze the pen after generation, because, you wont going to change it while rendering, so save the system resources.

 

private static void OnTextInvalidated(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            m_pen = new Pen(((HiResTextBlock)d).Stroke, ((HiResTextBlock)d).StrokeThickness);
            m_pen.LineJoin = PenLineJoin.Round;
            m_pen.MiterLimit = 1;
            m_pen = m_pen.GetAsFrozen() as Pen;
            ((HiResTextBlock)d).GenerateText();
        }

Now, tell the framework to call all those methods by doing this and you're done

 

public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
            "Text",
            typeof(string),
            typeof(HiResTextBlock),
            new FrameworkPropertyMetadata(
                 "",
                 FrameworkPropertyMetadataOptions.AffectsRender,
                 new PropertyChangedCallback(OnTextInvalidated),
                 null
                 )
            );

Now, let's see what we get. Pretty nice isn't it? 

 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/creating-datatemplates-from-code/]


It's really easy to create data templates from XAML, but today, one of my clients asks me to do it from code. The other request was to handle events of element form inside the template. So let's do it together

First of all we have to create DataProvider as we usual do in xaml.

 

XmlDataProvider data = new XmlDataProvider();
           data.Document = new System.Xml.XmlDocument();
           data.Document.InnerXml = getXMLstring();
           data.XPath = "/root/node";

Next step is to create binding expression for data template usage

 

Binding itemsBinding = new Binding();
            itemsBinding.XPath = "val";

So far, so good. Now comes the trick. In order to use DataTemplate class, we have to create Visual Tree of elements, being used in the template. In order to do it, first we should create FrameworkElementFactory and set all necessary handlers and properties there. The factory, is actually, kind of meta element,  derived from FrameworkElement and makes us able to markup anything, we have to have within the instance of future element. So let's do it

 

FrameworkElementFactory comboFactory = new FrameworkElementFactory(typeof(ComboBox));
            comboFactory.Name = "myComboFactory";
            comboFactory.SetBinding(ComboBox.ItemsSourceProperty, itemsBinding);
            comboFactory.AddHandler(ComboBox.SelectionChangedEvent, new SelectionChangedEventHandler(popup));

As you can see, I'm setting Binding and add event handler to FrameworkElementFactory in order them to appears in real element that will be generated by the factory.

The next step is rather simple. We'll create DataTemplate and set it's VisualTree property to newly created factory.

 

DataTemplate itemsTemplate = new DataTemplate();
           itemsTemplate.VisualTree = comboFactory;

Another binding, setting ItemTemplate property and we done.

 

Binding nodeBinding = new Binding();
            nodeBinding.Source = data;
 
            ListView lv = new ListView();
            lv.ItemTemplate = itemsTemplate;
            lv.SetBinding(ListView.ItemsSourceProperty, nodeBinding);
 
            myGrid.Children.Add(lv);

It's rather straight-forward to do it, but still, it's much simpler to create templates from XAML, isn't it? :)

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/camtasia-401-for-vista-is-shipping/]


Finally, TechSmith released the final new version of their Camtasia Studio. Now, it's Microsoft Windows Vista compatible. I will now explain about this product, but just in case, this is the best tool for screen capturing ever. All those who had Camtasia used, know it.

Currently, I'm using their beta version, downloaded a couple of months ago. It's running fine on Vista, but still there are some issues.

For all those who have not purchased this tool, do it - it worth each penny of $299 individual license.

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


One of most common questions, I meet with my customers is how to animate grid size. Actually, there is no problem to animate doubles, ints, even sizes, but how to animate GridLength, which actually used to measuring the sizes in grid?

In order to do it, we'll create our own animation, named GridLengthAnimation and use it within our grid. So, let's start.

First of all we have to inherit base animation timeline in order to get the functionality we want.

 

public abstract class GridLengthAnimationBase : AnimationTimeline
    {
        protected GridLengthAnimationBase() { }
        public override sealed Type TargetPropertyType
        {
            get { return typeof(GridLength); }
        }
        public override object GetCurrentValue(object defaultOriginValue, object defaultDestinationValue, AnimationClock animationClock)
        {
            if (defaultOriginValue is GridLength == false)
                throw new ArgumentException("Parameter must be a GridLength", "defaultOriginValue");
            if (defaultDestinationValue is GridLength == false)
                throw new ArgumentException("Parameter must be a GridLength", "defaultDestinationValue");
            return GetCurrentValueCore((GridLength)defaultOriginValue, (GridLength)defaultDestinationValue, animationClock);
        }
        public abstract GridLength GetCurrentValueCore(GridLength defaultOriginValue, GridLength defaultDestinationValue, AnimationClock animationClock);
    }

Next step is to create actual animation. It's pretty simple, when we already have base type. We have to create some Dependency Properties used within timeline, such as By, From, To etc. and treat of some possible errors (we do not want our animation to be thrown :) )

 

public class GridLengthAnimation : GridLengthAnimationBase
{
    public static readonly DependencyProperty ByProperty = DependencyProperty.Register(
     "By",
     typeof(double?),
     typeof(GridLengthAnimation),
     new PropertyMetadata(null));
    public static readonly DependencyProperty FromProperty = DependencyProperty.Register(
     "From",
     typeof(double?),
     typeof(GridLengthAnimation),
     new PropertyMetadata(null));
    public static readonly DependencyProperty ToProperty = DependencyProperty.Register(
     "To",
     typeof(double?),
     typeof(GridLengthAnimation),
     new PropertyMetadata(null));
    public double? By
    {
        get { return (double?)this.GetValue(ByProperty); }
        set { this.SetValue(ByProperty, value); }
    }
    public double? From
    {
        get { return (double?)this.GetValue(FromProperty); }
        set { this.SetValue(FromProperty, value); }
    }
    public double? To
    {
        get { return (double?)this.GetValue(ToProperty); }
        set { this.SetValue(ToProperty, value); }
    }
    protected override Freezable CreateInstanceCore()
    {
        return new GridLengthAnimation();
    }
    public override GridLength GetCurrentValueCore(GridLength defaultOriginValue, GridLength defaultDestinationValue, AnimationClock animationClock)
    {
        if (From == null)
            throw new Exception("From must be specified in a GridLengthAnimation");
        double a_to;
        if (To != null)
            a_to = To.Value;
        else if (By != null)
            a_to = From.Value + By.Value;
        else
            throw new Exception("Either To or By must be specified in a GridLengthAnimation");
        return new GridLength(From.Value + ((a_to - From.Value) * animationClock.CurrentProgress.Value));
    }
}

That's all, folks. All we have to do now is to use our new animation type with our grid as we know to use any other type of animation. It's looks like this

 

<BeginStoryboard>
              <Storyboard>
                <ParallelTimeline>
                  <local:GridLengthAnimation
                    Storyboard.TargetName="l"
                    Storyboard.TargetProperty="(ColumnDefinition.Width)"
                    From="30"
                    To="300"
                    AutoReverse="False"
                    Duration="0:0:1"/>
                </ParallelTimeline>
              </Storyboard>
            </BeginStoryboard>

Hurrah, from now we know to build any type of animations and actually do not need Microsoft to provide us with premade one. Do't it looks like working with converters? The answer is "yes".

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/adding-web-references-to-your-vs-project-by-code/]


Today one of my clients asked me to find a way to add web references to existing VS project without using Visual Studio wizard, to do it by code. I built VS add-ins before, so I know what Studio can do, but the question is if I can replace existing system dialogs, without writing a lot of code (I'm really lazy, if I'll write a lot, I'll have no time to write here). So I start to check the material. Really, there is very few information regarding build VS addins, much less regarding creation of references, and nothing about creation web references. So let's deep into VS to understand what's happens there.

While adding web references (the references to Web Services), Visual Studio performs a couple of action. First it checks and retrieves the WS information, then it checks if the current project has local reference to System.Web.Services (the default WinForms project has not). The it discovered the service, by using discovery client protocol, collects the information regarding it's methods by using their public descriptors, then reveals XML schema and based on this schema generates web proxy by using WSDL. Wow, a lot of stuff. How to get rid on it?

Let's discover the world of DiscoveryClientProtocol from Discovery namespace of Web.Services assembly. It has a couple of really interesting methods such as Discover... or Resolve... By using those methods we can create the web service protocol

DiscoveryClientProtocol protocol = new DiscoveryClientProtocol();
            protocol.DiscoverAny(url);
            protocol.ResolveOneLevel();

The next step is to get descriptors. In order to do it, we'll iterate References property of the protocol and create ContractReference and DiscoveryReference for each of methods. We'll add them to our services collection

ServiceDescriptionCollection services = new ServiceDescriptionCollection();
            foreach (DictionaryEntry entry in protocol.References)
            {
                ContractReference contractRef = entry.Value as ContractReference;
                DiscoveryDocumentReference discoveryRef = entry.Value as DiscoveryDocumentReference;
                if (contractRef != null)
                {
                    services.Add(contractRef.Contract);
                }
            }

Let's get the schema of our service in order to generate proxy later it's pretty easy, especially when we have our protocol ready

XmlSchemas schemas = new XmlSchemas();
            foreach (DictionaryEntry entry in protocol.References)
            {
                SchemaReference schemaRef = entry.Value as SchemaReference;
                if (schemaRef != null)
                {
                    schemas.Add(schemaRef.Schema);
                }
            }

Next step is proxy (code) generation. In order to use it, we'll have to ask CodeDom and CodeCompile classes to help up. Actually, we can also call WSDL.exe to generate it, but if we already deep in code, let's create it

ServiceDescriptionImporter importer = new ServiceDescriptionImporter();
 
            foreach (ServiceDescription description in serviceDescriptions)
            {
                importer.AddServiceDescription(description, null, null);
            }
 
            foreach (XmlSchema schema in schemas)
            {
                importer.Schemas.Add(schema);
            }
 
            System.CodeDom.CodeNamespace codeNamespace = new System.CodeDom.CodeNamespace(proxyNamespace);
            CodeCompileUnit codeUnit = new CodeCompileUnit();
            codeUnit.Namespaces.Add(codeNamespace);
            ServiceDescriptionImportWarnings warnings = importer.Import(codeNamespace, codeUnit);
 
            CodeDomProvider provider = new Microsoft.CSharp.CSharpCodeProvider();
            using (StreamWriter sw = new StreamWriter(fileName))
            {
                CodeGeneratorOptions options = new CodeGeneratorOptions();
                options.BracingStyle = "C";
                provider.GenerateCodeFromCompileUnit(codeUnit, sw, options);
            }

Now we have generated files (Reference.map with it's cs, Service.disco and Service.wsdl) all we have to do now is to put them together and reference somehow to our project.

string path = Path.GetDirectoryName(project.FullName)+@"\Web References";
            if (!Directory.Exists(path))
                Directory.CreateDirectory(path);
 
            Directory.CreateDirectory(path + @"\" + name);
 
            path += @"\" + name;
 
 
            protocol.WriteAll(path, "Reference.map");
 
            ServiceDescriptionCollection services = GetServiceDescriptionCollection(protocol);
            XmlSchemas schemas = GetXmlSchemas(protocol);
            GenerateWebProxy(name, path + @"\Reference.cs", services, schemas);

So far so good. In order to do something with our project we'll build add-in for Visual Studio, that can pass us the current project and work with it. I'll not explain how to build addins here, ask Google Live to help :)

Just the small explanation for how to get current project, while in DTE _applicationObject.ActiveDocument we only have a document). DTE Document has something, called ProjectItem (we'll meet them closer later) inside this Item we have property named ContainingProject, that actually holds the parent project of this item.

internal static Project GetCurrentProject(Document currDoc)
        {
            if (currDoc == null)
                return null;
 
            ProjectItem prjItem = currDoc.ProjectItem;
            if (prjItem == null)
                return null;
 
            Project currPrj = prjItem.ContainingProject;
            if (currPrj == null)
                return null;
 
            return currPrj;
        }

Good, but in order to add reference to the project we need Visual Studio Project in other words VSProject. This special project holt by secret property of the Project named Object and their namespace is VSLangProj, that dug in VSLangProj assembly in Program Files\Microsoft Visual Studio 8\Common7\IDE\PublicAssemblies (are they really so "public" ? :)

Now let's take a look on this project. Oh my g-d! It has method named ADDWEBREFERENCE, that received url string as input. Just throw all you did off and use it in order to add web reference to your project. Smart VSTS will do all the rest for you!

 

internal static ProjectItem AddWebReference(Project project, string url, string name)
        {
            VSProject vsProj = project.Object as VSProject;
            if (vsProj == null)
                return null;
 
            ProjectItem item = vsProj.AddWebReference(url);
            try
            {
                item.Name = name;
            }
            catch (System.Runtime.InteropServices.COMException ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
 
                item.Remove();
            }
 
            return item;
 
        }

Have a nice day,  Visual Studio development and documentation team (this methods exists in MSDN, but researchable for some reason :))

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/how-to-bind-to-animation-to-and-from-properties/]


If you tried to bind to From and To values of Animation anything, you sure pay attention to the behavior, that the animation object just ignored you in the best case, in worth case, you couch very strange an annoying exception (if used from code-behind) "Cannot freeze the storyboard timeline tree for use across threads". What's the problem?

First of all, just note in your notepad: "When you start an animation, Timeline object copying itself frozen into internal boundary. There it creates Clock object and begin to run. So, how to change properties of the animation and make them occur immediately.

First of all, you can recreate whole storyboard. Rather costly and  very bad idea. You can make some woodoo on Completed event. For example, call invalidation of the animation to affect your changes. Good idea, but still a lot of mess. Another try is to work with FillBehavior. This one make you able to tell your animation to stop or to hold up for something else. Very good and very efficient method is to deal with internal clocks. For example, recreate or exchange them by your own. This is really hardcore. Maybe other day I'll explain this methods, but today, we'll just call new BeginAnimation to restart it. This method is not very efficient, but better then nothing. In order to do it, we'll deal today with EventTriggers.

So let's build the simple application, that continuously  changes the size of rectangle, based on values you provided with sliders. So, here we go.

First of all, let's create an animation

<Storyboard x:Key="myAnim">
      <ParallelTimeline>
        <DoubleAnimation
          Name="myAnimWidth"
          Storyboard.TargetName="myRect"
          Storyboard.TargetProperty="Width"
          From="100"
          To="500"
          Duration="0:0:5
          RepeatBehavior="Forever"
          AutoReverse="True"
                />
        <DoubleAnimation
          Name="myAnimHeight"
          Storyboard.TargetName="myRect"
          Storyboard.TargetProperty="Height"
          From="100"
          To="500"
          Duration="0:0:5"
          RepeatBehavior="Forever"
          AutoReverse="True"
                />
      </ParallelTimeline>
    </Storyboard>

We'll put in into resources in order us to be able to reuse and rerun it. The next step is to create two sliders, a couple of textblocks and the rectangle. I'll not rewrite a code here, 'cos it's really straight forward and we have nothing tricky with it. Now, I want to start the animation right after my page was loaded, so I'll add Page.Trigger to my Page.Loaded event. Pay attention, this method makes you able to almost any event from XAML. This is really cool feature. So, let's do it. 

<Page.Triggers>
    <EventTrigger RoutedEvent="Page.Loaded">
      <EventTrigger.Actions>
        <BeginStoryboard Storyboard="{StaticResource myAnim}"/>
      </EventTrigger.Actions>
    </EventTrigger>
  </Page.Triggers>

So far so good. The next step is to rerun the animation each time I'm changing the value of my sliders. As well as done with Page, Slider (as well as any other ContentPresenter has it's own event. So let's restart the animation on value change

<Slider Name="slFrom" Value="50" Width="150" Minimum="50" Maximum="600">
        <Slider.Triggers>
          <EventTrigger RoutedEvent="Slider.ValueChanged">
            <EventTrigger.Actions>
              <BeginStoryboard Storyboard="{StaticResource myAnim}"/>
            </EventTrigger.Actions>
          </EventTrigger>
        </Slider.Triggers>
      </Slider>

Rather simple, isn't it? We done, now we have our storyboard restarted and "invalidated" each time the bindings of From="{Binding ElementName=slFrom, Path=Value}" and To="{Binding ElementName=slTo, Path=Value}" where changed.

That's all, folks

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/multithread-logger-in-wpf/]


So, we already know to write very nice WPF application, but one of most common modules in every application is logger. How to use WPF capabilities to build smart logger. What to do with multithreading environment, when methods InvokeRequired is absent?

Today, we'll build logger, based on DataBinding of WPF, learn how to know what thread we are in and how to bind resources to ListView. Pretty enough? Let's start

First of all we have to build our XAML page. It sill consists of checkbox, binded to static variable that tells us if we have to log everything. So, we'll add simple boolean, named m_isDebug and bind it to the checkbox. But how? m_isDebug is simple variable, not dependency property, how to get it and bind to control. This part is really simple. We'll do it this way ("l" is our clr-namespace: xmlns:l="clr-namespace:WPFLogger")

<CheckBox IsChecked="{x:Static l:Window1.m_isDebug}">Debug view</CheckBox>

So far, so good. Now the main part of the application, our XmlDataSource - the log file. It's really simple to add the structure to our XAML

<Window.Resources>

  <XmlDataProvider x:Key="myDebugData" XPath="/myXmlData">
    <x:XData>
      <myXmlData xmlns="">
        <Item Date="00/00/00 00:00:00" Source="None" Message="Testing..."/>
      </myXmlData>
    </x:XData>
  </XmlDataProvider>
</Window.Resources>

Event simpler to get it from the code

protected void OnLoad(object sender, RoutedEventArgs e)

        {
            debug = this.Resources["myDebugData"] as XmlDataProvider;
            debug.IsAsynchronous = true;
            WriteLog(this, "Initialized...");            
        }

Let's build the static method, that knows to write to this Data Provider. This method should know where he called from in order us to be able to get debug information later. We do not have to leave our project in Debug mode to get StackTrace information. So, the method will looks like this:

if (debug != null && m_isDebug)
                {
                    System.Diagnostics.StackTrace stack = new System.Diagnostics.StackTrace(false);
 
 
                    XmlElement item = debug.Document.CreateElement("Item");
 
                    item.SetAttribute("Date", DateTime.Now.ToString("dd-MMM-yy HH:mm:ss.ffff"));
                    item.SetAttribute("Source", sender!=null?sender.ToString() + ".":Thread.CurrentThread.Name + stack.GetFrame(1).GetMethod().Name);
                    item.SetAttribute("Message", data);
 
                    debug.Document.SelectSingleNode(debug.XPath).AppendChild(item);
                }

StackTrace will tell us where we are together will caller attribute or thread information, if inaccessible. We are speaking about threads, is this method is thread safe? Not really. We are interacting with "debug" object, and updates ListView, so we have to be sure that we are in the right (STA) thread. There is no such thing InvokeRequired in WPF, due the new object, named Dispatcher tries to help us with thread synchronization. It knows what thread the caller exists and make us able to BeginInvoke and invoke our method within the right context. In order to get such information, we add a couple of things. First of all delegate to this method, next we'll add to the method check of context and then invoke or execute the rest

internal delegate void WriteLogDelegate(object sender, string data);
 
internal static void WriteLog(object sender, string data)
        {
            if (!Application.Current.Dispatcher.CheckAccess())
            {
                Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new WriteLogDelegate(WriteLog), sender, data);
            }
            else
            {
                if (debug != null && m_isDebug)
                {
                    System.Diagnostics.StackTrace stack = new System.Diagnostics.StackTrace(false);
 
 
                    XmlElement item = debug.Document.CreateElement("Item");
 
                    item.SetAttribute("Date", DateTime.Now.ToString("dd-MMM-yy HH:mm:ss.ffff"));
                    item.SetAttribute("Source", sender!=null?sender.ToString() + ".":Thread.CurrentThread.Name + stack.GetFrame(1).GetMethod().Name);
                    item.SetAttribute("Message", data);
 
                    debug.Document.SelectSingleNode(debug.XPath).AppendChild(item);
                }
            }
        }

The rest is really simple, we'll add listview, binded to our xml data provider, a couple of nice features, like saving, sorting etc within the data source. All the rest you'll see in the source code attached.

The usage of new WPF logger is really simple. Just call WriteLog(object, string) from any place in any thread you want and the result will appears in listbox immediately. The are a couple of FXes I'd add to the logger, such as priority, display or write flag and more, But all those are really application specific, so do it yourself :)

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/global-events-hooking/]


WPF has really great engines for global hooking. Dependency Properties/Objects are only small part of those engines. Today, we'll look into Routed Events and EventManager

Let's take a scenario, where I want to handle OnClosing event of all windows in my application. The propose is clear - popup an exclamation asking me to approve this action.

The "legacy" way is to create kind of BaseWindow, derived from Window Class and override onClosing event to popup my message. This approach means, that I have to inherit BaseWindow in all windows or instead of creation of Window instance, create Basewindow one.

But, I want to do it easily, and I'm in WPF world, so let's start new WPF project and put into App.xaml.cs class following lines

 

protected override void OnStartup(StartupEventArgs e)
        {
            EventManager.RegisterClassHandler(typeof(Window), Window.LoadedEvent, new RoutedEventHandler(WindowLoaded));
 
            base.OnStartup(e);
        }

Done. Now let's understand what we did. We register globally routed event Window.Loaded for each new instance of Window class, so each time this event will be fired (that not mention, if we have or will have an instance of window), EventManager will subscribe to it and execute WindowLoaded method. The rest is really simple. Let's implement WindowLoaded and subscribe the loaded instance to our message popping

 

void WindowLoaded(object sender, RoutedEventArgs e)
        {
            Window w = sender as Window;
            if (w != null)
            {
                w.Closing += new System.ComponentModel.CancelEventHandler(w_Closing);
            }
        }
 
void w_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (MessageBox.Show("Really [with EventManager]?", "Workaround window", MessageBoxButton.YesNo, MessageBoxImage.Exclamation) == MessageBoxResult.No)
                e.Cancel = true;
        }

That's all, folks. Now each window (or window derived class) in my system, even those how I do not know will ask me about closing each time, I'll try to close them.

Nice, don't you think?

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/how-to-set-your-mobile-modem-to-work-without-dialer/]


So, all those happy owners of Orange GSM modem Novatel Merlin U740 and Windows Vista already paid their attention to bugs of it's software dialer MobiLink. Someone called to Orange customer service to get answers, and understand, that their are not officially support Windows Vista. Someone get new drivers (via YouSendIt.com - please someone tell my why Orange can not just put those drivers to their website). But even the new version of Mobilink is really junk

I believe, that  most of you have problem while entering Stand by or Hibernate mode after using the MobiLink dialer, while the dialer can not  properly close itself at all this what you see (after the dialer close) in process manager

So, how to solve it? How to set your modem work with access points you want without using their dialer. No problem. Let's do it.

All we have to do is to create two regular dialers for two possible orange 3G access point - UInternet and UWap (as well as configured in your mobile phone). But before creating a dialer let's put all setting inside the modem? How it's possible? Novatel Merlin U740 is very good hardware modem. It has it's own memory able to save upto 100 network locations.

So, open HyperTerminal (oh, my godness, there is not hyper terminal in Windows Vista, so open Putty, IMHO, the best open source terminal, SSH, and telnet client) and connect the COM port your modem sits in. Obvious, that you should have you modem in and connected (see in phone and modem options what com port it sits on)

Now in terminal windows type "at+cgdcont=?" (without quotes) to get your modem capabilities. You can see, that you have 1 upto 99 "IP" locations. Let's use slot one and two. Don't you want check what the modem has saved before usage? Sure, type "at+cgdcont?" to get this information.

Now let's put our location in slot 1 and 2. Type (case sensitive, two separate lines):
at+cgdcont=1,"IP","uinternet";
at+cgdcont=2,"IP","uwap.orange.co.il";

Done, now you have UINTERNET access point configures in slot 1 and UWAP in slot 2. Check this by typing "at+cgdcont?" (no quotes). If you see following - you did it!

The only thing we have to do is to tell your dialer use first or second (or both) slot. To do it add to default service center phone number ***X#, where X - your slot number. So to use UWAP, enter "*99***2#" (without quotes).  Please remember to have you modem in and selected for this connection while setting it.

Dunno. Don't forget to set proxy server (192.118.11.55:8080) in IE's Internet Options for UWAP AP. For UInternet you don't need to do it.

That's all folks, now plug your modem in and dial the required connection by using Windows Vista Connection Manager and forget the buggy MobiLink.

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


Today sales manager from the distributor store called me to notice, that I can come there and get my upgrade to Windows Vista, arrived from Dell. The only thing he mentioned in our conversation is fee of $45. I asked him what I'm paying for, and he told me that is's for shipping and handling. The same sentence, I found in Dell Vista Upgrade FAQ web site, but if I'll come to take it, it's seemed him, that I should not pay anything. 

A minute later, he called me back, and told, that I should pay for it anyway. After my request to explain he told me, that those $45 actually they'll pay to Microsoft and don't earn a penny for it. So he do not really know what I should pay for. 

The upgrade price for Windows Vista Home Basic is around $100, complete package costs around $200 (including shipping and handling), so the question is what's for I'm going to pay a half of regular licence price, when my system is eligible and the licence for Windows XP Pro SP2 already paid? I'll check this next Sunday.

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/text-length-measurement-its-really-easy-with-wpf/]


Don't you remember how it was to measure the length of your text in pixels? Do you really remember methods named GetLineFromCharIndex, Bounds and MeasureText?

Let's do it in WPF:

TextBox myText = new TextBox();
Rect textRext = myText.GetRectFromCharacterIndex(myText.Text.Length);

That's all, folks. Now you have the boundaries of your text in pixels. You can even measure the size of text up to your cursor position by using CaretIndex property of TextBox. Don't it really easy?

 

<Window x:Class="TextMeasurement.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:m="clr-namespace:TextMeasurement"
        Name="myWindow" 
    Title="TextMeasurement" Height="300" Width="300"
    >
  <StackPanel>
    <StackPanel Orientation="Horizontal">
      <TextBlock>The text length is:  </TextBlock>
      <TextBlock Name="mySize" Text="{Binding ElementName=myWindow, Path=TextSize}"/>
    </StackPanel>
    <TextBox Name="myText" TextChanged="onChanged"/>
  </StackPanel>
</Window>

 

public partial class Window1 : System.Windows.Window
{
    public static readonly DependencyPropertyKey TextSizePropertyKey = DependencyProperty.RegisterReadOnly("TextSize",
        typeof(double), typeof(Window1),
        new UIPropertyMetadata((double)0));
    public static DependencyProperty TextSizeProperty = TextSizePropertyKey.DependencyProperty;
    public double TextSize
    {
        get { return (double)GetValue(TextSizeProperty); }
    }
 
 
    public Window1()
    {
        InitializeComponent();
 
    }
 
    void onChanged(object sender, TextChangedEventArgs e)
    {
        Rect textRext = myText.GetRectFromCharacterIndex(myText.Text.Length);
 
        SetValue(TextSizePropertyKey, textRext.Right);
    }
 
}
 
Have a nice day with WPF :)

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/how-to-use-ie-authentication-dialog-window-programmatically/]


So, there are two methods deals with credential dialog of IE

CredUIPromptForCredentials – for pop it up and CredUIConfirmCredentials for persist those settings in password manager (optional). So, in order to call it explicitly, you should implement them via P/Invoke like this

[DllImport("credui.dll", EntryPoint = "CredUIConfirmCredentialsW", CharSet = CharSet.Unicode)]

       private static extern CredUIReturnCodes CredUIConfirmCredentials(string targetName, [MarshalAs(UnmanagedType.Bool)] bool confirm);

[DllImport("credui.dll", EntryPoint = "CredUIPromptForCredentials", CharSet = CharSet.Ansi)]

       private static extern CredUIReturnCodes CredUIPromptForCredentials(ref CREDUI_INFO creditUR,

                                                                                                                       string targetName,

                                                                                                                       IntPtr reserved1,

                                                                                                                       int iError,

                                                                                                                       StringBuilder userName,

                                                                                                                       int maxUserName,

                                                                                                                       StringBuilder password,

                                                                                                                       int maxPassword,

                                                                                                                       [MarshalAs(UnmanagedType.Bool)] ref bool pfSave,

                                                                                                                       CREDUI_FLAGS flags);

Please pay attention, that you’ll need CREDI_INFO structure and CREDI_FLAG and CredUIReturnCodes enums, that those function using

public struct CREDUI_INFO

{

   public int cbSize;

   public IntPtr hwndParent;

   public string pszMessageText;

   public string pszCaptionText;

   public IntPtr hbmBanner;

}

[Flags]

enum CREDUI_FLAGS

{

   INCORRECT_PASSWORD = 0x1,

   DO_NOT_PERSIST = 0x2,

   REQUEST_ADMINISTRATOR = 0x4,

   EXCLUDE_CERTIFICATES = 0x8,

   REQUIRE_CERTIFICATE = 0x10,

   SHOW_SAVE_CHECK_BOX = 0x40,

   ALWAYS_SHOW_UI = 0x80,

   REQUIRE_SMARTCARD = 0x100,

   PASSWORD_ONLY_OK = 0x200,

   VALIDATE_USERNAME = 0x400,

   COMPLETE_USERNAME = 0x800,

   PERSIST = 0x1000,

   SERVER_CREDENTIAL = 0x4000,

   EXPECT_CONFIRMATION = 0x20000,

   GENERIC_CREDENTIALS = 0x40000,

   USERNAME_TARGET_CREDENTIALS = 0x80000,

   KEEP_USERNAME = 0x100000,

}

public enum CredUIReturnCodes

{

   NO_ERROR = 0,

   ERROR_CANCELLED = 1223,

   ERROR_NO_SUCH_LOGON_SESSION = 1312,

   ERROR_NOT_FOUND = 1168,

   ERROR_INVALID_ACCOUNT_NAME = 1315,

   ERROR_INSUFFICIENT_BUFFER = 122,

   ERROR_INVALID_PARAMETER = 87,

   ERROR_INVALID_FLAGS = 1004,

}

Now, when you have those calls, you may want to make some wrappers (like those)

const int MAX_USER_NAME = 100;

const int MAX_PASSWORD = 100;

const int MAX_DOMAIN = 100;

static CredUIReturnCodes PromptForCredentials(ref CREDUI_INFO creditUI, string targetName, int netError, ref string userName, ref string password, ref bool save, CREDUI_FLAGS flags)

{

                         StringBuilder user = new StringBuilder(MAX_USER_NAME);

                         StringBuilder pwd = new StringBuilder(MAX_PASSWORD);

                         creditUI.cbSize = Marshal.SizeOf(creditUI);

                         CredUIReturnCodes result = CredUIPromptForCredentials(ref creditUI, targetName,IntPtr.Zero, netError,user, MAX_USER_NAME, pwd, MAX_PASSWORD, ref save, flags);

                                                  userName = user.ToString();

                                                  password = pwd.ToString();

                         return result;

}

CredUIReturnCodes AskCredit(ref string username, ref string password)

{

                         string host = url.Host;

                         CREDUI_INFO info = new CREDUI_INFO();

                         info.pszCaptionText = host;

                         info.pszMessageText = "The server "+host+" requires a username and password. \r\n\r\n\r\nWarning: This server is requesting that your username and password be sent in an insecure manner (basic                                                                            authentication without a secure connection).";

                         CREDUI_FLAGS flags = CREDUI_FLAGS.GENERIC_CREDENTIALS |

                                                  CREDUI_FLAGS.SHOW_SAVE_CHECK_BOX |

                                                  CREDUI_FLAGS.ALWAYS_SHOW_UI |

                                                  CREDUI_FLAGS.EXPECT_CONFIRMATION;

                         bool savePwd = false;

                         CredUIReturnCodes result = PromptForCredentials(ref info, host, 0, ref username,

                         ref password, ref savePwd, flags);

return result;

}

After it, you should call AskCredit to get the credit window pop up and get secured username and password from them. Something like this

CredUIReturnCodes code = AskCredit(ref user, ref pwd);

                         if (code == CredUIReturnCodes.NO_ERROR)

                         {

//Do something useful

                         }

The next part is to provide those credentials to your HTTPRequest. See the sample doing that

//create some string to get response into

string str = string.Empty;

HttpWebRequest req = HttpWebRequest.Create(url) as HttpWebRequest;

HttpWebResponse res = null;

//while you don’t OK keep trying

while (res == null || res.StatusCode != HttpStatusCode.OK)

{

                         try

                         {

//you are fine, do the rest

                         res = req.GetResponse() as HttpWebResponse;

                         using (Stream s = res.GetResponseStream())

                         {

                                                  using (StreamReader sr = new StreamReader(s))

                                                  {

                                                                           str = sr.ReadToEnd();

                                                                           sr.Close();

                                                  }

                         s.Close();

                         }

                         res.Close();

                         }

//You got an exception

                         catch (WebException e)

{

//If this exeption is not security one, you have nothing to do, the only thing is, maybe, check you network connection

                         if (e.Status == WebExceptionStatus.ProtocolError)

                                                  {

                                                                           res = e.Response as HttpWebResponse;

//Now check what the problem and if it’s security, threat it

                                                                           if (res.StatusCode == HttpStatusCode.Unauthorized)

                                                                           {

                                                                                                    string user = "";

                                                                                                    string pwd = "";

//pop you window and get information

                                                                                                    CredUIReturnCodes code = AskCredit(ref user, ref pwd);

                                                                                                    if (code == CredUIReturnCodes.NO_ERROR)

                                                                                                    {

//create new credential set and use it as required

                                                                                                                             CredentialCache creds = new CredentialCache();

                                                                                                                             creds.Add(url, "Basic", new NetworkCredential(user,pwd));

                                                                                                    //creds.Add(url, "Digest", new NetworkCredential(user, pwd));

                                                                                                    //creds.Add(url, "Negotiate", CredentialCache.DefaultNetworkCredentials);

                                                                                                                             req.Credentials = creds;

                                                                                                    }

                                                                           }

                                                  }

                         }

}

Well done. Now you have an application, that pops Internet Explorer authentication window and optionally saves your passwords into security password store within the browser

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/israel-its-beside-our-propose-the-dell-story/]


I got my new Dell Latitude D820 a couple of month ago (after the announcement about Dell's Express Upgrade to Windows Vista. Right after I got it, I checked the site with the computer service tag to be sure eligible for upgrade program. So, I was. Working fluently with my new Dell with Beta and RTM versions (first internals and then MSDN) I was really happy (the first thing I did, after purchase is to format disks with pre-installed Windows XP Pro SP2, which I paid for). So, some way after the official announcement about Windows Vista release, I called the retailer, I purchased the laptop to ask about upgrade. The first answer was: "We do not know anything about it, please contact local Dell office for additional information".

I called them and, sure, was redirected to distributor. I sent a message (using my google account) to Dell's local office and wait for about a month for them to reply, that I never got. So, after another call, I sent another mail (this time using my work address in Microsoft) with the same question. Hurrah!, I got an answer from Shlomy Quarter from Dell as following (hebrew):

 

תמיר שלום,

מדיניות השדרוג אינה חלה על ישראל בשלב זה, מכיוון שהמידע לא הגיע אליך במעמד הרכישה חברת אומניטק תשדרג לך תוך כדי חיוב בדמי המשלוח בלבד כפי שמתוארים באתר.

אנא פנה אל מאיר או עמית בחברת אומניטק בנושא.

Best Regards,

                   Shlomy

Wow, why you, Shlomo, did not sent me the same email to @gmail.com account? The regular client, paid for new Dell computer is not important enough? The client should come from big company to get any response for his after-purchase query?

A couple of hours after this email, I checked another time my Dell service tag for eligibility of the upgrade. Sure it was not, but I have a screenshot a month ago, where the same service tag was eligible. Strange, don't you think? Another computer, purchased earlier in US is still eligible system for this upgrade, I tell you more, I got a disk with an upgrade to my Israel postal address about two weeks after global lunch of Windows Vista.

So, according Dell, all those who purchased new Dell computer here, is Israel are really different customers, then all those who purchased in USA? Don't this seemed a bit strange, or, maybe Dell is really different here? Don't them? All of us are beside there propose, or not?

P.S. I'm really do not need this upgrade, due? I have both internals and MSDN versions, but the Principe is matter...

UPD: Here the article from Real Tech News regarding very similar behaviour, but in US.

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/uac-strikes-again-flash-fix-for-ie/]


Until recently, we all saw flash content from Vista in IE, however about a week ago we do not. What's the problem? The new version of Macromedia Flash Player, that do not leaves well with Vista's User Account Control (UAC). So how to fix it?

Great thank to Frank, that investigated the problem and found the solution. You should authorize flash installer to run with elevated rights. How to do it?

Go to "Your Windows Directory\System32\Macromed\Flash", right click FlashUtil9b.exe and select "Run as administrator". The program will install new flash player with elevated security settings.

How you can see any flash content. So, what do you think, UAC still good or bad for Jews?

More Posts Next page »