Welcome to WindowsClient.net | Sign in | Join
in

WindowsClient.net

This Blog

Syndication

Sponsors





Tags

No tags have been created or used yet.

Archives

Bragi

  • Neural networks

    For those of you who were wondering what ever happened to me, don't worry, I did not drop of the face of the earth.  I simply was extremely busy working on a new project. In light of this new endeavor, I probably wont be posting many WPF related entries on this blog any longer. I do have a feeling that the new blog might get some interesting posts in the future.

  • Pressure dependent stroke transparency for InkCanvas

    I got my shot of TNF blockers for crohn's again yesterday, so I'm a bit on a high right now, getting a lot of stuff done. One of those things was implementing an input system based on the InkCanvas for one of my other projects. Now, I am by no means a graphical kind of guy, but I do have this pen tablet thingie lying around which has pressure sensitive input and is just good fun to play around with.  Linking the pressure to other variables can get you some cool results.

    And as you would expect, WPF's InkCanvas also supports pressure sensitive input devices. All be it not completely how I had expected.  In most packages, it is possible to alter the transparency of the stroke through the pressure exercised on the stylus, which the WPF version doesn't do.  Bugger.

    Luckily, this turns out to be dead simple to implement for ourselves. InkCanvas has a protected property OnStrokeCollected which is called whenever a new stroke is added. If you override it, you can change the values of the stroke, like it's color which we will assign a new alpha value based on the average pressure of each point in the stroke.

    /// <summary>
    /// Raises the <see cref="E:System.Windows.Controls.InkCanvas.StrokeCollected"/> 
    /// event. /// </summary> /// <param name="e">The event data.</param> protected override void OnStrokeCollected(InkCanvasStrokeCollectedEventArgs e) { if (IsPressuredTransparency == true) { float iPressure = (from i in e.Stroke.StylusPoints
    select i.PressureFactor).Average(); Color iTemp = e.Stroke.DrawingAttributes.Color; iTemp.A = (byte)((float)byte.MaxValue * iPressure); e.Stroke.DrawingAttributes.Color = iTemp; } base.OnStrokeCollected(e); }

    Not much special to report here.  Kirupa.com provided me the info on how to get the pressure data out of a stroke. The article also demonstrates a lot of other features of the InkCanvas.  It definitely helped me to get started. In this implementation though, I used a linq statement to get the average value. The color is copied over to a temporary variable cause the color appears to be locked once it has been assigned to the drawing attributes.

    Off course, life is never as simple as that, so there is one more little issue to handle. Apparently, the InkCanvas uses different renderers for the final result compared to while the strokes are being drawn.   To show a transparency based on the pressure while the drawing action is still in progress, we need to use the protected property called DyamicRenderer which gets/sets the object used to render the strokes on a drawing context while the stroke is being drawn. This rendering object must be a descendent of DynamicRenderer. All you need to do here is override the OnDraw method and change the brush that is used. When you assign a new value to this property, the InkCanvas actually changes an internal 'PlugIn list' which is called whenever data is entered using the stylus.  In fact, all UI elements have this plug in list.  They use it to recognize gestures.  We on the other hand, use it to change the rendering behavior of the ink canvas.

    protected override void OnDraw(DrawingContext drawingContext, 
       StylusPointCollection stylusPoints, Geometry geometry, Brush fillBrush)
    {
       SolidColorBrush iBrush = fillBrush as SolidColorBrush;
       if (iBrush != null)
       {
          float iPressure = (from i in stylusPoints 
                             select i.PressureFactor).Average();
          Color iTemp = iBrush.Color;
          //can't directly change the A of a color that is assigned 
          //to somehting
          iTemp.A = (byte)((float)byte.MaxValue * iPressure);                                          
          base.OnDraw(drawingContext, stylusPoints, geometry, 
             new SolidColorBrush(iTemp));
       }
       else
          base.OnDraw(drawingContext, stylusPoints, geometry, fillBrush);
    }

    Again, not much going on here. The class can only change the alpha channel if a solid color is used to fill the stroke, if this is not the case, only the base method is called. Otherwise, we calculate the average pressure of the stylus points in the same way as the previous class.  The only difference is that this function only draws a few points instead of the entire stroke.

    So to recap, we need to override 2 classes to add pressure sensitive stroke transparency functionality:

    • A render class in which we are going to change the brush of the stroke to render, based on the pressure value assigned to the points that are being rendered.
    • A descendent of InkCanvas that uses this new render class and which changes the brush of the final stroke.

    The full implementation of the 2 classes can be downloaded from here. To use it, replace your InkCanvas with the TransparentInkCanvas and set it's IsPressuredTransparency to true. Here's a small screenshot of the end result:

    image

  • Using settings in WPF (or how to store/retrieve window pos and loc)

    One of those questions that comes up once and a while on the MSDN forum is how to properly use the project settings (that you can easily define in the VS designer) from xaml. A common situation is to store and retrieve the location and size of the main window at startup and closing of the app. This can all be done from xaml using bindings without having to write a line of code (actually, that's not entirely true, you still need 1 line). Here's a small example:

    <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:self="clr-namespace:JaStDev.CQuenze"
            x:Class="JaStDev.CQuenze.Window1"
            Title="CQuenze" 
            Height="{Binding Source={x:Static self:Properties.Settings.Default}, 
                             Path=ApplicationHeight, Mode=TwoWay}"
            Width="{Binding Source={x:Static self:Properties.Settings.Default}, 
                            Path=ApplicationWidth, Mode=TwoWay}"
            Top ="{Binding Source={x:Static self:Properties.Settings.Default}, 
                           Path=ApplicationTop, Mode=TwoWay}"
            Left ="{Binding Source={x:Static self:Properties.Settings.Default}, 
                            Path=ApplicationLeft, Mode=TwoWay}"
            WindowState="{Binding Source={x:Static self:Properties.Settings.Default}, 
                                  Path=ApplicationWindowState, Mode=TwoWay}">

    The basic idea is very simple.  I have created 5 setting entries called ApplicationWidth, ApplicationHeight, ApplicationTop, ApplicationLeft (all doubles) and ApplicationWindowState (System.Windows.WindowState). Since the settings can be reached in code through properties on a static class (Properties.Settings.Default), you can also bind to them using Source={x:Static self:Properties.Settings.Default}. Also notice that I have declared Mode=TwoWay on all bindings to make certain that changes to the position of the main window are automatically stored in the settings.

    And finally, we need to save the settings to disk when the user closes the application.  This is done by with the following statement:

    Properties.Settings.Default.Save();

    Usually, you call this in the event handler for the Window.Closed or Application.Exit event. This depends on your taste and the way you close your application. Personally, I find the Application.Exit the most appropriate.

  • Stretch the content of the ListView's cells

    A short time ago, I came across an interesting question on the MSDN forum.  The person in question had a ListView using a GridView as it's view with a couple of columns defined which use DataTemplates. One of those templates contained a control that needed to be stretched out across the entire width of the cell that it occupied. This seems like a trivial task for a common situation. Which it is in the end, you just have to know how to do it. To illustrate the situation, I have included a small code sample.

    <Window.Resources>
       <DataTemplate x:Key="cellTemplatePreviewPlayable">
          <ProgressBar  Height="14" Value="30" Maximum="100"/>
       </DataTemplate>
    
    </Window.Resources>
    
    <ListView x:Name="listView" ItemsSource="{Binding}">
       <ListView.View>
          <GridView>
             <GridViewColumn Header="Artist" 
                             DisplayMemberBinding="{Binding Path=Artist}" />
             <GridViewColumn Header="Title" 
                             DisplayMemberBinding="{Binding Path=Title}" />
             <GridViewColumn Header="Genre" 
                             DisplayMemberBinding="{Binding Path=Genre}" />
             <GridViewColumn Header="Listen" 
                CellTemplate="{StaticResource cellTemplatePreviewPlayable}"/>
          </GridView>
       </ListView.View>
    </ListView>

    I first came across a similar situation some time ago, and I must admit, it took some time, and a little bit of luck to find the proper solution, which is simply done using a custom style applied to the ListViewItems used as containers by the ListView. Here's the same example, but updated so that all items are stretched out.

    <Window.Resources>
    
       <Style TargetType="{x:Type ListViewItem}" x:Key="ContainerStyle">
          <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
       </Style>
       <DataTemplate x:Key="cellTemplatePreviewPlayable">
          <ProgressBar  Height="14" Value="30" Maximum="100"/>
       </DataTemplate>
    
    </Window.Resources>
    
    <ListView x:Name="listView" ItemsSource="{Binding}"
                   ItemContainerStyle="{StaticResource ContainerStyle}">
       <ListView.View>
          <GridView>
             <GridViewColumn Header="Artist" 
                             DisplayMemberBinding="{Binding Path=Artist}" />
             <GridViewColumn Header="Title" 
                             DisplayMemberBinding="{Binding Path=Title}" />
             <GridViewColumn Header="Genre" 
                             DisplayMemberBinding="{Binding Path=Genre}" />
             <GridViewColumn Header="Listen" 
                 CellTemplate="{StaticResource cellTemplatePreviewPlayable}"/>
          </GridView>
       </ListView.View>
    </ListView>

    As you can see, all it takes is a style that sets the HorizontalContentAlignement to Stretch. At first, this seemed a little bit illogical to me, since a ListViewItem contains the entire data object while each cell in the GridView displays only a part of the content in a separate object. If you only go by the name of the property, you'd expect it to control the layout of the entire content, while actually it controls the layout of each data item separately.  Having said that, a ListViewItem combined with a GridView doesn't actually have a content to lay out, this is done by the GridView and to know how it lays out all of the columns, it uses the properties of the ListViewItem, and then it all starts to make sense, at least to me.  I hope for you to.

    Of course, when you use this style, the cells in 'all' columns are stretched out, so if you need another alignment in another column, you will have to wrap your the content of your template inside another object which defines the correct alignment.  Here's the another DataTemplate that does exactly this.

    <DataTemplate x:Key="cellTemplatePreview">
       <Border>
          <ProgressBar HorizontalAlignment="Left"  
                       Height="14" Value="30" Maximum="100"/>
       </Border>
    </DataTemplate>
  • DistributionPanel

    Intro

    Let me start by telling you that this post was actually written about 40 days ago.  Non finished code kept me from posting it, which I found a real shame. Especially, since the technique it is based on got me very excited. But I simply couldn't get me to post some code, I hadn't even been able to test yet, which I am sure you understand.  So what exactly got me so excited anyway?  Well, simply put: this.  It's an article from Dr WPF about a new panel he made called 'ConceptualPanel' which  detaches the items in the panel from it's visual tree so that they can be placed somewhere else. When I read this, I had just started looking into a similar feature (well, actually I had already begun coding it for a couple of days), but had approached my problem from a different angle: I presumed that Panels couldn't be changed in the way Dr WPF did (thank you for proving me wrong on this one), so I figured to re-implement most of the ItemsControl features but in such a way that you could send them to multiple panels.  This proved to be a nightmare to put it mildly due to the many internal functions used by the ItemsControl. All situations could be solved, but not without breaking with the logic used by the .net framework. So this article really came as a blessing and saved my but.

    What does DistributionPanel do?

    First of though: What really is the importance of this conceptual panel?  Well, from my point of view it all becomes very interesting when you start using this panel with an ItemsControl.  You see, in the past, whatever you put on an ItemsControl, had to remain in its visual tree (in other words, inside the ItemsControl) because this uses a Panel to perform it's layout and those automatically made all their Children also visual children.  This is no longer true, you can now put the containers anywhere you want, even on another window.  So the ItemsControl simply becomes a factory of container elements.  Of course, ConceptualPanel only gives you the detaching, it doesn't actually provide a way to use multiple destinations for it's children (which is the feature that I was looking for). As you probably already guessed by know, this is where DistributionPanel steps in.  It expands the ConceptualPanel so that it can distribute it's children to other lists without using code (so you can do it all from xaml).

    Yes, you read it correctly, and it isn't a typo or expression error (something I can't always claim to be true), DistributionPanel can add it's children to other lists through the IList interface - not just to Panels.  It can also remove them again when the child is removed.  Using the IList interface instead of Panels can be useful in cases where you want to put the items on something different, for instance, you can create a class that makes all items in the list floating. And since the UIElementCollection used by the Panel also implements IList, you can still use panels as distribution targets.

    Key and Targets

    Distribution is performed by using a Key and a list of Targets.  The key is assigned to each item in the list using an attached property. Each DistributionTarget declares a value that is compared to the key of each item that needs to be distributed.  The first target who's value matches with the key will be used to put the item in.  This is done by adding the item to an IList that can be found through the Target property of the DistributionTarget.

    Ouch, sounds complicated.  Actually it really isn't.  I think a code sample is appropriate by know to clear things up.

    <DockPanel>
       <jc:DistributionPanel>
          <jc:
    DistributionPanel.Targets> <jc:DistributionTarget Value="Top"
    Target="{Binding ElementName=PART_Top, Path=Children}" /> <jc:DistributionTarget Value="Left"
    Target="{Binding ElementName=PART_Left, Path=Children}"/> <jc:DistributionTarget Value="Right"
    Target="{Binding ElementName=PART_Right, Path=Children}"/> <jc:DistributionTarget Value="Bottom"
    Target="{Binding ElementName=PART_Bottom, Path=Items}"/> </jc:DistributionPanel.Targets> <TextBlock jc:DistributionPanel.Key="Bottom">Item A</TextBlock> <TextBlock jc:DistributionPanel.Key="Top">Item B</TextBlock> <TextBlock jc:DistributionPanel.Key="Left">Item C</TextBlock> <TextBlock jc:DistributionPanel.Key="Right">Item D</TextBlock> </jc:DistributionPanel> <StackPanel DockPanel.Dock="Top" x:Name="PART_Top"/> <ListBox DockPanel.Dock="Bottom" x:Name="PART_Bottom" /> <WrapPanel DockPanel.Dock="Left" x:Name="PART_Left"/> <self:CustomList/> </DockPanel>

    Basically, this is a DockPanel which has a stackPanel at the top, a listbox in the bottom, a WrapPanel to the left and some custom list object in the middle.  The DistributionPanel contains all the items which will be distributed to all the other items found in the DockPanel. Each item in the list has an attached property DistributionPanel.Key.  The DistributionPanel also has a list of targets. The first one will receive all the items in the distributionPanel who's key value equals "Top" and sends the items to the StackPanel at the bottom of the app.  The second target filters out all the items which have a Key value of 'Left', and so on.

    Uses

    Even though the distribution technique appears very simple, you can get some really cool results, especially if you combine it with the triggers. Lets say there are 2 properties that determine where an item needs to be placed.  You can use a DataTrigger that checks the value of these 2 properties and use a setter to assign the correct value to DistributionPanel.Key.

    As already mentioned, this class is probably most useful when used as the ItemsPanel of an ItemsControl (such as a ListBox or TreeView).  This way, you can easily detach the items in the ItemsControl and put them somewhere else, like during a drag operation.

    Extending/changing the distribution

    DistributionPanel has been designed with extensibility in mind.  Interesting  new features could be: support for animation during a transition of one distribution location to another one, or you could implement a placeholder kind of functionality where the panel is able to put an item back in it's original location (The current implementation of DistributionPanel looses the order of items when it moves items from one list to another).

    To extend the class, you should overwrite the protected method Distribute, which is called whenever an item is added, removed or moved from one target to another. So, if you would want to add animation functionality, this is the place to do it. All that the method does is raise the appropriate events and ask the DistributionTarget objects to add/remove the item.  Here's the implementation:

    protected virtual void Distribute(UIElement item, DistributionTarget oldTarget,
             DistributionTarget newTarget)
    {
       //raise the preview event
       DistributionEventArgs iArgs = RaisePreviewDistributedEvent(item, oldTarget, newTarget);
       //do some sanity check: if there is an oldTarget, it should be in it's list, otherwise
       //we shouldn't be able to remove it.
       //Also, if there is an oldTarget, ask it to remove the item.
       if (oldTarget != null)
       {
          if (oldTarget.Items.Contains(item) == true)
             oldTarget.RemoveChildFromTarget(item);
          else
             throw new DistributionException("Invalid old key!", new ArgumentOutOfRangeException("oldTarget"));
       }
       //if there is a new target, ask it to add the item.
       if (newTarget != null)
          newTarget.AddChildToTarget(item);
       //and finally raise the event indicating that the distribution has completed.
       RaiseDistributedEvent(iArgs);
    }

    For animation, you will probably have to re-implement this method, that is, if you want the Distributed event to be raised after the animation is done. The panel also has the Redistribute (called only when an item is moved from one location to another), RebuildDistribution (recreates the entire distribution) and ClearDistributionFor (removes all the items from a specific target list) virtual methods that you can overwrite, but scenario's to overwrite these are not that abundant.

    Besides DistributionPanel itself, you can also modify the RemoveChildFromTarget and AddChildToTarget protected methods defined in the DistributionTarget class. These methods do the actual adding and removing from the target lists.  Here are the default implementations:

    internal protected virtual void AddChildToTarget(UIElement item)
    {
       IList iList = Target;
       if (iList != null)
          iList.Add(item);
       //Items is a protected property that contains all the UIElements
       //assigned to this target.
       Items.Add(item);
    }
    internal protected virtual void RemoveChildFromTarget(UIElement item)
    {
       IList iList = Target;   
       if (iList != null)
          iList.Remove(item);
       //Items is a protected property that contains all the UIElements
       //assigned to this target.
       Items.Remove(item);
    }

    As you can see, nothing fancy, just a standard add or remove from the target list. The only thing worth noting is the second list: DistributionTarget also maintains an internal list containing all the items that it maintains.  This is used in case there is no target list assigned. So this list must also be kept in sync. You can easily extend these 2 functions by replacing the item with a dummy instead of removing it from the list, which allows you to put the item back at it's original location.

    It is my opinion that DistributionPanel provides the foundations for some interesting Controls. I hope you find good use for it, and I would be glad if you'd let me know about the cool stuff you did with it. The panel is part of my ControlFramework library, which you can download from here.

    And as a final note there are still the:

    Limitations and restrictions

    All the limitations of the ConceptualPanel on which it is based are also valid for this panel, so items can loose their order (especially when they are redistributed from one list to another, because items are always added to the back of the new target list) and if microsoft decides to change the internals of their  panels, this could brake. 

    In addition, you should keep in mind that a DistributionPanel can't be used in a cascade, so you can't send the content of one DistributionPanel to another one.  Other than that, you are free to do with it whatever you want.

  • HTML help from within WPF applications Part II

    Ok, so here's the follow-up I promised about integrating html help within WPF applications which will mostly be about the class's internals and it's methods. Most often, when I need to write smaller code units such as this one, I tend to just jump in (head first) and splurt out the code in one go.  This one however, was an exception. In fact, I was kind of reluctant at first to even write it.  You see, in a previous life, I once made a translation (or part) of the windows header files to another language, which was NO FUN. So I really wasn't looking forward to do this again for the help part. Needless to say I was very happy to find this resource on the web which is basically a complete port of the HTMLHelp api to C#.

    This low level library is  quite simple to use really.  It declares a bunch of statics and structs inside the 'HH1Interop' class, most of which you will probably never need or use. More importantly, the class also has a number of public methods which provide access to help files.  Some of which are:

    • HtmlHelp_DisplayTopic(int caller,String file): Displays a help file or a specific page of it. The first argument (caller) identifies the application that does the request.  This is used for moving focus when the help file is closed (the default implementation of the help class always sets this to 0). The 'file' argument should contain the path (or filename if it is in the same dir as the app) of the help file, followed by the name of the content page to display, using '::/' to separate file and page info. As an example, if I have an html file cold 'foo' with a page called 'bar' that I would like to display, the second argument for this method would be: "foo::/bar".
    • HtmlHelp_DisplayIndex(int caller,String file,String keyword): This method will display the index page of the help file (declared in the second argument), with the value for 'keyword' used to fill in the default search criteria. 'Caller' has the same meaning as for 'HtmlHelp_DisplayTopic'.
    • HtmlHelp_DisplaySearch(int caller,String file,ref HH_FTS_QUERY query): Displays the search page of the html file who's path is declared in the 'file' argument. 'Caller' again is the application that requests the operation and the final argument (query) can be used to declare a default search value. The Help class currently leaves the query arg empty.
    • HtmlHelp_DisplayTOC(int caller,String file): This method shows the table of contents page of the specified help file.

    Using these functions it is possible to construct a class that provides basic help access from within WPF.  There are more methods declared in the raw, low level HH1Interop class such as for showing popups, but these are currently not used by the Help class, so I wont go into these any further.

    The internal structure of the Help class can now actually be split up into 2 main parts:

    1. Some properties for assigning help info to UI elements and to assign the help file. By using attached properties, we can declare help info from xaml. The different properties have already been explained in my previous post.  There is nothing more to say really about them, they are simple attached properties with no event handlers.  The only thing to note is that both Topic and HelpFile are declared using the "FrameworkPropertyMetadataOptions.Inherits" argument value.
    2. Some functions for displaying the help content by properly calling into the low level api. These are there to make certain that all the Help class's properties are used correctly. They make certain that the correct help file is used (the default or a custom defined one) and they format the topic string.  For each method in the 'HH1Interop' that I described, there is a corresponding method in the help class + some extra methods:
      • ShowHelp(): displays the help file using the help file's default starting page.  This uses 'HH1Interop.HtmlHelp_DisplayTopic' internally to show the file.
      • ShowHelpFor(UIElement item): This method will look up the topic and help file that is assigned to the item (either directly or through property inheritance).  If both are found, it displays the topic using the 'HH1Interop.HtmlHelp_DisplayTopic' method.
      • ShowHelpTopic(string topicId): This method will show the specified topic found in the default help file, also using 'HH1Interop.HtmlHelp_DisplayTopic'.
      • ShowHelpContents(): Is used to show the table of contents page of the default help file. This is done using the 'HH1Interop.HtmlHelp_DisplayTOC'.
      • ShowHelpSearch(): Is used to show the search page of the default help file. Note that it is currently not possible to fill in a default search term, instead this is always left empty. This is done using the 'HH1Interop.HtmlHelp_DisplaySearch' method.
      • ShowHelpIndex(string start): Displays the index page, with the value for 'start' used as the initially selected index (use null if there shouldn't be a term filled in). This is done with the 'HH1Interop.HtmlHelp_DisplayIndex' method.

    There are many ways that this class can be extended.  For instance, there is no support for popups, nor for default search criteria and I am certain that seasoned help hackers know tons of stuff that's missing.  These are things I leave up to you, if you feel like it or have an itch to scratch.

  • HTML help from within WPF applications

    I don't know if you have noticed already, but the WPF framework doesn't contain any class for providing context sensitive help in a WPF application. I am not certain why this is, but I doubt that they simply forgot about it.  My (non educated, actually more like 'complete' ) guess: Vista was supposed to have included some new help system (based on XML I believe), which didn't ship in the end product.  I think the WPF designers wanted to use this, but when it got dropped, had no time to find a replacement.  If anyone knows the real reason why nothing was done in the help department, I would love to know about it.

    Anyway, A couple of months ago, I had a project that needed this feature, so I set about implementing my own version, based on the Help component provided for windows forms (for the methods at least). You can find the end result in my ControlFramework lib.

    Using the code

    There are basically 2 different operations that need to be done for incorporating context sensitive help.  You need to assign the help topics to your controls and you need to set up your application for displaying a help file. Here's a more detailed explanation:

    1. Setting up the application:
      1. add a Help control to your application (through a field (in the main window/application object) or as a resource in xaml) and assign it a default help file. ex:
         help.DefaultHelpFile = "help1.chm";

        Note: Thanks to Sam Jack for letting me know that some systems require a full path to the help file for it to display properly.  So if your help file is missing icons, check if you are using a relative or absolute path to the help file.

      2. Add a CommandBinding to the control handling help (usually the main window) for the Help command and provide the CanExecute and Executed events. ex:
        <CommandBinding Command="Help" CanExecute="HelpCanExecute" Executed="HelpExecuted"/>
      3. Implement the commandBinding's CanExecute event by checking if there is a control that has keyboard focus.
        private void HelpCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
        e.CanExecute = Keyboard.FocusedElement is UIElement;
        }
      4. Implement the commandBinding's Executed event by calling "Help.ShowHelpFor(Keyboard.FocusedElement as UIElement)".  This will show the help topic assigned to the control that currently has keyboard focus.
        void HelpExecuted(object aSender, ExecutedRoutedEventArgs e)
        {
        help.ShowHelpFor(Keyboard.FocusedElement as UIElement);
        }
    2. Assigning help info to your controls: This is done using attached properties
      1. Topic: This property will assign  a help topic to the control and all it's children.  The value should be the name of the html file that contains the help for the control. This property is required for all controls that need to display a help page.  Note that it is an inherited property, so if you have a single help page for a group of controls (such as a window or Page), you only need to assign the attached property 'Topic' to the container (aka the Window or Page), all other controls will inherit the value. Ex:
        <Button jc:Help.Topic="button.html">
        Button with help
        </Button>
      2. HelpFile: This is an optional attached property that you can use which provides a feature that wasn't found in the previous (WinForms) version of Help. It allows you to declare a help file to use other than the default one assigned to the Help control.  So you can use multiple help files from 1 application. I don't know if this is a very useful feature, but I was thinking in the direction of plug-ins: if your application can display custom xaml somehow, external writers will still be able to provide help for there work without having to hack into you help file or building up a custom help system.  they simply provide there own help file which will be displayed instead of the default one when help is activated on there xaml.  (noted disadvantage: you don't have a global help file, but multiple files).
        <TextBox jc:Help.Topic="textbox.html"
        jc:Help.HelpFile="Help2.chm">
        Text with help from other file
        </TextBox>

    That pretty much covers the basics of the Help control.  There are also some useful functions that work similar to those found in the WinForms version. You have 'ShowHelp()' to show the default help file and "ShowHelpTopic(string TopicID)" to display a specific topic (the id is the name of the html page).   I believe "ShowHelpContents()", "ShowHelpSearch()" and "ShowHelpIndex()" are pretty self explanatory.

    The Next entry will probably be about how I implemented all this (You'll be amazed how simple it all was thanks to Microsoft).

  • Prologue

    Well, I guess I have finally succeeded in setting up my blog, so I should probably start writing about stuff. Anyway, I am Belgian, so English isn't my native tongue.  If you find any grammar or typing error (I'm sure you already have), don't shoot me, blame the TV, which for years, was my primary English tutor.  These days, it's become the Internet, so no improvement there.

    I have a ton of interesting topics waiting to be written about, they just need to be pushed out of my head onto the keyboard, a hard task for someone who doesn't consider himself fluent in natural languages. So, if you believe a little push is needed, drop me a mail.

    Upcoming topics will be about some of the free controls I have created (you know, the usual stuff like the itch they scratched, how they should be used, techniques that accompany them,...).  First topics will probably be about help integration, static Treeview items combined with itemsSource, breadcrumbs,....

    Oh, before I forget, about me: I'm the lead developer at JaStDev, an upstart focused on WPF stuff, so the blog will be mostly about xaml.

Page view counter