Welcome to MSDN Blogs Sign in | Join | Help

My first XBap application ...

Currently we are all down to last nitpicks for VsOrcas release of Visual Studio. What can be a better time than this to engage into app-building and find how "useful" is the product that we have been building for almost 2 years now.

I created my first XBAP application and its real fun and the WPF Designer (a.k.a CIDER) is looking really good.

 

-SOak. 

Posted by subhagpo | 1 Comments

Workflow between CIDER (Interactive Designer in VS) and SPARKLE (Graphics Designer)...

Karsten explains the seamless workflow between the designer and developer tools in this video here
Posted by subhagpo | 1 Comments

Implementing a "Insitu editor" using CIDER Adorner extensibility model.

Implementing a "Insitu" editor was so tough in Whidbey, but not in CIDER.  Lets see how its done for a button. When this button gets selected we want to pop-up a textbox on top of the text area of the button as follows

Editor1

Our Insitu textbox is an adorner that is provided for our "InsituButton". This adorner is provided by our custom AdornerProvider. Since we want to show our adorner when the button is the primary selection, we derive from the PrimarySelectionAdornerProvider as follows

    // Insitu AdornerProvider.

    class InSituAdornerProvider : PrimarySelectionAdornerProvider

    {

        private MyTextBox _inSitu;   //insitu editor

        private AdornerPanel _panel; //the parent adorner panel

        private ShowInSituEditorTask _myTask; //the task that enables the insitu editor

 

        public InSituAdornerProvider()

        {

 

            _panel = new AdornerPanel();

            _panel.IsContentFocusable = true;

            _inSitu = new MyTextBox(_panel);

 

            //Add a textbox but initially its hidden.

            _inSitu.Enabled = false;

            _panel.Children.Add(_inSitu);

           

            //Add our task which will show the TextBox on doubleclick

            _myTask = new ShowInSituEditorTask(_inSitu);

            AdornerPanel.SetTask(_inSitu, _myTask);

            Adorners.Add(_panel);

        }

 

        protected override void Activate(System.Windows.Design.Model.ModelItem item, UIElement view)

        {

            base.Activate(item, view);

            _myTask.Item = item;

        }

    }

The ShowInSituEditorTask task is responsible to add a new ToolCommand and listen for the double click on the left mouse button and enable our Insitu editor. The editor (textbox) itself listens for the KeyDown event and deactivates the task when "enter" key is pressed. The code for our custom task looks like follows

class ShowInSituEditorTask : Task {

        private MyTextBox _inSituEditor; // actual editor

        private ModelItem _item;         // model item

       

        // The actual model item representing the "button"

        // This is required to change the "text" property.

        public ModelItem Item {

            get {

                return _item;

            }

            set {

                _item = value;

            }

        }

 

        // Constructor for our task.

        public ShowInSituEditorTask(MyTextBox edit) {

            _inSituEditor = edit;

 

            // Create our new custom command

            ToolCommand command = new ToolCommand("InSitu");

 

            // Add our command to the InputBinding

            InputBindings.Add(new InputBinding(command, new ToolGesture(ToolAction.DoubleClick, MouseButton.Left)));

           

            // Associate our ToolCommand with the command handler.

            ToolCommandBindings.Add(new ToolCommandBinding(command, EnableInSituEditor));

 

            //Listen to the "Enter Key on the Text"

            _inSituEditor.KeyDown += new KeyEventHandler(_inSituEditor_KeyDown);

        }

 

        //Listen for Enter key to deactivate the "insitu editor"

        //by de-activating the task.

        void _inSituEditor_KeyDown(object sender, KeyEventArgs e)

        {

            MyTextBox edit = sender as MyTextBox;

            if (edit != null) {

                if (e.Key == Key.Enter){

                    this.Deactivate();

                }

            }

        }

 

        //Enable the insitu editor on "doubleclick"

        private void EnableInSituEditor(object sender, ExecutedToolEventArgs args)

        {

            //Enable the Editor

            _inSituEditor.Enabled = true;

        }

 

        //when the task is de-activated set the Content property

        //on the button to the text of the Insitu textBox.

        protected override void OnDeactivated(EventArgs e)

        {

            Item.Properties["Content"].SetValue(_inSituEditor.Text);

            //Disable the Editor

            _inSituEditor.Enabled = false;

            base.OnDeactivated(e);

        }

    }

So when our "fancy button" is selected and the user double clicks the "grey" text area, the editor is now enabled and looks like following:

Editor2

The insitu editor itself is also a custom textbox which overrides the "ArrangeOverride" to position the editor right on the top of the button's text when its selected. The "MeasureOverride" causes the textbox to be of the exact size of the "text' being added. This allows the text editor to grow or shrink when the text is changed as shown in the figure below:

editor3

When you drop in the "InsituButton" on the window, you want to specify that the "InsituButton" should provide the Insitu editor. This is done by adding an extension on top of the "InsituButton" as follows:

 

   [Extension(typeof(InSituAdornerProvider))]

    public class InSituButton : Button

    {

        public InSituButton() : base()

        {

            this.Content = "MyButton";

        }

    }

When this InsituButton is added to the Window, the Cider extensibility mechansim finds this extension and sees that it is a AdornerProvider which derives from the PrimarySelectionAdornerProvider. Hence it knows that whenever this control is in the primary selection, the adornerprovider should be instantiated.

Once the provider is instantiated, our custom code runs and adds the "Insitu" editor to the InsituButton. When the button looses the primary selection, the AdornerProvider is removed and so is our "adorner" (Insitu textbox).

Thats how simple it is to add "Insitu" editor to a control with CIDER extensibility.

 

Posted by subhagpo | 1 Comments
Filed under:

Fun-in-CIDER

I have been in-CIDER from the end of last year. Its been a great busy journey till now. So let me start from the very beginning.

I joined the group from WINFORMS background and hence adopting WPF wasnt difficult. But I had to ram-up on new WPF concepts of command routing, dependency object/properties, data-binding, styles and templating and XAML. To get an overview of WPF, I implemented a tic-toc-toe application. (Coincidently, Chris Sells also explains WPF basics using tictactoe). Here is an image of what I created.

TTT

 

Then I implemented a new check-in system for the CIDER team. This system completely obliviates the developer from the "source-depot". The developer needs to run the suites, build and submit. The check-in system would pick up his change, validate and then submit it. It will send him/her a mail once its done and then go on to the next check-in.

Implementing this system gave me a great insight on how different teams are using different systems, what are the pros & cons of each system and what are some of the key features required for such a system.

I also worked on implementing a custom property grid using the data binding and templating features of avalon and using the CIDER property value extensibility model.  This feature helped me understand the basics of data-binding and control-templating.

Playing with PropertyGrid

During the last coding milestone I worked on two small assignnments

1) WPF exposes a new UI Automation framework for the WPF elements. Since CIDER needs to be UI accessible we had to use the UIAutomation and provide our own implementation for the design time UI elements that the CIDER adds like adorners, designer actions etc. CIDER has now implemented its own automation peer hierarchy using the WPF UIA framework making its design time elements accessible by UIAutomation clients (like UISPY).

UIA

2) When a control is selected in the the property-grid, CIDER designer "pre-instances" the control. This facilitates easy parenting and shows adorner-package as the control is being moved by the mouse. I worked on the early part of this feature, implementing the control pre-instancing.

pre-instancing

In the current milestone, we are following the feature crew model. I am currently working on the extensibility feature crew. The main goal of this milestone is to be "feature complete". This means that every feature checked into the production code should have a Specification, Design Documentation, Test plan, Test Implementation and atleast 85% code coverage. Phew! But we are driving towards that. The Extensibility feature being the basic building block of CIDER designer and beyond, getting the Extensibility right and "fully tested" would definitely pay rich dividends in the end game. The features in the extensibility FC are

  1. PropertyValueEditors
  2. DesignerActions
  3. Metadata store
  4. Tools and Tasks
  5. Adorners
  6. DesignerView, CommandGestures, ToolCommands.

We are currently finishing the current milestone and starting next week we are moving into planning phase for the next milestone. Our dear PM (Jim) has asked an open question to all you guys out there. What would you want CIDER extensbility team deliver? What are core features that would make you happy? What is it that you find missing in the current IDE (for winforms)?

Let us know and we will try to find out answers!!

Posted by subhagpo | 0 Comments

JNak has been blogging!

One more addition to CIDER bloggers. James Nakashima - PM on the CIDER team. He has some great posts about item creation and cider adorner extensibility stuff.

Posted by subhagpo | 0 Comments
Filed under:

first blog for cider !

This is my first blog since I have changed teams last year. Wow, its almost 5 months since I am in this new team and there are no excuses for not blogging.

I have decided to actively start blogging about this very cool technology that I am working on which is called Cider. I know you will ask "Cider.. what is that?". BrianPe has already explained it here. We are building a designer for WPF technology thats going to ship with Visual Studio.

We have adopted the new agile process of developement and have already shipped some CTPS (the most recent was the Feb CTP).

Instead of starting the first post with the customarty "Hello world" application, I will just provide some links to the blogs of my team members (who are active bloggers than myself)

BrianPe: http://www.urbanpotato.net/Default.aspx/document/970

ChuckJ: http://www.removingalldoubt.com/

Jfosler: http://blogs.msdn.com/jfoscoding/archive/category/11634.aspx

RBailey: http://blogs.msdn.com/richard_bailey/

 

Posted by subhagpo | 0 Comments
Filed under:

What's new in Whidbey? : Part 1 - ClipBoard Improvements.

   There are lots of new things in the Whidbey release for Windows Forms. 

 

   There are a bunch of new controls and components like StripControls (ToolStrip, MenuStrip, ContextMenuStrip, StatusStrip), DataGridView, SplitContainer, WebBrowser. We have also added some richness to existing controls and components. For example we have exposed the scrollbars on scrollablecontrol as properties, added a bunch of new properties to the treeview and the tooltip, provided auto complete feature on the combo-box and the text-box and added list-view virtualization.

   In the designer space we have exposed a framework to build simple light weight designers that automagically uses the new serialization code, snaplines and other designer features. We have added snap-lines and a new mechanism to show the properties that are commonly used which we call the smart-tags extension. We have also enabled configuration settings management and provided extensible interfaces for the users to plug-in their own configuration settings.

    In order to explain some of the new features in Whidbey, I have decided to take one feature at a time and explain how it works and how it can be used. I will also try to explain how it is different (and easy) to use the same feature in Whidbey as compared to Everett (if applicable). So let’s get started...

 

ClipBoard:

 

   ClipBoard is an existing class from Everett which can be found in System.Windows.Forms namespace. The basic functionality of this class is to allow user to set data into the clipboard and retrieve it. This is how the class looked in Everett

 

public sealed class System.Windows.Forms.Clipboard :
    object
{

    // Constructors

    // Methods
    public virtual bool Equals(object obj);
    public static System.Windows.Forms.IDataObject GetDataObject();
    public virtual int GetHashCode();
    public Type GetType();
    public static void SetDataObject(object data);
    public static void SetDataObject(object data, bool copy);
    public virtual string ToString();
} // end of System.Windows.Forms.Clipboard

 

   The user can use the SetDataObject to place the data on the clipboard. The bool “copy” denotes if the data should remain on the clipboard after the application exits.

Consider a form with a picture box and a button. The following code would place the image of the picture box on the clipboard.

 

private void button1_Click(object sender, System.EventArgs e)

{

// Takes the image and puts it on the clipboard.

      if(this.pictureBox1.Image != null)

            Clipboard.SetDataObject(this.pictureBox1.Image, true);

}

 

   Now consider another application which has a panel and a button. Now you can set the background image of the panel to the image of the picture box that was copied to the clipboard earlier using the following code.

 

private void button2_Click(object sender, System.EventArgs e)

{

// Declares an IDataObject to hold the data returned from the   // clipboard.

      // Retrieves the data from the clipboard.

      IDataObject iData = Clipboard.GetDataObject();

      // Determines whether the data is in a format you can use.

      if(iData.GetDataPresent(DataFormats.Bitmap))

      {

            // Yes it is, so display it.

this.panel1.BackgroundImage = (Bitmap)iData.GetData(DataFormats.Bitmap);

      }

      else

      {

            // No it is not.

            this.Text = "Could not retrieve data off the clipboard.";

      }

}

 

   In Whidbey the ClipBoard class looks as follows:

 

public sealed class System.Windows.Forms.Clipboard :
    object
{

    // Constructors

    // Methods
    public static void Clear();
    public static bool ContainsAudio();
    public static bool ContainsData(string format);
    public static bool ContainsFileDropList();
    public static bool ContainsImage();
    public static bool ContainsText();
    public static bool ContainsText(System.Windows.Forms.TextDataFormat format);
    public virtual bool Equals(object obj);
    public static System.IO.Stream GetAudioStream();
    public static object GetData(string format);
    public static System.Windows.Forms.IDataObject GetDataObject();
    public static System.Collections.Specialized.StringCollection GetFileDropList();
    public virtual int GetHashCode();
    public static System.Drawing.Image GetImage();
    public static string GetText();
    public static string GetText(System.Windows.Forms.TextDataFormat format);
    public Type GetType();
    public static void SetAudio(byte[] audioBytes);
    public static void SetAudio(System.IO.Stream audioStream);
    public static void SetData(string format, object data);
    public static void SetDataObject(object data);
    public static void SetDataObject(object data, bool copy);
    public static void SetDataObject(object data, bool copy, int retryTimes, int retryDelay);
    public static void SetFileDropList(System.Collections.Specialized.StringCollection filePaths);
    public static void SetImage(System.Drawing.Image image);
    public static void SetText(string text);
    public static void SetText(string text, System.Windows.Forms.TextDataFormat format);
    public virtual string ToString();
} // end of System.Windows.Forms.Clipboard

 

 

   A bunch of static helper functions have been added to the ClipBoard class that would wrap the data in the DataObject and pass it onto the clipboard in the correct format. The ContainsXXX methods can be used by the user to query the current contents of the clipboard without actually getting the IDataObject from the clipboard and calling GetDataPresent on it. The helper functions are provided on commonly used DataFormats but users can still use the old SetDataObject and GetDataObject for custom formats.

   In Whidbey, you can programmatically clear the clipboard calling the Clear method. This removes all the contents of the clipboard and can be helpful if you want to purge the contents of the clipboard periodically.

 

   Coming to the same example that we discussed above, the same code in Whidbey would look like the following:

 

private void button1_Click(object sender, EventArgs e)

{

// Set the image (this will throw an exception if image passed in // is null)

     Clipboard.SetImage(this.pictureBox1.Image);

}

 

private void button2_Click(object sender, EventArgs e)

{

     // Check if ClipBoard has image...

     if (Clipboard.ContainsImage())

     {

         // if so, set the image to the background image of the panel

         this.panel1.BackgroundImage = Clipboard.GetImage();

     }

     else

     {

         this.Text = "No Image in ClipBoard";

     }

}

 

   As you can see the code here is greatly reduced as compared to Everett and most of the work is done by the clipboard class. The user just needs to call the proper helper functions.


 

Whidbey security restrictions using ClipBoard

 

   Clipboard works for application running under STA apartment thread only. More information about STA and MTA thread can be found here. In full-trust the clipboard allows to set and get any data. In partial trust (Internet zone) data only in the following four formats can be set into the clipboard:

 Formats allowed: Text, UnicodeText, System.String and CSV.

All other formats are restricted in partial trust.

 

Coming up next is TreeView... so stay tuned.

Posted by subhagpo | 0 Comments
Filed under:

How can I drop controls within UserControl at Design time?

One of the most frequent questions asked on the community forum is how can I make my userControl act as a containerControl (panel) which allows me to add controls at design time. In V1.0 and V1.1 you had to write a lot if code to do this, but in Whidbey we have added a new concept of NestedContainers which would allow the users to accomplish this using a single method on the ControlDesigners.

Let me explain how you can expose the userControl as "container" at design time.

You may ask why doesnt it work by default?

What you’re seeing is our default encapsulation model for user controls.  By default, a user control is treated as a single object when placed on a form.  We do this because it may not be your intent to expose the inner layout of your user control (for example, the PropertyGrid control contains several panels, buttons, scroll bars, etc, but it is treated as a single control). So there are cases when you want the userControl to be a single composite control and hence not support it as container at design time. This still remains the default case in whidbey too.

What should I do to make the control act as container at Design time?

This is easy to do.  You need to change the designer on your user control:

[Designer(typeof(ParentControlDesigner))]

public class MyUserControl : UserControl {}

Once you do this, the user control can be edited on the form designer’s surface.If you want a region of “content” on your user control to be interactive at design time…

This is the more likely case – you have a control with some child controls on it, but one (or more) of the child controls needs to be a content area where the user can drop additional controls.  Within this content area you want the user to be able to select, drag, drop and otherwise manipulate controls, but outside of the area you want the user control to be treated as a single entity. In whidbey, you need to do two things:

1)  Add a public read-only property to your user control that exposes the child content control. Write a simple designer that enables the content control to be designed. 

[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]

public Panel MyChildPanel { get { return _myChildPanel; } }

The attribute on the property tells the code generator to write out code for the value contained in this property (normally, read-only properties are skipped).

2) Use EnableDesignMode method to plug in the designer for panel.

[Designer(typeof(MyUserControlDesigner))]

public class MyUserControl : UserControl {

}

class MyUserControlDesigner : ControlDesigner {

      public override void Initialize(IComponent comp) {

            base.Initialize(comp);

            MyUserControl uc = (MyUserControl)comp;

            EnableDesignMode(uc.MyChildPanel, “MyChildPanel”);

      }

}

Here I’ve created a designer (which can…and should be an internal class unless you specifically design for extensibility).  In the initialize code for the designer I’ve called EnableDesignMode, passing the panel I want to enable (MyChildPanel), and a text name that will be shown to the user when the panel is selected (“MyChildPanel”). 

Once you do this, the panel will work in the designer.  If you click on it, it will be selected and you can set properties on it in the property window.  The name for the selected panel will be a mix of the control’s name and the name you provided.  For a control named myUserControl1, the panel’s name in the grid will show up as “myUserControl1.MyChildPanel”.

I will discuss the basics of EnableDesignMode in a separate blog entry.

 

Posted by subhagpo | 2 Comments
Filed under:

Mark Boulter video...

Mark Boulter (Technical Lead PM) Dot Net Client team explains the new features for Whidbey.

He explains his definition of "smart" clients in the first half and then talks about his work here at Microsoft.

Here's the complete interview

Part1 

Part2

 

Posted by subhagpo | 1 Comments
Filed under:

Microsoft Research ...

Soma's blog describes some of the projects that have been undertaken by Microsoft Research lab.

Posted by subhagpo | 1 Comments
Filed under:

What is Control.SelectNextControl(...)?

Today I recieved a question on Winforms discussion list about SelectNextControl function on control and I thought it might be worth sharing some thoughts on this topic. The name "SelectNextControl" suggests that this function would select the next control in the taborder starting from the control on which this function is called. But THAT’S NOT THE CASE.

Infact, this function tries to select the next "child" control (in the taborder) of the control on which this function is called. Let me explain with a scenario to make this point easy to understand. Say you have a form which has couple of textBoxes and a button. In the button click you want to change the focus to the next control in tarorder from the Button. (which is the ActiveControl on the Form when you click it) 

The Following code is WRONG:

private void button1_Click(object sender, System.EventArgs e)

{

        ((Button)sender).SelectNextControl(ActiveControl, true, true, true, true);

}

 

The above code would try to select the next control within Button and since there is NO child control within button this will just be a NO-OP.

What you really want is to do the following:

 

private void button1_Click(object sender, System.EventArgs e)

{

         Control parent = ((Button)sender).Parent;

         parent.SelectNextControl(ActiveControl, true, true, true, true);

}

So be careful about using this function as the MSDN help doesnt explicity mention this behavior.

 

Posted by subhagpo | 4 Comments

Whats the difference between Control.Invalidate, Control.Update and Control.Refresh?

Control.Invalidate() v/s Control.Update() v/s Control.Refresh()

 

Before discussing each one of the above functions, let’s look at how winforms controls paint.

 

Windows controls paint is response to WM_PAINT messages. This message is sent when UpdateWindow or RedrawWindow is called, or by the DispatchMessage function when the application gets a WM_PAINT through the message queue. On getting the WM_PAINT message, the control paints its background and then the foreground if necessary. Double-buffering and transparency is honored while painting and then the OnPaint event is fired to give the user a chance to perform his custom painting.

 

With this background, let’s look at the above mentioned three functions in more detail,

 

Control.Invalidate( ) / Control.Invalidate(bool) / Control.Invalidate(Rectangle) / Control.Invalidate(Rectangle, bool) / Control.Invalidate(Region) / Control.Invalidate(Region, bool)

 

            The bool parameter denotes whether the user wants to invalidate the child controls of the control on which he is calling Invalidate. The Rectangle parameter are the bounds to invalidate and the region parameter is the region to invalidate. All the overloads essentially end up calling one of the RedrawWindow, InvaliateRect or InvalidateRgn functions. If RedrawWindow is called then this may result in a WM_PAINT message being posted to the application message queue (to invalidate the child controls).

            The important thing to note here is that these functions only “invalidate” or “dirty” the client area by adding it to the current update region of the window of the control. This invalidated region, along with all other areas in the update region, is marked for painting when the next WM_PAINT message is received. As a result you may not see your control refreshing (and showing the invalidation) immediately (or synchronously).

 

Control.Update()

 

Update function calls the UpdateWindow function which updates the client area of the control by sending WM_PAINT message to the window (of the control) if the window's update region is not empty. This function sends a WM_PAINT directly to WNDPROC() bypassing the application message queue.

Thus, if the window update region is previously “invalidated” then calling “update” would immediately "update" (and cause repaint) the invalidation.

 

Control.Refresh()

 

            By now, you might have guessed what Refresh( ) would be doing. Yes, it calls Invalidate(true) to invalidate the control and its children and then calls Update( ) to force paint the control so that the invalidation is synchronous.

Posted by subhagpo | 1 Comments
Filed under:

How can TypeDescriptor and TypeDescriptionProvider be used?

Brian explains how these features in whidbey can be used to design abstract forms.

As usual, a great post by Brian.

 

Posted by subhagpo | 0 Comments
Filed under:

Winforms Tips and Tricks:

Coding:

 

1)       Use BeginUpdate( ) and EndUpdate( ) while performing any bulk operation that would cause the window to redraw itself.

a.       Large number of items to ListView.

b.       Large number of nodes to TreeView.

 

BeginUpdate() sends a WM_REDRAW message passing FALSE as redraw state to the window. This stops any paint messages being sent or processed. After the bulk operation is done EndUpdate( ) should be called which sends a WM_REDRAW message passing TRUE as redraw state. This is followed by calling Invalidate( ) which adds a rectangle (normally this is the clientRectangle) to the specified window's update region. The update region represents the portion of the window's client area that must be redrawn when the window receives the next WM_PAINT message.

 

We provide this public API on the controls that need it like ComboBox, ListBox, ListView, TreeView. Thus following a call to BeginUpdate(), any redrawing caused by operations performed on the above mention controls is deferred until the call to EndUpdate().

MSDN help here 

 

2)       When list box is bound to a datasource he SelectedIndexChanged event fires 3 times.  If you set the datasource after the valuemember and displaymember then the selectedindex is only fired once.

 

3)       How Should the SelectedIndex event handled? 

You should set the DataSource last.  You can’t prevent SelectedIndexChanged from firing because the SelectedIndex does change (from -1 to 0).

 

4)       Is there any reason why Datasource should be set first? 

This is more efficient.  Setting the DisplayMember after the DataSource will cause the ComboBox to rebind (re-populate).