New blog theme

by Administrator 28. November 2008 06:05

After some hideous CSS work I have deployed a new default theme for the blog.  I couldn't have done it without the help of CJ from CarSpot; I definately owe him a link on here as soon as I find the URL of his freelance site. 

The old design looked horrid in some browsers, mostly IE 6, 7, and 8 and Firefox 2 and 3...

Some older pages/huge images are absolutely breaking the layout still, but anything newer (posted with Live Writer using <pre> tags for code) should be working and scrolling code.  Please leave me a comment if you notice anything broken and I'll continue to tweak.

Tags:

Towards a Validation Framework for Silverlight

by Administrator 22. November 2008 05:03

Regular readers may observe that my interest in Silverlight is primarily in making business applications and Visual Tools. I am not an artist and I don't stream music or movies.  No, I'd just like to see Silverlight replace HTML, CSS, and Javascript.  There are a few issues with this plan right now (other than wish thinking) - binding ought to be closer to what WPF has.  There are some handy controls missing. 

There's no built in Validation strategy. 

Obviously nothing stops me from doing validation in a completely manual fashion but it's unlike Microsoft to leave holes like this...

Strategy

We have Extension Methods, but not Extension Properties or Extension Events, so simply adding Validating event handlers isn't an option.  What's more, the typical Windows Forms strategy requires an Extender Provider framework - perhaps when I get further on the AGT Project I'll revisit this, but for now something quicker to implement would be better.

For some reason the ASP.Net model was intriguing to me given the current state of Silverlight.

  1. The Validators have a visual representation and function essentially as a Control.
  2. The Validators "point to" the Control they will validate, so they are completely outside the rest of the functionality on the Page so to speak.
  3. Making sure to trigger Validation via code like if(Page.IsValid) { /* do something*/} is a fair enough way to write code.

So, could we create something like this, but in a reasonably Silverlight-y way? Read on.

Test Form and three sample validation scenarios

I have created a very simple Wine Order Form:

Form0

I have some simple data requirements for this order form.

  • For Name, I'd like something similar to the Asp.Net RequiredFieldValidator
  • For Email, I'd like a RegexValidator
  • For Bottles of Wine I'd like a RangeValidator

Design Goals

So, how would I like this to work and what should the programming experience be?

  • I'd like some sort of Control in the Visual Tree representing the Validation state of an associated Control; it should be easy to Template
  • I do not want to require any interface implementation, base classes, or any changes to the Visual Tree besides the addition of the Validation visuals. (ie no <v:ValidationArea>... your XAML here </v:ValidationArea>, although this is a common enough model in Silverlight and WPF)
  • Calling an arbitrary # of Validators on any given Control or Panel should require as little code as possible
  • Adding new Validator types should be as easy as possible.
  • It should feel like the Validators have saved programming time and created consistency versus pure manual validation.

 

Implementation

How did I do on these goals?  Let's see.

Validator Control Class

As I stated, I've taken the approach of creating a Control which will live in the Silverlight Visual Tree.   Any inheritance involving XAML & User Controls is painful or impossible, but I wanted to see about directly inheriting from Control and using a ControlTemplate instead of working at any kind of visual inheritance.  I'm happy with the results.

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace DamonPayne.AGContrib.Validation
{
    [TemplatePart(Name = "LayoutRoot", Type = typeof(Panel))]
    public class Validator : Control
    {
        public Validator()
        {
            DefaultStyleKey = typeof(Validator);
            Loaded += new RoutedEventHandler(Validator_Loaded);
        }

        private Panel _layoutRoot;

        void Validator_Loaded(object sender, RoutedEventArgs e)
        {
            CheckValidState(this);
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            _layoutRoot = (Panel)GetTemplateChild("LayoutRoot");
        }

        /// <summary>
        /// A single Framework Element to display when Valid, perhaps an empty TextBlock?
        /// </summary>
        public FrameworkElement ValidContent
        {
            get { return (FrameworkElement)GetValue(ValidContentProperty); }
            set { SetValue(ValidContentProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ValidContent.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ValidContentProperty =
            DependencyProperty.Register("ValidContent", typeof(FrameworkElement), typeof(Validator), new PropertyMetadata(new TextBlock()));



        public FrameworkElement InValidContent
        {
            get { return (FrameworkElement)GetValue(InValidContentProperty); }
            set { SetValue(InValidContentProperty, value); }
        }

        //Inheritance, does NOT like sharing visual elements, very sneaky
        public static readonly DependencyProperty InValidContentProperty =
            DependencyProperty.Register("InValidContent", typeof(FrameworkElement), 
            typeof(Validator), 
            new PropertyMetadata(new TextBlock { Text="*", Foreground=new SolidColorBrush(Colors.Red) }));



        /// <summary>
        /// Tool tip content, possibly explaining more information about why the value is invalid
        /// </summary>
        public FrameworkElement InvalidToolTip
        {
            get { return (FrameworkElement)GetValue(InvalidToolTipProperty); }
            set { SetValue(InvalidToolTipProperty, value); }
        }

        // Using a DependencyProperty as the backing store for InvalidToolTip.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty InvalidToolTipProperty =
            DependencyProperty.Register("InvalidToolTip", typeof(FrameworkElement), typeof(Validator), new PropertyMetadata(null));


        /// <summary>
        /// Is the target Control currently valid
        /// </summary>
        public bool IsValid
        {
            get { return (bool)GetValue(IsValidProperty); }
            set { SetValue(IsValidProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsValid.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsValidProperty =
            DependencyProperty.Register("IsValid", typeof(bool), typeof(Validator), new PropertyMetadata(true, IsValidChanged));


        public static void IsValidChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            if (obj is Validator)
            {
                var v = (Validator)obj;
                CheckValidState(v);
            }
        }

        /// <summary>
        /// show/hide the valid/invalid FrameworkElement
        /// </summary>
        /// <param name="v"></param>
        private static void CheckValidState(Validator v)
        {
            if (null != v.ValidContent && null != v.InValidContent)
            {
                if (v.IsValid)
                {
                    v.ValidContent.Visibility = Visibility.Visible;
                    v.InValidContent.Visibility = Visibility.Collapsed;
                }
                else
                {
                    v.ValidContent.Visibility = Visibility.Collapsed;
                    v.InValidContent.Visibility = Visibility.Visible;
                }
            }
        }

        /// <summary>
        /// The name of the Control within the visual tree we should try to validate
        /// </summary>
        public string ControlToValidate
        {
            get { return (string)GetValue(ControlToValidateProperty); }
            set { SetValue(ControlToValidateProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ControlToValidate.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ControlToValidateProperty =
            DependencyProperty.Register("ControlToValidate", typeof(string), typeof(Validator), new PropertyMetadata(null));


        /// <summary>
        /// When overridden in a derrived class, checks for a valid value
        /// in <paramref name="ctv"/>
        /// </summary>
        /// <param name="ctv"></param>
        /// <returns></returns>
        public virtual bool Validate(FrameworkElement ctv)
        {
            return true;
        }

        /// <summary>
        /// Helper method for derived classes
        /// </summary>
        /// <param name="fe"></param>
        /// <returns></returns>
        protected virtual string GetInputValue(FrameworkElement fe)
        {
            string val = string.Empty;
            if (fe is TextBox)
            {
                val = ((TextBox)fe).Text;
            } //else if ComboBox, etc.
            return val;
        }
    }
}

We define three dependency properties for showing content, presumably using a ContentPresenter in the default template for Validator.  ValidContent, InValidContent, and InvalidToolTip.  We leave the Validate function empty, and the work of showing or hiding the correct content happens in CheckValidState.

Default Validator ControlTemplate

We target Validator with this ControlTemplate:

<ControlTemplate TargetType="local:Validator">
    <Canvas x:Name="LayoutRoot">
        <ContentPresenter x:Name="ValidPresenter" Content="{TemplateBinding ValidContent}"></ContentPresenter>
        <ContentPresenter x:Name="InValidPresenter" Content="{TemplateBinding InValidContent}">
            <ToolTipService.ToolTip>
                <ContentPresenter x:Name="InvalidToolTipPresenter" Content="{TemplateBinding InvalidToolTip}"></ContentPresenter>
            </ToolTipService.ToolTip>
        </ContentPresenter>
    </Canvas>
</ControlTemplate>

This should be easy to understand.   Since this is an early experiment I haven't created any Visual States yet.

 

Validator Implementations

Surprisingly, creating classes that extend Validator worked out fairly well.  We can review the main pitfall in the ValidContentProperty Dependency Property registration.  Note the default value inside the PropertyMetadata constructor.  I left this in to make a point - only one instance of this TextBlock will be created, and a single Control cannot appear multiple times in the Visual Tree.  Be sure to provide values in each instance or you will get hard to grok errors.  For the sake of brevity, we can just review the implementation for RegexValidator:

using System.Text.RegularExpressions;
using System.Windows;

namespace DamonPayne.AGContrib.Validation
{
    public class RegexValidator  : Validator
    {
        public RegexValidator()
        {
            DefaultStyleKey = typeof(Validator);
        }

        public string RegularExpression
        {
            get { return (string)GetValue(RegularExpressionProperty); }
            set { SetValue(RegularExpressionProperty, value); }
        }

        // Using a DependencyProperty as the backing store for RegularExpression.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty RegularExpressionProperty =
            DependencyProperty.Register("RegularExpression", typeof(string), typeof(RegexValidator), new PropertyMetadata(string.Empty));

        public override bool Validate(FrameworkElement ctv)
        {
            if (!string.IsNullOrEmpty(RegularExpression))
            {
                Regex r = new Regex(RegularExpression);
                string text = GetInputValue(ctv);

                return r.IsMatch(text);
            }
            return true;
        }
    }
}

There are a couple of things to note here.  Because this class is simply adding some logic we use DefaultStyleKey = typeof(Validator); in the constructor.  We could conceivably want different Validators to have utterly different visual representations, but in those cases we can create a ControlTemplate for those instances only.  Surprisingly this works just fine.  We've defined a RegularExpression property we can set from XAML and overridden Validate to do the Regex check.

Adding the Validators in XAML

Given all of this, it's easy to add the Validators onto my order form:

<!-- Name Row-->
<TextBlock Grid.Row="1">Name:</TextBlock>
<TextBox x:Name="NameTxt" Grid.Row="1" Grid.Column="1" Height="25" VerticalAlignment="Top"></TextBox>
<v:RequiredFieldValidator x:Name="NameReq" ControlToValidate="NameTxt" Grid.Column="2" IsValid="True">                
</v:RequiredFieldValidator>            

Obviously, the value of ControlToValidate must be a valid Name somewhere in the visual tree.  Because we allow the display of anything for valid vs. invalid Controls, and we can set the initial valid state my order form now looks like this when first loaded, calling out the fact that Wine Bottles needs a real value:

Form1

Firing the Validators

Given the primitive state of the example application so far, a single extension method for type FrameworkElement will cause all of the validators contained within that FrameworkElement to do their thing.  The extension code is worth taking a quick look at.

namespace DamonPayne.AGContrib.Validation
{
    public static class FrameworkElementExtensions
    {
        /// <summary>
        /// Search the control's Visual Tree for Validators, and run them!
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        public static bool IsValid(this FrameworkElement c)
        {
            bool valid = true;
            var lv = FindAllValidators(c);

            foreach (var v in lv)
            {
                var ctv = (Control)c.FindName(v.ControlToValidate);
                if (null != ctv)
                {
                    if (!v.Validate(ctv))
                    {
                        valid = false;
                        v.IsValid = false;
                    }
                    else
                    {
                        v.IsValid = true;
                    }
                }
            }

            return valid;
        }

        public static List<Validator> FindAllValidators(FrameworkElement fe)
        {
            return FindAllValidators(fe, new List<Validator>());
        }

        public static List<Validator> FindAllValidators(FrameworkElement fe, List<Validator> lst)
        {
            int count = VisualTreeHelper.GetChildrenCount(fe);
            for (int i = 0; i < count; ++i)
            {
                DependencyObject d = VisualTreeHelper.GetChild(fe, i);
                if (d is Validator)
                {
                    lst.Add((Validator)d);
                }
                if (d is FrameworkElement)
                {
                    FindAllValidators((FrameworkElement)d, lst);
                }
            }
            return lst;
        }

    }    
}

You may note that only certain elements such as Canvas define a "Children" property.  VisualTreeHelper is the only way I know of to walk the Visual Tree in a generic fashion.  This allows us to look for Validators within the target FrameworkElement no matter how nested.  I will revisit my choice of FrameworkElement as the entry point.  With this Extension Method, validating my form when I attempt to submit is easy, "Form" being the name of my top level Grid.

private void Button_Click(object sender, RoutedEventArgs e)
{
    if (Form.IsValid())
    {
        //Do something meaninful
    }
}

So, if we push the Submit button without doing anything, we see this ...

Form2

... but providing valid values ...

Form3

 

Conclusion

So, what do you think?  Is this worth a little more work?  I would probably add some kind of Validation Summary, some Visual States to Validator, potentially support Validation Groups, and add a way to dynamically fire the Validators when a Control loses focus.

Send me a note with your comments.

Source code:

DamonPayne.AGContrib.zip (44.75 KB)

Tags:

Red Wine vs. Alzheimer's

by Administrator 21. November 2008 20:50

http://www.scienceblog.com/cms/red-wine-may-reduce-incidence-alzheimers-17841.html

Red wine, you just keep getting better.  Not only do you cure cancer and prevent cancer, you combat Alzheimer's as well.  Scientists are wasting their time with on this nonsense with stem cells and sequencing genomes and so forth - red wine is the answer to everything.  Now, if you'll excuse me, I'm heading to Thief Wine to get some medicine.

Cheers!

Tags:

SIG talk debriefing

by Administrator 19. November 2008 18:06

The talk went very well last night and I got very positive feedback.  Thanks to John from Microsoft for helping with the Projector system, which mysteriously shut down at special pre-programmed times twice during the night; this was a complex topic and the audience was excellent.

Now that I'm done preparing for this, you can expect the next AGT article to happen soon.

Here are the slides and code from the talk:

AGSIGControlTeplatePreso.zip (1.03 MB)

Tags:

Silverlight Talk Reminder - tonight

by Administrator 18. November 2008 17:27

http://www.wi-ineta.org/DesktopDefault.aspx?tabid=192

Hopefully I will see some of you there at 6pm in the Microsoft offices.  I think I'll be visiting the bar at Thunder Bay Grill next door after the show...

Tags:

Research finds that people still like physical media

by Administrator 14. November 2008 22:19

This article on Engadget HD today greatly amused me.

Research finds that people still heart physical discs, greatly prefer Blu-ray to streaming

While the "HD streaming rulez!one11!1!" bandwagon was quickly filling up after Netflix announced that it would be bringing such a service to the Xbox 360, the numbers just don't substantiate the claims that physical discs are doomed anytime soon. Sure, for the budding technophile, streaming is just the next great thing, but for the average joe / jane, the tried and true disc still holds a great deal of importance. A recent study by market research firm SmithGeiger found that out of over 2,000 surveyed, "HDTV owners familiar with Blu-ray favor the format over downloading and streaming by a margin of nearly 10-to-1, with about 70% of respondents citing the fact that there's a physical disc to keep as a key factor in their decision to buy Blu-ray." It also found that 96% of BD users were "familiar with downloading and streaming services, but that two-thirds believe watching a movie on Blu-ray is a better overall entertainment experience." Sure, BD has its flaws, but not having to re-rent an HD film after a remarkably short 24-hour window sure is nice, huh?

No kidding.  The cries that physical media was dead reached their highest volume as it became clear that Blu-Ray was going to win over HD-DVD.  The people making these claims were either:

  1. Analysts who must not watch many movies at home (ergo are relatively clueless on these dynamics)
  2. HD-DVD supporters/fanboys trying a little et tu coque to ease the pain of their loss by saying BD's days were numbered as well.
  3. HD-DVD supporters with huge vested interests in media streaming
  4. Random outfits not necessarily associated with HD-DVD, but with huge vested interests in media streaming

Media streaming has such a long way to go it's not even funny.  I am certain that we'll get there, but consider the following facts and anecdotes:

  1. I have yet to see a streaming solution that offers basic DVD features: chapter selection, audio selection, special features, multiple audio streams, multiple subtitle options.  This takes bandwidth and increases technical complexity of this delivery mechanism.
  2. Ditto #1 but for the advanced interactive features of Blu-Ray: online features, PiP, advanced programmability, etc.
  3. Quality.  Last time I checked HD Cable bitrates top out at around 9mb/s for audio plus video.  Right now this is overwhelmingly Dolby Digital and MPEG-2, with some outfits moving to MPEG-4 for better video compression.  While not bad compared to normal TV, this is a far cry from the 40+ mb/s I have on numerous Blu-Ray discs or even the relatively lean 18+ mb/s on say the King Kong HD-DVD encode.  Some people will not be able to tell the difference, and some won't care, but a lot of people do.  If you have a sound system you care.  The larger the TV you have, the more you'll be able to notice the flaws.
  4. Delivery.  While some people have the option of 10mb/s U-Verse, 18mb/s cable, or 150mb/s fiber in their home, that is still a relatively small # of people and not increasing very fast.  A lot more folks are still slumming it with 3mb/s or 5mb/s cable because they either can't get or won't pay for the faster options.  Where I live, 1.5mb/s DSL is my best option.  I could literally get a Blu-Ray from Amazon.com with 2-day shipping before I could download even half of a high quality movie.

The convenience of just instantly grabbing a movie over the PSN or Netflix, or even queuing it up while I make dinner has appeal.  I will not, however, pay money to watch something of less than DVD quality in my home theater on my 106" screen.  Those of you who will, enjoy your mediocre entertainment! 

Tags:

Speaking at Silverlight SIG

by Administrator 10. November 2008 16:46

I will be speaking next week at the Silverlight Special Interest Group, a subset of the Wisconsin .NET user's group.  The topic is Control Templating, Styling, and the Visual State Manager.  For more details check out:

http://www.wi-ineta.org/DesktopDefault.aspx?tabid=192

 

 

Tags:

Run time is design time for AGT[16]

by Administrator 6. November 2008 23:20

The AGT (Argentum Tela) series of articles is an effort to do two things. Usually an idea is presented only in its finished form. The first goal is to do some Reality Blogging, to show an idea evolve over time without pulling any punches. The second goal, and the example vehicle for the evolution aspect, is an extensible Design Surface for Silverlight similar to what we have in Visual Studio 2008. This type of application has all sorts of interesting uses. My example is a Home Theater layout tool. Read the entire saga: http://www.damonpayne.com/2008/09/14/RunTimeIsDesignTimeForAGT0.aspx

Yes, I've switched to Live Writer full time now.  The VSPaste plug-in is excellent.

Editing Properties

The theory is baked and the anticipation is high, it’s time to edit Properties inside the designer. I add the DesignablePropertyDescriptor type to the solution and add the GetDesignProperties method to the IDesignableControl interface. With this added I need to go about providing implementations. This project is going to start and end with the purple Couch.

public Color FillColor { get; set; }

        public List<DesignablePropertyDescriptor> GetDesignProperties()
        {
            return new List<DesignablePropertyDescriptor>();
        }

I need to implement this for the FillColor property.

public List<DesignablePropertyDescriptor> GetDesignProperties()
      {
          List<DesignablePropertyDescriptor> props = new List<DesignablePropertyDescriptor>();
          
          DesignablePropertyDescriptor fillColor = new DesignablePropertyDescriptor();
          fillColor.PropertyInfo = GetType().GetProperty("FillColor");
          fillColor.DisplayName = "Color";
          fillColor.DisplayType = typeof(TextBlock);
          fillColor.Editable = true;
          fillColor.EditorType = typeof(ComboBox); 
          fillColor.SupportsStandardValues = true;
          fillColor.StandardValues = new List<object>( new object[] {Colors.Purple, Colors.Red, Colors.Gray, Colors.Brown, Colors.Gray});
            return props;            
        }

I’m already questioning the decision not to use attributes, that’s a lot of code for one property. Still, I’ll follow this through for now. Later I’ll dig up some WinForms designer code for comparison. Now I need to provide my default implementation for IDesignableControlInspector.

    public interface IDesignableControlInspector : IService
    {
        List<DesignablePropertyDescriptor> Inspect(IDesignableControl idt);
    }
    public class DefaultInspector : IDesignableControlInspector
    {
        public System.Collections.Generic.List<DesignablePropertyDescriptor> Inspect(IDesignableControl idt)
        {
            if (null != idt)
            {
                return idt.GetDesignProperties();
            }
            return null;
        }
    }

Refactor: At this point, I can see that my IService interface is broken. I’m forcing myself to implement Startup() and Shutdown() methods that I have yet to use in any meaningful way. Removing Startup from the IService definition gives a single compiler error from ServiceManager. The methods are gone until I need them. Of course, this leaves IService as a simple “marker” interface with no methods. Not terribly evil, but there are better ways to supply what is essentially metadata in .NET.

Now I create the IDesignEditorService interface:

public interface IDesignEditorService:IService
{
    Control Visual { get; }
    void Edit(IList<IDesignableControl> targets);
}

For now, I will leave IDesignablePropertyEditor alone.  I need to create a PropertyGrid control and wire up the code needed to use it.  The specifics of making the templatable PropertyGrid will be in the next article.  The PropertyGrid must implement IDesignEditorService.

public Control Visual
{
    get
    {
        return this;
    }
}

public void Edit(IList<IDesignableControl> targets)
{
    //TODO: something
}

Now, in order to begin doing our work of displaying and editing properties, we need to get whatever IDesignableControlInspector has been registered.

public PropertyGrid()
{
    DefaultStyleKey = typeof(PropertyGrid);
    Inspector = ServiceManager.Resolve<IDesignableControlInspector>();
}

IDesignableControlInspector Inspector { get; set; }

And now we can begin doing some work inside Edit. 

public virtual void Edit(IList<IDesignableControl> targets)
{
    if (null != targets && targets.Count > 0 && null != targets[0])
    {
        if (targets.Count > 1)
        {
            EditMultiple(targets);
        }
        else
        {
            EditSingle(targets[0]);
        }
    }
    else
    {
        ClearProperties();
    }
}

I've decided it really would be nice to handle editing multiple items with the same Properties, so we'll do that too.  In order to keep methods small and readable and provide some extra support for alternative implementations of IDesignEditorService I have created EditServiceHelper to assist with the various reflection and control instantiation operations.  In order to more easily see how well this is working, I start by wiring up Selection events with the PropertyGrid.  This means that my DesignSurface control now needs to get an IDesignEditorService injected and we need to handle selection events.

private ISelectionService _selectionSvc;

public ISelectionService SelectionSvc 
{
    get
    {
        return _selectionSvc;
    }
    set
    {
        if (null != _selectionSvc)
        {
            _selectionSvc.SelectionChanged -= new EventHandler(SelectionSvc_SelectionChanged);
        }
        _selectionSvc = value;
        _selectionSvc.SelectionChanged += new EventHandler(SelectionSvc_SelectionChanged);
    }
}
void SelectionSvc_SelectionChanged(object sender, EventArgs e)
{
    List<IDesignableControl> idtList = new List<IDesignableControl>();
    foreach (var idt in SelectionSvc.GetSelection())
    {
        if (idt is DesignSite)
        {
            idtList.Add(((DesignSite)idt).HostedContent);
        }
        else
        {
            idtList.Add(idt);
        }
    }
    EditSvc.Edit(idtList);    
}      

Ok, nothing crazy so far, then things got interesting.  How to generically have some kind of binding between "some visual representation of a property" and "some property on an IDesignableControl" ?  I went back and added another property to my Couch, namely the "DesignTimeName", in order to illustrate this better.  This is where things stand now inside Couch.GetDesignProperties()

public List<DesignablePropertyDescriptor> GetDesignProperties()
{
    List<DesignablePropertyDescriptor> props = new List<DesignablePropertyDescriptor>();

    DesignablePropertyDescriptor name = new DesignablePropertyDescriptor();
    name.PropertyInfo = GetType().GetProperty("DesignTimeName");
    name.DisplayName = "Name";
    name.Editable = true;
                
    DesignablePropertyDescriptor fillColor = new DesignablePropertyDescriptor();
    fillColor.PropertyInfo = GetType().GetProperty("FillColor");
    fillColor.DisplayName = "Color";
    fillColor.DisplayType = typeof(TextBlock);
    fillColor.Editable = true;
    fillColor.EditorType = typeof(ComboBox); 
    fillColor.SupportsStandardValues = true;
    fillColor.StandardValues = new List<object>( new object[] {Colors.Purple, Colors.Red, Colors.Gray, Colors.Brown, Colors.Gray});

    props.Add(name);
    props.Add(fillColor);

    return props;            
}

Inside PropertyGrid.EditSingle, we build up our Grid rows & columns using the EditServiceHelper I mentioned earlier.

protected virtual void EditSingle(IDesignableControl idt)
{
    List<DesignablePropertyDescriptor> props = Inspector.Inspect(idt);
    var propCount = 0;
    foreach (var descriptor in props)
    {
        _propertyPart.RowDefinitions.Add(new RowDefinition());
        TextBlock tb = new TextBlock();
        tb.Text = descriptor.DisplayName;                
        tb.SetValue(Grid.RowProperty, propCount);
        tb.SetValue(Grid.ColumnProperty, 0);
        //
        FrameworkElement displayElement = EditServiceHelper.GetBoundDisplayElement(descriptor, idt);
        displayElement.SetValue(Grid.RowProperty, propCount);
        displayElement.SetValue(Grid.ColumnProperty, 1);
        displayElement.GotFocus += new RoutedEventHandler(displayElement_GotFocus);
        //
        _propertyPart.Children.Add(tb);
        _propertyPart.Children.Add(displayElement);
        ++propCount;
    }
}

That's not too bad so far.  Here's a screenshot with my so far, so lame PropertyGrid template.

Vision12

I'm going to skip over the implementations inside EditServiceHelper for now, it's in the code if you're curious.  Suffice to say it contains some Property to Control mappings for some basic supported types.  For example, a Property of Type string will be both displayed and edited using a TextBox element and a Two Way Binding. When we get into more complex Visualization and Edit scenarios in a later article.

The next thing we need to do is handle a couple of simple edit scenarios when the display element gets focus, as shown above in EditSingle.

void displayElement_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    VerifyEditVisualization(sender);   
}

void displayElement_GotFocus(object sender, RoutedEventArgs e)
{
    VerifyEditVisualization(sender);            
}

private void VerifyEditVisualization(object sender)
{
    if (null != _targets && _targets.Count > 0)
    {
        FrameworkElement element = (FrameworkElement)sender;
        Type t = sender.GetType();
        DesignablePropertyDescriptor descriptor = _descriptorMap[element];
        Type editType = EditServiceHelper.GetEditElementType(descriptor);
        if (!t.Equals(editType))
        {
            FrameworkElement editElement = EditServiceHelper.GetBoundEditElement(descriptor, editType, _targets[0]);
            editElement.SetValue(Grid.RowProperty, element.GetValue<int>(Grid.RowProperty) );
            editElement.SetValue(Grid.ColumnProperty, 1);
            _propertyPart.Children.Add(editElement);
            _editElement = editElement;
        }
        //else do nothing, the Editor is the same as the Displayor
    }
}

One thing you can see immediately is that my colors are listed using their Hex #s, so I need to introduce the value converters into the mix.  I also need to fill in a better look and feel for the PropertyGrid, handle Multiple IDC Edit,  and invent a scenario where I can test out the IDesignablePropertyEditor idioms.  This article is already getting a bit long, so I will do some more cleanup and stop here for now.

Making Property Registration Easier

I'm still thinking that writing code for this property edit scheme this will be easier than the current WinForms equivalent, but I realized immediately that it could be better.  I have created a couple of new methods in EditServiceHelper:

/// <summary>
/// Returns common props from IDesignableControl : DesignTimeName, 
/// </summary>
/// <returns></returns>
public static List<DesignablePropertyDescriptor> GetDefaultDescriptors()
 
/// <summary>
/// Return default <code>DesignablePropertyDescriptor</code> objects for each Property name
/// </summary>
/// <param name="names"></param>
/// <returns></returns>
public static List<DesignablePropertyDescriptor> GetDescriptors(IEnumerable<string> names, IDesignableControl idc)

The default can be merged with any custom list a control author wishes to return.

 

Updating the Existing Controls

Refactor: The Couch, Chair, Center channel speaker, and Tower speaker controls were all refactored to use the new convienience methods to return their own properties.  The descriptions no longer say "red chair" or "purple" couch, since we can actually edit the properties of items on the surface now!

Here's what the application looks like now:

Vision13

Yes, we left Property Editing half done in order to keep each article shorter.

 

Conclusion

If you're following the source code, there were several tiny refactorings made that weren't mentioned here.  Speaking of code, as I previously blogged, the project is in CodePlex now at http://www.codeplex.com/agt. The current source code for this article, then, is the 0.7.16.1 Release.  Tune in next time to see Property Editing reach a more finished state.

The live demo has been updated at http://www.damonpayne.com/agt

Tags:

Argentum Tela moves to Codeplex

by Administrator 6. November 2008 16:14

The various source drops were taking up a lot of space on my server and I promised to open source this tool, and now it's done.  The AGT project has moved to Codeplex.

http://www.codeplex.com/agt

I have not published an actual Release yet, but from this point forward I'll be tagging a release at the end of each article.  Parts [16] and [17] are in progress now.

Tags:

The Obligatory Post-election Comments

by Administrator 6. November 2008 16:09

So, I was at Drinking Right throughout election night.  It was a good time and I met various "right leaning" area bloggers.

As I had been saying for quite some time, unless the polls were outrageously skewed, Obama was going to be our next president.  Fairly early in the night, when Fox news an CNN called Pennsylvania for Obama, I said it was a done deal.

First, I am happy that the victory was decisive, much more decisive than the past couple for certain.  I'm glad to see that we won't be wasting time on recounts and "hanging chad" type nonsense.  I thought McCain's concession speech was gracious and appropriate; it may have been the best speech of his campaign.

Taking Obama on his word, I expect him to greatly increase the size of the government.  While my wife and I make less than $250,000/year I expect that # to change.  Through this and various other hidden items (such as the extra payroll taxes I previously identified) I expect my own tax burden to increase significantly by no later than Q2 2009.  The degree that his various other plans are implemented will be the degree to which we move towards Germany-style chronic 12% + unemployment and zero GDP growth year to year.  GDP typically grows even with your population plus a little extra for technology-driven per-hour productivity gains.  If your GDP is staying "flat" but your population is still growing 3% a year or so, that's a chronic decline folks.

We'll see what we'll see. 

The Republican Party and the Religious Right have taken some serious losses here.  I attribute this to the Republican Party hitching its wagon to the various would be Theocrats in the evangelical christian population and to laying down their former mantle.  The republicans clearly no longer stand for small government.  As many pundits are saying, it will be fascinating indeed to see what comes next for the GOP.  Will they

  1. Try to return to small government, low taxes (without deficit spending), and some semblance of individual rights?
  2. Simply move to the Left since a populist platform seems to be  getting democrats elected?
  3. Decide that they didn't go far enough and that Sarah Palin and those like her are the future of the GOP?

Obviously I'm hoping for #1 but I wonder what the chances are.

This election season has taken far too many mental cycles for too long.  I'm glad it's over and I sincerely hope the nonsense about Obama being an assassination target is indeed fear mongering nonsense.  Among other things, America needs some stability right now.

Tags:

About the author

Damon Payne is a Microsoft MVP specializing in Smart Client solution architecture. 

INETA Community Speakers Program

Month List

Page List

flickr photostream