September, 2009

  • Kirill Osenkov

    Visual Studio 2010 New Editor screenshot

    • 18 Comments

    I’m very happy with how the Visual Studio editor improved since Beta1:

    image

    This is all WPF and includes the WPF 4.0 text rendering changes (DWrite integration). Note the new Zoom combobox in the bottom-left (which is the visual counterpart of Ctrl+ScrollWheel). Also the cross to close the tab is now located ON the tab, and not on the rightmost edge of the document. It takes about a day to get used to, but after you get used to it, it’s really awesome.

    I believe that the editor team has done a phenomenal job making the editor better for all of us. Note that although the screenshot was made from a remote desktop to a virtual machine, it still looks much better than in Beta1. And believe me, it is way faster and more reliable now.

    I’m very excited for Beta2 and can’t wait to get the bits out for you to play with!

  • Kirill Osenkov

    Random Gradient Wallpaper Generator

    • 10 Comments

    Oops, I did it again :) Created a new CodePlex project, that is.

    Introducing http://wallpaper.codeplex.com – a simple tool to generate a nice random wallpaper and display it on your desktop with just one click:

    WallpaperGenerator1.png

    WallpaperGenerator3

    • Randomize - assigns random colors to all four corners
    • Set as Wallpaper - saves the picture to My Pictures\GradientWallpaper.bmp and sets the Windows Desktop wallpaper to this file. It will overwrite the file if it exists.
    • Save colors to text file - appends the RGB values of the current colors to the "colors.txt" log file in the program's directory (in case you liked the colors and want to save the colors, not the image)
    You can also click on each of the four corners and pick any color yourself. There are a total of 79228162514264337593543950336 possible wallpapers that can be generated this way.

    WallpaperGenerator4WallpaperGenerator5WallpaperGenerator6WallpaperGenerator7WallpaperGenerator8WallpaperGenerator9WallpaperGenerator10WallpaperGenerator11

    I wrote this in an evening a couple of years ago and it was just lying around, so I decided to publish it so that it doesn’t get lost. Of course, one could take WPF, MEF, Bling, pixel shaders and build it out into a super-extensible plug-in-based photoshop-like fractal-ray-tracing rendering wallpaper generator, but... For now, let’s stick with a simple WinForms app. But hey, it’s open source, feel free to contribute if you like ;)

  • Kirill Osenkov

    Dump Environment Folder Paths

    • 7 Comments

    This is just a quick snippet to dump the value of Environment.GetFolderPath for all special folders on a machine (so I don’t have to write it again next time):

    public static string DumpEnvironmentPaths()
    {
        var paths = Enum.GetValues(typeof(Environment.SpecialFolder))
            .Cast<Environment.SpecialFolder>()
            .Select(folder => folder + " = " + Environment.GetFolderPath(folder))
            .Aggregate((line1, line2) => line1 + Environment.NewLine + line2);
        return paths;
    }

    Also, I’m using the opportunity to give Rick Strahl’s CodePaste.NET service a spin: http://codepaste.net/uo7zx8.

    “CodePaste.NET is a public site for pasting snippets of code and linking to them from social network sites.”

    As for the code itself (envisioning potential rotten tomatoes flying my way), several things are probably worth mentioning:

    1. First, the code is written in a more functional style, being more explicit about its intent. I think it shows more clearly the input data and the output data, how the data flows and what operations are performed on the input data.
    2. Second, I have a temporary local variable instead of returning the expression immediately. This is for easier debugging, because if you don’t have a temporary variable, you won’t be able to view the return value of the method easily in the debugger. With a temporary local, you can put a breakpoint on the return statement and inspect the results of the query. Someday the tools will be improved and will allow to inspect the return value of methods.
    3. I’m using Aggregate instead of string.Join or StringBuilder because it composes better. On large data volumes, this approach is horrible because it will allocate a ridiculous amount of unnecessary intermediate strings. StringBuilder and string.Join are much more frugal in this respect, so don’t use Aggregate with strings this way. However, in this particular example, the number of strings is O(1), so I just picked the approach that composes better. Unfortunately, string.Join doesn’t compose well at all and was never intended to be used in a fluent API.
  • Kirill Osenkov

    Tip: Don’t enter your CodePlex credentials every time

    • 0 Comments

    I just ran across an awesome tip at Scott’s blog that is going to save me a whole lot of time: Save Your Codeplex Repository Credentials

    I was entering my username and password everytime I connected to a CodePlex TFS server. Now I don’t have to!

    P.S. To make this work on XP, go to Control Panel –> User Accounts –> click your account –> in the leftmost pane click Manage My Network Passwords:

    image

    image

  • Kirill Osenkov

    ColorPicker Control for WPF/Silverlight

    • 8 Comments

    A while back I was looking around for a color picker control for Live Geometry. The ColorPicker from http://silverlightcontrib.codeplex.com was exactly what I was looking for:

    Get Microsoft Silverlight

    (live preview needs Silverlight 3.0)

    Using the control in your code

    I just took the source from CodePlex and embedded it in my project. You need 5 files:

    image

    Alternatively, you can reference the binary which you can download from the SilverlightContrib CodePlex project site. Pay attention that generic.xaml contains the template for the control, so don’t forget the xaml. The control will work just fine with WPF and Silverlight, which is really a great thing, especially if you’re multitargeting.

    To include the control in your application, here’s the basic code:

    <sc:ColorPicker SelectedColor="LightGreen" />

    Don’t forget to add an XML namespace:

    xmlns:sc="clr-namespace:SilverlightContrib.Controls"

    How does the gradient work?

    The source code for this control is very good for educational purposes. For instance, I had no idea how they create the big gradient for every possible hue. Well, it’s genius and it’s simple. In generic.xaml:

    <Canvas Canvas.Top="0" Canvas.Left="20">
        <Rectangle x:Name="ColorSample" Width="180" Height="180" Fill="Red"></Rectangle>
        <Rectangle x:Name="WhiteGradient" IsHitTestVisible="False" Width="180" Height="180">
            <Rectangle.Fill>
                <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
                    <GradientStop Offset="0" Color="#ffffffff"/>
                    <GradientStop Offset="1" Color="#00ffffff"/>
                </LinearGradientBrush>
            </Rectangle.Fill>
        </Rectangle>
        <Rectangle x:Name="BlackGradient" IsHitTestVisible="False" Width="180" Height="180">
            <Rectangle.Fill>
                <LinearGradientBrush StartPoint="0,1" EndPoint="0, 0">
                    <GradientStop Offset="0" Color="#ff000000"/>
                    <GradientStop Offset="1" Color="#00000000"/>
                </LinearGradientBrush>
            </Rectangle.Fill>
        </Rectangle>
        <Canvas x:Name="SampleSelector" IsHitTestVisible="False" Width="10" Height="10" Canvas.Left="100" Canvas.Top="96">
            <Ellipse Width="10" Height="10" StrokeThickness="3" Stroke="#FFFFFFFF"/>
            <Ellipse Width="10" Height="10" StrokeThickness="1" Stroke="#FF000000"/>
        </Canvas>
    </Canvas>

    This canvas contains layers like in a cake. ZIndex of objects is stacked bottom to top, so the solid rectangle with initially red background is on the bottom.

    Above it, there is a horizontal white gradient fill, completely white on the left and completely transparent on the right.

    Above it, there is a vertical black gradient fill, completely black on the bottom and completely transparent on the top.

    As these gradients overlay, the transparency over the initial solid background creates the desired effect – the actual color is in top-right, where both gradients are 100% transparent. The white spot is in top-left, where the white gradient is most intense and black gradient fades out. Same for the black edge of the gradient.

    Also, it is well worth studying how the template is written – I learned a lot from this sample.

    My fixes

    Since I conveniently borrowed the source code for my project, I did several fixes for my own purposes. Ideally I should contribute the fixes back to SilverlightContrib, but I can never get around to it.

    First of all, I reordered the two StackPanels in the template so that the actual selected color is on top. I also made it collapsible and collapsed by default. You can expand the control by clicking it like a combobox. Unlike a combobox, you have to explicitly click the color area to collapse it again.

    Get Microsoft Silverlight

    I’ve enabled this by adding an Expanded property:

    public bool Expanded
    {
        get
        {
            return m_chooserArea.Visibility == Visibility.Visible;
        }
        set
        {
            var visibility = value ? Visibility.Visible : Visibility.Collapsed;
            if (m_chooserArea.Visibility == visibility)
            {
                return;
            }
            m_chooserArea.Visibility = visibility;
        }
    }

    When clicking on the color area, I just call Enabled = !Enabled to toggle it and it does the magic for me. The default value for this is obviously the default visibility of m_chooserArea, which is specified in XAML template (Visibility=”Visible” to set to true by default).

    Other fixes are not as interesting. I fixed a division by zero in ColorSpace.ConvertRgbToHsv (they had h = 60 * (g - b) / (max - min); and didn’t check if min == max). There are a couple of other things which I don’t remember off the top of my head. I’d have to view TFS history to remember what those were. If you’re willing to help and incorporate these fixes to the original project, I’ll dig this up, just let me know :)

    Conclusion

    Both Sara Ford and myself agree that this control deserves both thumbs up:

    image

  • Kirill Osenkov

    Reflection with dynamic

    • 0 Comments

    This past summer the Visual C# IDE team was fortunate enough to have Paul van Brenk intern with us. Paul is a great guy and an experienced .NET developer from Rotterdam, Netherlands, who is interested in Azure, cloud computing and many other things. He also happens to be one of the contributors for dasBlog, which I think is seriously cool.

    Paul and I were sharing an office during his internship and I was the “coach”, i.e. the guy who gets to answer all intern’s questions and helps figure things out :)

    Anyway, once we were talking about how to simplify the mess of calling private members using reflection and dynamic came into conversation. So Paul went ahead and wrote some C# 4.0 code that greatly simplifies calling private members using dynamic. Check out his blog: http://www.paulvanbrenk.com/blog/2009/05/30/UsingDynamicForEvil.aspx

    There are several things I’d like to shed more light on.

    Dynamic usage scenarios

    It’s a common misconception that dynamic can only be used for narrow interop scenarios. People came up with ways to use dynamic in a lot more scenarios.

    A common theme emerges from such usages: delay the name resolution (binding) to runtime. Instead of hardcoding the symbols known to compiler, we just specify source strings that have to be used at a later stage at runtime for lookup.

    Dynamic gives you nicer syntax to express member calls – you express them in your code using usual C# syntax (but without type checking), and the compiler does the heavy lifting of “quoting” the code down to the runtime. If you inspect the generated code, you will see that the compiler is taking your neat member call syntax and generating a complex call site data structure that contains metainformation about the call.

    How the language design team designed the dynamic feature

    From previous versions of C# you will notice that the language design team is clever when adding new features. They don’t just hardcode a feature into the language, they define an interface and implement the feature against that interface. Not literally, mind you. On the contrary, they rather use duck-typing in most cases, but still there is a contract defined somewhere and the feature works against that contract. Let me give a couple of examples:

    • foreach – they could have added foreach just to arrays. Instead, they “designed against an interface” and said: foreach will work against any IEnumerable implementation. Moreover, the contract of the feature is actually broader and is specified using duck-typing and not an explicit interface. foreach actually works against anything that has a GetEnumerator method, it does not have to implement IEnumerable. The extensibility here is that you can plug-in your own enumerator whatever it is and foreach will work just fine with it.
    • LINQ – again, the language design team said: query syntax will just translate into a well-defined set of methods (Select, Where, etc.). The extensibility here is that you can define your own Select, Where, etc. and LINQ will rewrite the query expressions against your own methods – the queries will continue to just work with your own implementation of the query pattern.
    • dynamic – and here, in C# 4.0, they didn’t just add dynamic. They defined an abstraction layer (an interface) that allows you to plug in your own implementation and the feature (dynamic) will work fine with it.

    In other words, they encapsulated an object with runtime callable members into an interface (IDynamicMetaObjectProvider) and provided default base implementation (DynamicObject). What this gives you is the ability to plug in your own implementation and dynamic will work against it.

    Effectively, this means that you can intercept member calls on any object, if these calls are dynamic. The secret of “abusing” dynamic is to use member call syntax for stuff other than calling a method. By redefining DynamicObject members you handle member calls in any way you want (usually some sort of lookup by string) – dictionary lookup, XML element and attribute lookup, reflection MemberInfo lookup, and so on.

    Now whether this all is good or bad is a totally separate discussion.

  • Kirill Osenkov

    First videos of the structured editor prototype

    • 15 Comments

    Disclaimer: the structured editor work described in my posts is unrelated to my work at Microsoft. Everything shown is my personal research done as part of my MSc thesis during 2004-2007. Also, it’s not ready for real use and does not cover all the features of even C# 1.0. It’s a concept prototype and work in progress.

    Introduction

    As part of my research back in school I was building an experimental structured editor for C#. Now I’ve decided to publish the sources and binaries on CodePlex:

    http://structurededitor.codeplex.com

    A detailed discussion of structured editors deserves a separate post, which is coming soon. For now, to give a better idea of how the editor works, I’ve recorded six short videos showing the different features below. If your blog reader doesn’t support iframes, I recommend you view this post in the browser.

    What is a structured editor?

    Programs are represented as text by most code editors. Structured editors, on the contrary, directly display the syntax tree of a program on screen and allow the user to manipulate the tree directly (think MVC: program parse tree is a model, the editor displays a view of it):

    This way the visual layout better illustrates the structure of the program and allows for atomic operations on the language constructs. A structured editor lets developers avoid syntax errors and concentrate on the meaning of the program instead of formatting.

    1. Introduction

    2. Editing

    You will notice that there is no intellisense and coloring at the expression level – I did build a SharpDevelop add-in that hosts the structured editor control inside the SharpDevelop project system (DOM), but these videos were recorded on a stand-alone version of the structured editor control hosted inside an empty windows form. Also the support at the expression level is rudimentary, because so far I’ve concentrated the most at declaration level (namespaces, types, members) and statement level (except for single-line statements such as assignments).

     

    3. Drag & Drop Support

    4. No Parser Means Less Syntax

    5. Properties

    6. Modifiers

    Background

    Structured editing is a topic surrounded with scepticism and controversy for the past 20-30 years. Some argue that directly editing the AST on screen is inflexible and inconvenient, because the constraints of always having a correct program restrict the programmer way too much. Others expect structured editors to be more helpful than text editors because the user operates atomically and precisely on the language constructs, concentrating on the semantics and not on syntax.

    In summer 2004, my professor Peter Bachmann initiated a student research project - we started building a structured editor for C#. I took part because I was deeply persuaded that good structured editors can actually be built, and it was challenging to go and find out for myself. After the original project was over, I took over the basic prototype and evolved it further to the state it is today.

    As one of numerous confirmations for my thoughts, in 2004, Wesner Moise wrote:

    ...I see a revolution brewing within the next three years in the way source code is written.

    Text editors are going to go away (for source code, that is)! Don't get me wrong, source code will still be in text files. However, future code editors will parse the code directly from the text file and will be display in a concise, graphical and nicely presented view with each element in the view representing a parse tree node. ...

    I remember how I agreed with this! After three years, in 2007, the prototype implementation was ready - it became the result of my master's thesis. I still agree with what Wesner was envisioning in 2004 - with one exception. Now I believe that structured editors shouldn't (and can't) be a revolution - fully replacing text editors is a bad thing to do. Instead, structured editors should complement text editors to provide yet another view on the same source tree (internal representation, or AST).

    My conclusions about structured editors

    1. Text-based editors aren’t going away anytime soon.
    2. Structured editors can only succeed by evolution, not revolution – hybrid approach and ability to toggle between text and structure is the way to go.
    3. The main difficulty with structured editors is getting the usability right.
    4. The right approach is solving all the numerous little problems one by one – find a solution for every situation where a text editor is better (from my experience, for any advantage of a text editor, a good structured solution can be found).
    5. You need a graphical framework with auto-layout such as WPF to build a structured editor.
    6. Don’t try to build a universal editor editor that can edit itself – this approach is too complex. Take a specific language and build a hardcoded editor for this language first. Only after you’ve built 2-3 successful editors, it makes sense to generalize and build a universal editor or an editor generator.
    7. Compilers shouldn’t be a black box, but expose the data structures as a public API surface.
    8. Syntax trees should be observable so that the editor can databind to them.
    9. Traditional expression editor should be hostable inside a structured editor for single-line statements and expressions.

    As a result of my work, I'm convinced that structured editors actually are, in some situations, more convenient than text editors and providing the programmer with two views on the code to choose from would be a benefit. Just like Visual Studio Class Designer - those who want to use it, well, just use it, and the rest continues to happily use the text editor. All these views should co-exist to provide the programmer with a richer palette of tools to chooce from.

    Hence, my first important conclusion. A program's internal representation (the AST) should be observable to allow the MVC architecture - many views on the same internal code model. With MVC, all views will be automatically kept in sync with the model. This is where for example something like WPF data-binding would come in handy.

    As for the structured editor itself - it is still a work in progress and I still hope to create a decent complement for text editors. It has to be usable and there are still a lot problems to solve before I can say: "Here, this editor is at least as good as the text editor". But I managed to solve so many challenging problems already, that I'm optimistic about the future.

    Current implementation

    The current implementation edits a substantial subset of C# 1.0 - namespaces, types, members (except events), and almost all statements. If you're interested, you can read more at http://www.guilabs.net and www.osenkov.com/diplom - those are two sites I built to tell the world about my efforts. I also accumulate my links about structured editing here: http://delicious.com/KirillOsenkov/StructuredEditors

    More languages

    It turned out that it makes sense to build structured editors not only for C#, but for other languages as well - XML, HTML, Epigram, Nemerle, etc. That is why, at the very beginning, the whole project was split in two parts - the editor framework and the C# editor built on top of it.

    Links

    1. http://structurededitor.codeplex.com
    2. http://guilabs.net
    3. http://www.osenkov.com/diplom
    4. http://delicious.com/KirillOsenkov/StructuredEditors

    If you want to know more, or if you want to share your opinion on this, please let me know. I welcome any feedback! Thanks!

    kick it on DotNetKicks.com Shout it
  • Kirill Osenkov

    UI without code or XAML: PropertyGrid, DataForm, etc.

    • 2 Comments

    WPF has certainly advanced the state-of-the-art in programming the UI. I personally think that WPF and Silverlight remove all the limits that existed to UI development in the past. The only limit now is your imagination. XAML is there for declarative machine-friendly UI descriptions, data-binding is for easier logic of syncing the UI to the data, automatic layout saves you all the trouble of aligning controls yourself, and so on.

    One thing that I notice though is that if your goal is a simple dialog or a wizard page, one is likely to write more code than is absolutely necessary, be it in a .NET language or XAML. For instance, one of the most trendiest patterns nowadays, MVVM, suggests that you have a model, a ViewModel (a UI-aware wrapper around model classes) and a View (that is databound to ViewModel). It is also suggested that you write some XAML to achieve what you need.

    My personal impression is that UI development nowadays has become complicated. It's super powerful, yes, but I find it is hard to find a simple solution for a simple scenario.

    For the purpose of this article, suppose that we need to implement a person editor dialog, and our business logic describes a person like this:

    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }

    One is now likely to open XAML and start adding a Grid or a StackPanel with a couple of TextBoxes and databind them to the properties of the Person class. This approach is fine with me in general (since it decouples the UI from the domain model), however what I find is that it's just complicated and for simple scenarios might be an overkill.

    One other approach is often forgotten.

    PropertyGrid

    WinForms introduced an excellent control which unfortunately is probably less known and less used than it deserves: the PropertyGrid. You just add this control to your form and you just say:

    propertyGrid1.SelectedObject = joe;

    and you get:

    image

    What happens is that property grid reflects over the "editable" properties on the object, and displays each property as a separate row in the grid. Depending on the type of the property, it's value can be editable, additional controls can be displayed, for example, a color picker for colors, a calendar for DateTime, a checkbox for bool, etc. The nice thing about the design of the property grid (and I do think that the WinForms PropertyGrid is a very well designed control) is that if you have your own type, then you can also provide your own editor control for properties of that type.

    Also, the property grid provides certain other services - grouping properties into collapsible categories, description for selected property, multi-object selection (which enables setting a property on multiple objects simultaneously), etc.

    From the design perspective, what the property grid gives you is the ability to not duplicate the description of the UI. In the XAML example above you essentially describe your UI twice: first in your domain model (Name, Age) and then in your XAML (TextBox, TextBox) and then you bind the two together. With a property grid, on the contrary, you just declare your UI once, in your domain model, and you don't have to write any UI at all - property grid generates the UI for you.

    Unfortunately, this promising approach didn't go much further in the WinForms world, probably because of the limitations of the WinForms architecture (you couldn't re-style the property grid editors, rearrange rows or significantly customize its appearance (besides choosing the background and foreground colors and fonts).

    Silverlight 3 DataForm

    Thankfully, Silverlight 3 starts to fill the gap in the direction of eliminating the UI code by introducing the DataForm control: you can watch a great video by Mike Taulty about the DataForm here: http://beta.silverlight.net/learn/videos/silverlight-videos/dataform-control/ (and by the way, Mike Taulty makes excellent, excellent videos, highly recommended).

    Silverlight 3 DataForm comes with the free Silverlight Toolkit (http://silverlight.codeplex.com), is located in the assembly System.Windows.Controls.Data.DataFormToolkit.dll (GAC'ed) and works almost exactly like the WinForms PropertyGrid:

    image

    which results from this code:

    dataForm.CurrentItem = joe;

    I'm not aware of such a control for WPF but I haven't actually looked. All I know that it's a cool and promising approach which makes UI programming simpler, especially if you're designing dialogs, wizard pages or property grids.

    My own experiments

    Before Silverlight 3 came out, I was looking for an elegant way to implement property editing for the geometric figures in Live Geometry:

    image

    Since I didn't want to write any XAML for every property of every figure, I decided that the PropertyGrid design was a perfect fit, so I just implemented my own Property Grid (The benefit of having a hobby project is that you can use it as a playground and sandbox for experimenting with new ideas and approaches).

    The API is almost the same as the ones mentioned above:

    PropertyGrid.Show(this.Style, this.Drawing.ActionManager);

    gives you this:

    image

    Unlike the previous ones though, apart from the actual object being edited you can pass the second argument - a pointer to the ActionManager from my Undo Framework, which will make sure all the edits are recorded in the Undo history and can be undone.

    Metadata

    An important decision in the design of my property grid was to abstract away from modeling UI elements as CLR metadata. Both PropertyGrid and DataForm use CLR metadata (properties and their types) to encode what controls do we want to show on the form. Although metadata is very good for this (especially enriched with the .NET attributes as additional declarative mechanism, such as the [Description] attribute), I wanted to abstract away how the UI intent is declared, just like MEF can abstract away how a contract is specified. This was also inspired by the introduction of ICustomTypeDescriptor that allows extensibility of the PropertyGrid along the dimension of the property set. I didn't like metadata as the host language for the declaration of my UI elements, neither I liked XAML, for various reasons. So I came up with a basic interface called IValueProvider that encapsulates any value, be it a value of a CLR property or a value of a method's parameter, or anything else that might come up in the future. Here's a rough sketch (design not finalized yet):

    public interface IValueProvider
    {
        event Action ValueChanged;
        void RaiseValueChanged();
        T GetValue<T>();
        bool CanSetValue { get; }
        void SetValue<T>(T value);
        object Parent { get; }
        Type Type { get; }
        string Name { get; }
        string DisplayName { get; }
        T GetAttribute<T>() where T : Attribute;
        IEnumerable<T> GetAttributes<T>() where T : Attribute;
    }

    This gives me extensibility along the dimension of how UI elements are declared.

    Discovery

    To solve the task of taking content (think: an object) and extracting a set of editable values from it (think: properties), I introduced a so-called ValueDiscoveryStrategy. IncludeByDefaultValueDiscoveryStrategy will include all the properties unless they're marked with the [Ignore] attribute, and ExcludeByDefaultValueDiscoveryStrategy will exclude all the properties unless they're marked with the [PropertyGridVisible] attribute. Right now the discovery strategies only support CLR metadata (object properties and method parameters), but in the future nothing prevents me from adding more strategies to create editable values off of something else, say columns of a table in a database schema. This gives me the extensibility along the dimension of what properties to show in the grid.

    As an example, here's the ShapeStyle class that is being displayed in the screenshot above:

    public class ShapeStyle : LineStyle
    {
        public override FrameworkElement GetSampleGlyph() {...}
    
        Color mFill = Colors.Yellow;
        [PropertyGridVisible]
        public Color Fill
        {
            get
            {
                return mFill;
            }
            set
            {
                mFill = value;
                OnPropertyChanged("Fill");
            }
        }
    
        public override Style GetWpfStyle() {...}
    }

    The Fill property comes in here, and Color and Stroke width are inherited from LineStyle.

    Editors

    An editor is essentially a visual control that can edit an IValueProvider. For different types of values there are different kinds of editors. It's just a textbox for a string property, a checkbox for bools, a color picker for Color, etc. The nice thing (again, borrowed from the WinForms property grid design) is that the pool of editors is extensible. I use homemade dependency injection to discover all types of editors available for a type and instantiating a proper editor for a proper type. This gives me extensibility along the dimension of what types of properties can I edit.

    Also at this point kudos go to SilverlightContrib folks for an excellent ColorPicker control that I reused in my app! Great job and many thanks! BTW I fixed two bugs in it which I think I should submit as patches... need to remind myself later...

    Anyway, here's all it took to add an editor type for properties of type System.Windows.Color:

    public class ColorEditorFactory 
        : BaseValueEditorFactory<ColorEditor, Color> { }
    
    public class ColorEditor : LabeledValueEditor, IValueEditor
    {
        public ColorPicker Picker { get; set; }
    
        protected override UIElement CreateEditor()
        {
            Picker = new ColorPicker();
            Picker.SelectedColorChanging += ColorChanged;
            return Picker;
        }
    
        void ColorChanged(object sender, SelectedColorEventArgs e)
        {
            SetValue(e.SelectedColor);
        }
    
        public override void UpdateEditor()
        {
            Picker.SelectedColor = GetValue<Color>();
            Picker.IsEnabled = Value.CanSetValue;
        }
    }

    Complex types

    There are two approaches for displaying properties of complex types: either expanding them in place just like a tree view item gets expanded (and collapsed), or providing a hyperlink that, when clicked, will display that object's properties in the PropertyGrid, instead of the current one. In the latter case, one will probably want to implement some sort of a back button that will display the original object.

    Method Caller Buttons

    Finally, why not move it one step further? If we can get and set properties using this property grid, why not support calling methods? Here's a screenshot again of the polygon properties - note the three buttons at the bottom. They're generated too!

    image

    Here's the full source for the buttons:

    [PropertyGridVisible]
    public virtual void Delete() { ... }

    As you see, just add the [PropertyGridVisible] on any method and it will appear in the UI. Obviously, pushing the button will call the method.

    [PropertyGridVisible]
    [PropertyGridName("Create new style")]
    public void CreateNewStyle()
    {
        Drawing.ActionManager.SetProperty(this, "Style", 
            Drawing.StyleManager.CreateNewStyle(this));
        EditStyleButton();
    }
    [PropertyGridVisible]
    [PropertyGridName("Edit style")]
    public void EditStyleButton()
    {
        if (PropertyGrid != null)
        {
            PropertyGrid.Show(this.Style, this.Drawing.ActionManager);
        }
    }

    The next logical step is to allow calling methods with arguments. For example, having this method on your type:

    [PropertyGridVisible]
    public void AddEntry(Person person, DayOfWeek day, bool check)
    {
    }

    will result in this UI:

    image

    Clicking the button will call the method and pass the current values as arguments. This button is implemented as a simple button with PropertyGrid content that displays MethodInfo's parameters instead of object's property values (it uses a different ValueDiscoveryStrategy called ParameterDiscoveryStrategy).

    Summary

    Using this approach we can build a UI library for calling APIs. You design an API and you get a (basic) UI for it for free. This library will allow us to inspect any object at runtime similar to how debugger watch windows allow us to do it. Moreover, we can not only inspect it, but also set the property values and call methods on objects. This might prove useful not only for dialogs, wizard pages, property browsers, but possibly in other scenarios where you're editing a data structure directly and don't really care about customizing the default UI.

    I'm currently planning to continue evolving this prototype library as part of the Live Geometry source code, and possibly spawn off a separate project when this library becomes more mature and if there is demand.

    For now, if you're interested in the source code, you can find the up-to-date implementation at http://livegeometry.codeplex.com/SourceControl/ListDownloadableCommits.aspx -> Main\DynamicGeometryLibrary\PropertyGrid.

    Let me know if you have any questions or feedback. Thanks!

Page 1 of 1 (8 items)