April, 2006

  • Cloudy in Seattle

    Cider Item Creation

    • 5 Comments

    In Windows Forms, there were many occasions when you wanted to separate out your design time code from your runtime code.  For design time code you didn't want to execute at runtime, you would use a designer and for runtime code you didn't want to execute at design time (i.e. it did a bunch of database access) in most situations you could use the Control.DesignMode property.

    The problem with Control.DesignMode was that it depended on IComponent.Site being set for your control.  Unfortunately, there were cases when this wasn't true, most noteably in the constructor of that control.

    If you've seen Brian Pepin's articles on the Cider designer (part 1 here and part 2 here) you'll know that in Cider, we've gone away from IComponent.  This means that there isn't a Control.DesignMode property for WPF/Avalon controls.

    Instead, Cider's Extension architecture (again see Brian's articles) is used to add a CustomInstanceFactory to a control.  When Cider goes to instantiate that control, instead of calling "new" or "Activator.CreateInstance" it calls its CustomInstanceFactory instead.

    For example:

    [Extension(typeof(SliderAdornerInstanceFactory))]
    public class SliderAdornedButton : Button
    {
          private bool IsDesignMode = false;

          public bool SetIsDesignMode
          {
                
    get { return this.IsDesignMode; }
                
    set { this.IsDesignMode = value; }
          }
    }

    class SliderAdornerInstanceFactory : CustomInstanceFactory
    {

          public override object CreateInstance(Type type, params object[] arguments)
          
    {
                
    object instance = base.CreateInstance(type, arguments);
                
    SliderAdornedButton sab = instance as SliderAdornedButton;

                if (sab != null) sab.SetIsDesignMode = true;
                
    return instance; 
          
    }
    }

    In other words, at design time, if there is a Custom Instance Factory attached to an object, it is used to instantiate the object instead of the designer itself.  This extensibility point allows you to set a bool indicating that you are in design mode (as the sample code above shows) and do any other design time specific code.

    In fact, you could even return a design time specific instance of your control.  As an example, Cider currently uses this mechanism to provide our own design time version of Window since we can't use a real Window instance as it cannot be parented to another control.

    To summarize: Custom Instance Factories provide the ability to hook into the creation of your control at design time.

  • Cloudy in Seattle

    WPF Resources

    • 2 Comments

    I've been playing around and learning about WPF (Avalon) resources and ResourceDictionaries recently.  I had a few questions pop up that the knowledgeable people on the Avalon team graciously answered for me.  (Thanks to Rob Relyea, WeiBing Zhan, and Ashish Shetty)

    Here's a recap:

    1) I used to look at resources in reflector or ildasm, how can I see inside WPF resources?

    As it turns out, reflector is still the tool du jour!

    Figure 1: Reflector's view of WPF Resources

    2) What's the difference between:

    <ResourceDictionary Source="ResourceDictionary.xaml"/>

    and

    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="ResourceDictionary.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>

    The answer to this may seem obvious however the examples I had seen up to that point and the fact that I only had Chris Sells book to go on (which doesn't cover MergedDictionaries) left me wondering. 

    In both cases you end up with the same resources, the reason for the ResourceDictionary.MergedDictionaries property is to allow for this:

    <ResourceDictionary>
        <SolidColorBrush x:Key="myRed" Color="Red" Opacity="0.5"/>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="ResourceDictionary.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>

    Since the following is not valid:

    <Window.Resources>
        <ResourceDictionary Source="ResourceDictionary.xaml"/>
        <SolidColorBrush … />  <!—this solid color brush is not allowed
    </Window.Resources>

    3) What is the difference between setting the build action on a XAML file that only contains a ResourceDictionary to page and to resource?

    In other words, suppose you have a XAML file that contains only a ResourceDictionary – should you use the “Page”, “Resource” or “Embedded Resource” build action?

    You don’t want to use “Embedded Resource” – that simply doesn’t work. Specifying “Embedded Resource” puts the resource in the .mresource section of the assembly, these resources need to go in the g.resources section (which in turn is in the .mresource section).

    Most likely you’ll want to use the “Page” setting as it creates a .baml file, which adds compile time checking and improved runtime performance.

    You can use Reflector to inspect the generated resources:

    Figure 2: Build action "Page" generates a more performant BAML file.

    Figure 3: Build action "Resource" generates an embedded XAML file.

  • Cloudy in Seattle

    Cider Adorners

    • 2 Comments

    Following up on my previous article on Cider Item Creation, I want to talk a bit about Adorners.  Brian Pepin introduces adorners in his article on User Interaction in Cider:

    Adorners are WPF elements that float on top of the design surface and track the size and location of the elements they are adorning. All of the UI Cider presents to the user, including snap lines, grab handles and grid rulers, is composed of these adorners. Adorners can contain any control, and an adorner can be associated with a task so they can participate in Cider’s commanding mechanism. Adorners can also get focus and be treated just like normal WPF elements, because that’s what they are.

    For example, looking at the following screen shot:

    The grid lines, the anchor lines, the grid information, the grab handles...  all are done with adorners. 

    One of the cool things is that Cider uses the same infrastructure that we expose as extensibility points for control developers.  This allows us to work with and get validation of the extensibility points we ship to customers.

    So what does the code look like to write one of these things? 

    I'll go over the high level details around writing a control that has 2 adorners.  Note that if you want to try this at home, you'll have to wait for the next CTP.  I'll be blogging about getting this sample working with the CTP when it becomes available.

    In the screen shot below, the Slider is an adorner on the button as is the little square in the upper right hand corner.  The slider adjusts the yellow gradient stop to be the position where the slider is and the little square pops a dialog when clicked.

    1) The first step is to decorate your Control such that it knows that there is an attached adorner.

    [Extension(typeof(SliderAdornerProvider))]
    public class SliderAdornedButton : Button
    {
    }

    The Extension attribute is used to attach the SliderAdorner to the SliderAdornedButton class. 

    2) Write the SliderAdornerProvider

    The SliderAdornerProvider is an Extension.  So what's an Extension?  Well, essentially, when Cider loads up, it has an ExtensionManager which loads all of the Extensions found in metadata.  This is an extensibility point for adding new functionality into Cider.  They are lightweight features or add ins which don't require much from Cider and are created and destroyed within a given context

    Each group of Extensions have an ExtensionServer which manages its Extensions and can request and publish services, listen to events etc.  ExtensionServers exist for the lifetime of Cider and are also a Cider extensibility point. 

    In this example, because the SliderAdornerProvider derives from PrimarySelectionAdornerProvider which has an associated ExtensionServer (AdornerProviderExtensionServer), we don't need to write one. 

        class SliderAdornerProvider : PrimarySelectionAdornerProvider
        {
             . . . 
         }

    PrimarySelectionAdornerProvider is a Cider provided class that activates the adorner when the control that the adorner is attached to is the primary selection on the design surface.

    The bulk of the work is in the constructor which creates two adorners and puts them in the adorner layer.  The first is a Rectangle, the second is a Slider.  The first step is to create the AdornerPanel:

            public SliderAdornerProvider()
            {
                // All adorners are placed in an AdornerPanel
                // for sizing and layout support.
                AdornerPanel myPanel = new AdornerPanel();

    A Rectangle is instantiated, its position is set and its added to the AdornerPanel. 

                // 10 pixels Rectangle adorner
                Rectangle rect = new Rectangle();
                rect.Fill = Brushes.Azure;
                rect.Stroke = Brushes.Blue;
                rect.Width = rect.Height = 10.0;
     
                // Inset the slider 5 pixels from the top edge of the
                // control.
                AdornerPanel.SetAdornerOriginOffset(rect, new Vector(5, 5));
                myPanel.Children.Add(rect);

    We now add a Task that is fired when the Rectangle adorner is clicked:

                // Add a task to the Rectangle adorner that is
                // fired when we click on it.
                ToolCommand command = new ToolCommand("OpenPopup");
                Task task = new Task();
                task.InputBindings.Add(new InputBinding(command, new ToolGesture(ToolAction.Click, MouseButton.Left)));
                task.ToolCommandBindings.Add(new ToolCommandBinding(command, OnOpenPopup));
                AdornerPanel.SetTask(rect, task);

    Essentially, the OnOpenPopup method will be called when the Rectangle adorner is left clicked.

    Next up is the Slider Adorner:

               // adding a slider
                Slider slider = new Slider();
                slider.Minimum = 0;
                slider.Maximum = 1;
                AdornerPanel.SetHorizontalStretch(slider, AdornerStretch.Stretch);
                AdornerPanel.SetVerticalStretch(slider, AdornerStretch.None);
                AdornerPanel.SetTargetSizeFactor(slider, new Vector(1.0, 0));
                AdornerPanel.SetAdornerSizeFactor(slider, new Vector(0, 1.0));
                AdornerPanel.SetAdornerOriginFactor(slider, new Vector(0, -1.0));
                AdornerPanel.SetAdornerOriginOffset(slider, new Vector(0, -3));
                myPanel.Children.Add(slider);

    Here you'll notice 6 calls to AdornerPanel methods that are setting up the resizing and repositioning behavior of the adorner relative to the control it adorns.  We're still working on these APIs, however the key things to notice are that the Slider adorner will stretch horizontally to always be the same size as the control it adorns, it will not stretch vertically and its positioned above the control it adorns.

    Now when the slider values change, I also want to update the control that is being adorned:

                // handle the slide
                slider.ValueChanged += new RoutedPropertyChangedEventHandler<double>(slider_ValueChanged);

    Finally, add it to the Adorner layer:

                // Finally, add our panel to the Adorners collection
                Adorners.Add(myPanel);
           }

    The event handlers for the adorner events are pretty straight forward (yes, yes it creates a new LinearGradientBrush every time, that can and should be optimized out) :

            void slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
            {
                UIElement adornedControl = AdornerPanel.GetAdornedElement((UIElement)sender);
                SliderAdornedButton button = adornedControl as SliderAdornedButton;
                if (button != null)
                {
                    LinearGradientBrush gradBrush = new LinearGradientBrush();
                    gradBrush.StartPoint = new Point(0, 0);
                    gradBrush.EndPoint = new Point(1, 1);
                    gradBrush.GradientStops.Add(new GradientStop(Colors.Red, 0.0));
                    gradBrush.GradientStops.Add(new GradientStop(Colors.Yellow, e.NewValue));
                    gradBrush.GradientStops.Add(new GradientStop(Colors.Green, 1));

                    button.Background = gradBrush;
                }
            }

            private void OnOpenPopup(object sender, ExecutedToolEventArgs args)
            {
                MessageBox.Show("Hello Cider");
            }

    That's it!  Your first adorner.

    One of the things this sample highlights is that adorners can be any class that derives from UIElement.  There is no adorner base class.  It also shows the reuse opportunities for adorners as well as the flexibility they provide in terms of providing a customized design time experience for your control

    Obviously there are a lot of details and functionality that was glossed over, I'll be drilling down into various parts of our extensibility mechanisms in the coming weeks.  Stay tuned.

     

  • Cloudy in Seattle

    Collections of Items in Resources

    • 0 Comments
    Recently, I was trying to figure out how to reuse resources within multiple collections all defined in WPF XAML.  For example, suppose I have the following:
     
    <MeshGeometry3D.Positions>
        <Point3D X="0" Y="0" Z="0"/>
        <Point3D X="5" Y="0" Z="0"/>
        <Point3D X="0" Y="0" Z="5"/>
    </MeshGeometry3D.Positions>
     
    What if I wanted to put the 3 Point3D instances in a resource and use them to populate a collection?  Hmmm.... how about this?
     
    <Window.Resources>
        <Point3D x:Key="PointA" X="0" Y="0" Z="0"/>
        <Point3D x:Key="PointB" X="5" Y="0" Z="0"/>
        <Point3D x:Key="PointC" X="0" Y="0" Z="5"/>

    </Window.Resources>
     
    <MeshGeometry3D.Positions>
        {StaticResource PointA}
        {StaticResource PointB}
        {StaticResource PointC}
    </MeshGeometry3D.Positions>
     
    Sure enough, that doesn't work.  When you have something like this:
     
    <SomeElement>
       {Binding ... }
    </SomeElement>
     
    The "{Binding...}" string actually gets sent to the SomeElement type converter instead of being treated like a markup extension. 
     
    So as jfo pointed out to me, I can do this:
     
    <Window.Resources>
        <Point3DCollection x:Key="MyPoints">
            <Point3D X="0" Y="0" Z="0"/>
            <Point3D X="5" Y="0" Z="0"/>
            <Point3D X="0" Y="0" Z="5"/>
        </Point3DCollection>
    </Window.Resources>
     
    <MeshGeometry3D.Positions="{StaticResource MyPoints}">
     
    Ok, not bad, but now, what if I want to share those points on multiple resources (I know this is getting contrived but I can imagine there being a reason to want to do this to avoid duplication in some scenario), I can't go back and do this:
     
    <Window.Resources>
        <Point3D x:Key="PointA" X="0" Y="0" Z="0"/>
        <Point3D x:Key="PointB" X="5" Y="0" Z="0"/>
        <Point3D x:Key="PointC" X="0" Y="0" Z="5"/>

        <Point3DCollection x:Key="MyPoints">
              {StaticResource PointA}
              {StaticResource PointB}
              {StaticResource PointC}
        </Point3DCollection>
        <Point3DCollection x:Key="MyOtherPoints">
              {StaticResource PointA}
              {StaticResource PointC}
        </Point3DCollection>
    </Window.Resources>
     
    So how would I do this in such a way that I can avoid duplicating those Point3D values? 
     
    Well, off to Chuck I go.  His answer?  Element Syntax for Markup Extensions:
     
    <Window.Resources>
        <Point3D x:Key="First" X="0" Y="0" Z="0"/>
        <Point3D x:Key="Second" X="5" Y="0" Z="0"/>
        <Point3D x:Key="Third" X="0" Y="0" Z="5"/>
        <Point3DCollection x:Key="MyPoints">
            <StaticResource ResourceKey="First"/>
            <StaticResource ResourceKey="Second"/>
            <StaticResource ResourceKey="Third"/>
        </Point3DCollection>
        <Point3DCollection x:Key="MyOtherPoints">
            <StaticResource ResourceKey="First"/>
            <StaticResource ResourceKey="Third"/>
        </Point3DCollection>
    </Window.Resources>
     
    I thought that was pretty cool.  I wasn't aware of that syntax.  Very useful.  I've also seen it with Binding where this:
     
    <TextBlock FontSize="12" Foreground="Red" Text="{Binding XPath=@ISBN}"/>
     
    Is replaced by this:

    <TextBlock FontSize="12" Foreground="Red">
        <TextBlock.Text>
            <Binding XPath="@ISBN"/>
        </TextBlock.Text>
    </TextBlock>
    Cool and useful!
Page 1 of 1 (4 items)