Tag: Blend

VS2010, Silverlight, Blend, Phone 7 Upgrade

I’ve been holding back on moving to Visual Studio 2010 RTM because I wanted to keep all the bits that I had on my machine working including the Windows Phone 7 preview developer bits handed out at MIX10. But I was finding it more painful to stay there ( because e.g. I had products demanding .NET Framework 4.0 in order to install ) and new bits just come out for Windows Phone 7 development so I thought I’d upgrade. I installed Visual Studio 2010 RTM. That was painless for me although uninstalling the previous bits took a long time because I messed up the uninstall order and ended up in a situation where I’d not got Visual Studio installed but still had some Visual Studio add-on tooling installed and the latter wasn’t happy about uninstalling without the former being present. I fixed this in the end by putting VS2010 RC back on my box whilst uninstalling some of the other add-on bits. My mistake. With Visual Studio 2010 RTM installed I added the extra bits that I need ( for now ) which were; The latest drop of the...(read more)

Building a “real” extension for Expression Blend

.Last time I showed you how to get started building extensions for Expression Blend. Lets build a useful extension this time and go a bit deeper into Blend.

  • Source of project  => here
  • Compiled dll => here (extract into /extensions folder of Expression Blend)

 

The Extension

When working on large Xaml files in Blend it’s often hard to find a specific control in the "Objects and Timeline Pane”. An extension that searches the active document and presents all elements that satisfy the query would be helpful. When the user starts typing a search query a search will be performed and the results are shown in the list. After the user selects an item in the results list, the control in the "Objects and Timeline Pane” will be selected. Below is a sketch of what it is going to look like.

image

 

The Solution

Create a new WPF User Control project as shown in the earlier tutorial in the Configuring the extension project section, but name it AdvancedSearch this time. Delete the default UserControl1.Xaml to clear the solution (a new user control will be added later thought, but adding a user control is easier then renaming one).

Create the main entry point of the addin by adding a new class to the solution and naming this  AdvancedSearchPackage. Add a reference to Microsoft.Expression.Extensibility and to System.ComponentModel.Composition . Implement the IPackage interface and add the Export attribute from the MEF to the definition. While you’re at it. Add references to Microsoft.Expression.DesignSurface, Microsoft.Expression.FrameWork and Microsoft.Expression.Markup. These will be used later.

The Load method from the IPackage interface is going to create a ViewModel to bind to from the UI. Add another class to the solution and name this AdvancedSearchViewModel. This class needs to implement the INotifyPropertyChanged interface to enable notifications to the view.  Add a constructor to the class that takes an IServices interface as a parameter.

Create a new instance of the AdvancedSearchViewModel in the load method in the AdvanceSearchPackage class. The AdvancedSearchPackage class should looks like this now:

 

using System.ComponentModel.Composition;
using Microsoft.Expression.Extensibility;
 
namespace AdvancedSearch
{
    [Export(typeof(IPackage))]
    public class AdvancedSearchPackage:IPackage
    {
 
        public void Load(IServices services)
        {
            new AdvancedSearchViewModel(services);            
        }
 
        public void Unload()
        {
            
        }
    }
}

 

Add a new UserControl to the project and name this AdvancedSearchView. The View will be created by the ViewModel, which will pass itself to the constructor of the view. Change the constructor of the View to take a AdvancedSearchViewModel object as a parameter. Add a private field to store the ViewModel and set this field in the constructor. Point the DataContext of the view to the ViewModel. The View will look something like this now:

 

namespace AdvancedSearch
{    
    public partial class AdvancedSearchView:UserControl
    {
        private readonly AdvancedSearchViewModel _advancedSearchViewModel;
 
        public AdvancedSearchView(AdvancedSearchViewModel advancedSearchViewModel)
        {
            _advancedSearchViewModel = advancedSearchViewModel;
            InitializeComponent();
            this.DataContext = _advancedSearchViewModel;
        }
    }
}

 

The View is going to be created in the constructor of the ViewModel and stored in a read only property.

 

        public FrameworkElement View
        {
            get; private set;
        }
 
        public AdvancedSearchViewModel(IServices services)
        {
            _services = services;
            View = new AdvancedSearchView(this);
        }

The last thing the solution needs before we’ll wire things up is a new class, PossibleNode. This class will be used later to store the search results. The solution should look like this now:

 

image

Adding UI to the UI

The extension should build and run now, although nothing is showing up in Blend yet. To enable the user to perform a search query add a TextBox and a ListBox to the AdvancedSearchView.xaml file. I’ve set the rows of the grid too to make them look a little better. Add the TextChanged event to the TextBox and the SelectionChanged event to the ListBox, we’ll need those later on.

  <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="32" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <TextBox TextChanged="SearchQueryTextChanged" 
                 HorizontalAlignment="Stretch" 
                 Margin="4" 
                 Name="SearchQuery" 
                 VerticalAlignment="Stretch" />
        <ListBox SelectionChanged="SearchResultSelectionChanged"
                 HorizontalAlignment="Stretch"
                 Margin="4"
                 Name="SearchResult"
                 VerticalAlignment="Stretch" 
                 Grid.Row="1" />
    </Grid>

 

This will create a user interface like:

image

To make the View show up in Blend it has to be registered with the WindowService. The GetService<T> method is used to get services from Blend, which are your entry points into Blend.When writing extensions you will encounter this method very often. In this case we’re asking for an IWindowService interface. The IWindowService interface serves events for changing windows and themes, is used for adding or removing resources and is used for registering and unregistering Palettes. All panes in Blend are palettes and are registered thru the RegisterPalette method.

The first parameter passed to this method is a string containing a unique ID for the palette. This ID can be used to get access to the palette later.

The second parameter is the View.

The third parameter is a title for the pane. This title is shown when the pane is visible. It is also shown in the window menu of Blend.

The last parameter is a KeyBinding. I have chosen Ctrl+Shift+F to call the Advanced Search pane. This value is also shown in the window menu of Blend.

 

services.GetService<IWindowService>().RegisterPalette(
    "AdvancedSearch",
    viewModel.View,
    "Advanced Search",
    new KeyBinding
    {
        Key = Key.F,
        Modifiers = ModifierKeys.Control | ModifierKeys.Shift
    }
);
 

You can compiler and run now. After Blend starts you can hit Ctrl+Shift+F or go the windows menu to call the advanced search extension.

Searching for controls

The search has to be cleared on every change of the active document. The DocumentServices fires an event every time a new document is opened, a document is closed or another document view is selected. Add the following line to the constructor of the ViewModel to handle the ActiveDocumentChanged event:

 

_services.GetService<IDocumentService>().ActiveDocumentChanged += ActiveDocumentChanged;

 

And implement the ActiveDocumentChanged method:

 

private void ActiveDocumentChanged(object sender, DocumentChangedEventArgs e)
{
}

 

 

To get to the contents of the document we first need to get access to the “Objects and Timeline” pane. This pane is registered in the PaletteRegistry in the same way as this extension has registered itself. The palettes are accessible thru an associative array. All you need to provide is the Identifier of the palette you want. The Id of the “Objects and Timeline” pane is “Designer_TimelinePane”. I’ve included a list of the other default panes at the bottom of this article. Each palette has a Content property which can be cast to the type of the pane.

 

var timelinePane = (TimelinePane)_services.GetService<IWindowService>()
                        .PaletteRegistry["Designer_TimelinePane"]
                        .Content;

 

Add a private field to the top of the AdvancedSearchViewModel class to store the active SceneViewModel. The SceneViewModel is needed to set the current selection and to get the little icons for the type of control.

 

private SceneViewModel _activeSceneViewModel;

 

When the active SceneViewModel changes, the ActiveSceneViewModel is stored in this field. The list of possible nodes is cleared and an PropertyChanged event is fired for this list to notify the UI to clear the list. This will make the eventhandler look like this:

private void ActiveDocumentChanged(object sender, DocumentChangedEventArgs e)
{
    var timelinePane = (TimelinePane)_services.GetService<IWindowService>()
                                       .PaletteRegistry["Designer_TimelinePane"].Content;
 
    _activeSceneViewModel = timelinePane.ActiveSceneViewModel;
    PossibleNodes = new List<PossibleNode>();
    InvokePropertyChanged("PossibleNodes");    
}

The PossibleNode class used to store information about the controls found by the search. It’s a dumb data class with only 3 properties, the name of the control, the SceneNode and a brush used for the little icon. The SceneNode is the base class for every possible object you can create in Blend, like Brushes, Controls, Annotations, ResourceDictionaries and VisualStates. The entire PossibleNode class looks like this:

 

using System.Windows.Media;
using Microsoft.Expression.DesignSurface.ViewModel;
 
namespace AdvancedSearch
{
    public class PossibleNode
    {        
        public string Name { get; set; }        
        public SceneNode SceneNode { get; set; }
        public DrawingBrush IconBrush { get; set; }
    }
}

 

Add these two methods to the AdvancedSearchViewModel class:

 

public void Search(string searchText) { }
public void SelectElement(PossibleNode node){ }

 

Both these methods are going to be called from the view. The Search method performs the search and updates the PossibleNodes list.  The controls in the active document can be accessed thru TimeLineItemsManager class. This class contains a read only collection of TimeLineItems. By using a Linq query the possible nodes are selected and placed in the PossibleNodes list.

 

var timelineItemManager  = new TimelineItemManager(_activeSceneViewModel);
PossibleNodes =
    new List<PossibleNode>(
        (from d in timelineItemManager.ItemList
         where d.DisplayName.ToLowerInvariant().StartsWith( searchText.ToLowerInvariant())
         select new PossibleNode()
         {
             IconBrush = d.IconBrush,
             SceneNode = d.SceneNode,
             Name = d.DisplayName
         }).ToList()
    );
InvokePropertyChanged(InternalConst.PossibleNodes);

 

The Select method is pretty straight forward. It contains two lines.The first to clear the selection. Otherwise the selected element would be added to the current selection. The second line selects the nodes. It is given a new array with the node to be selected.

 

_activeSceneViewModel.ClearSelections();
_activeSceneViewModel.SelectNodes(new[] { node.SceneNode });

 

The last thing that needs to be done is to wire the whole thing to the View. The two event handlers just call the Search and SelectElement methods on the ViewModel.

 

private void SearchQueryTextChanged(object sender, TextChangedEventArgs e)
{
    _advancedSearchViewModel.Search(SearchQuery.Text);
}
 
private void SearchResultSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if(e.AddedItems.Count>0)
    {
        _advancedSearchViewModel.SelectElement(e.AddedItems[0] as PossibleNode);
    }
}

 

The Listbox has to be bound to the PossibleNodes list and a simple DataTemplate is added to show the selection. The IconWithOverlay control can be found in the Microsoft.Expression.DesignSurface.UserInterface.Timeline.UI namespace in the Microsoft.Expression.DesignSurface assembly. The ListBox should look something like:

 

<ListBox SelectionChanged="SearchResultSelectionChanged"
         HorizontalAlignment="Stretch" Margin="4"
         Name="SearchResult" VerticalAlignment="Stretch" Grid.Row="1" 
         ItemsSource="{Binding PossibleNodes}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">             
                <tlui:IconWithOverlay Margin="2,0,10,0"
                                      Width="12" Height="12" 
                                      SourceBrush="{Binding Path=IconBrush, Mode=OneWay}" 
                                   />
                <TextBlock Text="{Binding Name}"/>
            </StackPanel>
            
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

 

Compile and run. Inside Blend the extension could look something like below.

image

What’s Next

When you’ve got the extension running. Try placing breakpoints in the code and see what else is in there. There’s a lot to explore and build extension on. I personally would love an extension to search for resources.

Last but not least, you can download the source of project here.  If you have any questions let me know.

If you just want to use this extension, you can download the compiled dll here. Just extract the . zip into the /extensions folder of Expression Blend.

Notes

Target framework

I ran into some issues when using the .NET Framework 4 Client Profile as a target framework. I got some strange error saying certain obvious namespaces could not be found, Microsoft.Expression in my case. If you run into something like this, try setting the target framework to .NET Framework 4 instead of the client version.

 

Identifiers of default panes

 

 

 

Identifier Type Title
Designer_TimelinePane TimelinePane Objects and Timeline
Designer_ToolPane ToolPane Tools
Designer_ProjectPane ProjectPane Projects
Designer_DataPane DataPane Data
Designer_ResourcePane ResourcePane Resources
Designer_PropertyInspector PropertyInspector Properties
Designer_TriggersPane TriggersPane Triggers
Interaction_Skin SkinView States
Designer_AssetPane AssetPane Assets
Interaction_Parts PartsPane Parts
Designer_ResultsPane ResultsPane Results



Silverlight 4, Blend 4, MVVM, Binding, DependencyObject

I was thinking about an aspect of the new MVVM support that Expression Blend 4 has and how ( I think – someone can tell me if I’m wrong ) it all starts with a seemingly small change in data-binding in Silverlight. If I take a ViewModel like this into Expression Blend 4; using System; using System.ComponentModel; using System.Windows; using System.Windows.Input; using Microsoft.Expression.Interactivity.Core; namespace MvvmApplication2 { public class MainViewModel { public ICommand MessageBoxCommand { get { if (this.messageBoxCommand == null) { this.messageBoxCommand = new ActionCommand(parameter => { MessageBox.Show(string.Format("Parameter was {0}", parameter), null, MessageBoxButton.OK); }); } return(this.messageBoxCommand); } } ICommand messageBoxCommand; } } and we set the ViewModel as the DataContext for a view then I can use the Data window in Blend in order to create “sample data” around it; which then gives me a view such as; and it’s cool because I can then create some piece of UI like this...(read more)

Building extensions for Expression Blend 4 using MEF

Introduction

Although it was possible to write extensions for Expression Blend and Expression Design, it wasn’t very easy and out of the box only one addin could be used. With Expression Blend 4 it is possible to write extensions using MEF, the Managed Extensibility Framework.

Until today there’s no documentation on how to build these extensions, so look thru the code with Reflector is something you’ll have to do very often. Because Blend and Design are build using WPF searching the visual tree with Snoop and Mole belong to the tools you’ll be using a lot exploring the possibilities. 

Scott Barnes has written an article on his blog about How to hack Expression Blend. It provides some information on how to get to know more about the inside of Blend.

Configuring the extension project

Extensions are regular .NET class libraries. To create one, load up Visual Studio 2010 and start a new project. Because Blend is build using WPF, choose a WPF User Control Library from the Windows section and give it a name and location. I named mine DemoExtension1.

image

Because Blend looks for addins named *.extension.dll  you’ll have to tell Visual Studio to use that in the Assembly Name. To change the Assembly Name right click your project and go to Properties. On the Application tab, add .Extension to name already in the Assembly name text field.image

To be able to debug this extension, I prefer to set the output path on the Build tab to the extensions folder of Expression Blend. This means that everything that used to go into the Debug folder is placed in the extensions folder. Including all referenced assemblies that have the copy local property set to false.

image One last setting. To be able to debug your extension you could start Blend and attach the debugger by hand. I like it to be able to just hit F5. Go to the Debug tab and add the the full path to Blend.exe in the Start external program text field.image

Extension Class

Add a new class to the project.  This class needs to be inherited from the IPackage interface. The IPackage interface can be found in the Microsoft.Expression.Extensibility namespace. To get access to this namespace add Microsoft.Expression.Extensibility.dll to your references. This file can be found in the same folder as the (Expression Blend 4 Beta) Blend.exe file. Make sure the Copy Local property is set to false in this reference. After implementing the interface the class would look something like:

using Microsoft.Expression.Extensibility;
namespace DemoExtension1
{
    public class DemoExtension1:IPackage
    {
        public void Load(IServices services)
        {            
        }
        public void Unload()
        {         
        }
    }
}

 

 

 

These two methods are called when your addin is loaded and unloaded. The parameter passed to the Load method, IServices services, is your main entry point into Blend. The IServices interface exposes the GetService<T> method. You will be using this method a lot. Almost every part of Blend can be accessed thru a service. For example, you can use to get to the commanding services of Blend by calling GetService<ICommandService>() or to get to the Windowing services by calling GetService<IWindowService>().

To get Blend to load the extension we have to implement MEF. (You can get up to speed on MEF on the community site or read the blog of Mr. MEF, Glenn Block.)  In the case of Blend extensions, all that needs to be done is mark the class with an Export attribute and pass it the type of IPackage. The Export attribute can be found in the System.ComponentModel.Composition namespace which is part of the .NET 4 framework. You need to add this to your references.

using System.ComponentModel.Composition;
using Microsoft.Expression.Extensibility;
 
namespace DemoExtension1
{
    [Export(typeof(IPackage))]
    public class DemoExtension1:IPackage
    {

 

 

 

Blend is able to find your addin now.

Adding UI

The addin doesn’t do very much at this point. The WPF User Control Library came with a UserControl so lets use that in this example. I just drop a Button and a TextBlock onto the surface of the control to have something to show in the demo.image

To get the UserControl to work in Blend it has to be registered with the WindowService.  Call GetService<IWindowService>() on the IServices interface to get access to the windowing services. To get access to the IWindowService interface, add a reference to Microsoft.Expression.Framework to the project. The UserControl will be used in Blend on a Palette and has to be registered to enable it. This is done by calling the RegisterPalette on the IWindowService interface and passing it an identifier, an instance of the UserControl and a caption for the palette.

public void Load(IServices services)
{
    IWindowService windowService = services.GetService<IWindowService>();
    UserControl1 uc = new UserControl1();
    windowService.RegisterPalette("DemoExtension", uc, "Demo Extension");
}

 

 

 

After hitting F5 to start debugging Expression Blend will start. You should be able to find the addin in the Window menu now.

imageActivating this window will show the “Demo Extension” palette with the UserControl, style according to the settings of Blend.image

Now what?

Because little is publicly known about how to access different parts of Blend adding breakpoints in Debug mode and browsing thru objects using the Quick Watch feature of Visual Studio is something you have to do very often. This demo extension can be used for that purpose very easily.

Add the click event handler to the button on the UserControl. Change the contructor to take the IServices interface and store this in a field. Set a breakpoint in the Button_Click method.

public partial class UserControl1 : UserControl
{
    private readonly IServices _services;
 
    public UserControl1(IServices services)
    {
        _services = services;
        InitializeComponent();
    }
 
    private void button1_Click(object sender, RoutedEventArgs e)
    {
    }
}

 

 

 

Change the call to the constructor in the load method and pass it the services property.

public void Load(IServices services)
{
    IWindowService service = services.GetService<IWindowService>();
    UserControl1 uc = new UserControl1(services);
    service.RegisterPalette("DemoExtension", uc, "Demo Extension");
}

Hit F5 to compile and start Blend. Got to the window menu and start show the addin. Click on  the button to hit the breakpoint. Now place the carrot text _services text in the code window and hit Shift+F9 to show the Quick Watch window. Now start exploring and discovering where to find everything you need. image

 

More Information

The are no official resources available yet. Microsoft has released one extension for expression Blend that is very useful as a reference, the Microsoft Expression Blend® Add-in Preview for Windows® Phone. This will install a .extension.dll file in the extension folder of Blend. You can load this file with Reflector and have a peek at how Microsoft is building his addins.

Conclusion

I hope this gives you something to get started building extensions for Expression Blend. Until Microsoft releases the final version, which hopefully includes more information about building extensions, we’ll have to work on documenting it in the community.



  • Sponsored Links

  •  

    September 2010
    M T W T F S S
    « Aug    
     12345
    6789101112
    13141516171819
    20212223242526
    27282930  
  • .

    Copyright © 1996-2010 Answer My Query. All rights reserved.
    iDream theme by Templates Next | Powered by WordPress