Welcome to MSDN Blogs Sign in | Join | Help

IServiceProvider vs. IServiceProvider

I've been using the COM IServiceProvider interface for a while now, mostly through extensions to the ATL's IServiceProviderImpl and BEGIN_SERVICE_MAP. Last week, I've been writing some managed code where I wanted to use the same pattern based on the System.IServiceProvider interface. Unfortunately, it turned out that it's not exactly the same interface.

On the first glance, the two interfaces look sufficiently similar. Both interfaces have only one method - QueryService() (QS) in the unmanaged version and GetService() (GS) in the managed version (I do prefer the QS name), that takes a service identifier and returns an object that implements this service, the actual contract and behavior of the two method is quite different. QueryService() takes two in parameters - a service identifier (SID) and an interface identifier (IID), while GetService() takes only on parameter - Type.

Since a SID is just a GUID, QS() offers the ability to have an implementation-agnostic service identification in the client code. The actual service implementation can be changed completely while the SID used to discover the service can stay the same. Moreover, this allows the service provider implementation to be the one that maps SIDs and services.

The Type parameter in GS() on the other hand implies that the service discovery should be done based on the actual type of the implementation. This prevents services polimorphism, as any new implementation needs to be discovered through it's type, thus cannot replace previous implementation. It also requires more tight coupling between the IServiceProvider client and the services, as it forces the client to know the types that implement particular service. And at service implementation level, it makes it impossible to have aggregated services that consist of two or more objects that implement different facets, unless the service itself has a facet discovery mechanism. (Though, since a service is usually treated as a logical entity, it could be argued that the facet management is a function of the service itself, not the service provider)

Of course, it is possible to have an GS() implementation that treats the Type parameter as a random string identifier, thus mimicing the QS() behavior. However, while it does solve the two big limitations of the managed IServiceProvider, I find this approach undesireable - intentionally violating an interface contract is guaranteed to cause problems down the road.

Posted by Franci Penov | 0 Comments
Filed under:

Sky's the limit...

...or rather, 5GB is the limit on SkyDrive. :-) Plus, it's now available in 38 markets. Oh, and I should probably mention it's not a Beta anymore.

For more details, head on the the SkyDrive team blog.

Posted by Franci Penov | 5 Comments
Filed under:

Enablilng dragging in Canvas, v3.5 edition

About a year ago I posted a class implemented couple of dependency properties you could attach to a Canvas and it's children and allowed dragging. The DPs were working quite well with any visual element inside the Canvas, including the standard controls like Button.

Unfortunately, with .Net v3.5 the attached behavior was broken due to new behavior of some of the controls. For example, the Button control now supports three different OnClick behaviors - on hover, on press and on release. My DP code depends on the MouseLeftButtonDown event being fired; however, the new Button behavior was suppressing this event.

Fortunately, the fix for this is quite easy. Instead of hooking the MouseLeftButtonDown, MouseLeftButtonUp and MouseMove events, I now hook the PreviewMouseLeftButtonDown, PreviewMouseLeftButtonUp and PreviewMouseMove events. This allows my code to enable dragging on Buttons, without modifying their behavior.

Posted by Franci Penov | 0 Comments
Filed under:

Yahoo! Mail Web Service - interesting business model

Dare posted some thoughts on the limitations of the new Yahoo! Mail Web Service. One thing he missed to mention though, is how that Web Service ties into Yahoo! Mail subscription model:

"Yahoo! Mail offers an incentive for developers to build applications using the full functionality available for premium Yahoo! Mail accounts. Specifically, Yahoo! Mail provides a commission of $10.00 for every new Yahoo! Mail Plus account referred by developers."

It would be interesting to see how many developers would see this as an incentive to build an application around the Yahoo! Mail Web Service. It would be even more interesting to see the conversion rates overall and per application.

On more practical side, maybe I could find couple of hours this weekend and throw in together some WPF/WCF app to check my Yahoo! email. :-)

Posted by Franci Penov | 0 Comments
Filed under:

IObjectWithBrowserSiteImpl - base template for IE BHO or toolbars

If you want to extend IE with a browser helper object or a toolbar, you need to implement IObjectWithSite interface. ATL has a basic IObjectWithSiteImpl<> template that helps a bit.  Most BHOs and toolbars also subscribe to the browser events. ATL has a helper template for that as well - IDispEventImpl<>. Thus, pretty much any IE extension implemented with ATL inherits both templates. So, I've decided to implement a new template - IObjectWithBrowserSite<>, that besides storing the site pointer as IUnknown, also stores a pointer to IWebBrowser2 and subscribes to the browser events.

The new template declaration looks like this:

template <class T, UINT nID>
class ATL_NO_VTABLE IObjectWithBrowserSiteImpl :
   
public IObjectWithSiteImpl<T>,
   
public IDispEventImpl<nID, T, &DIID_DWebBrowserEvents2, &LIBID_SHDocVw, 1, 1>
{
public:
    // IObjectWithSite
   
IFACEMETHOD(SetSite)(IUnknown *pUnkSite)
    {
        HRESULT hr = S_OK;

        // Silently ignore any failure from CleanupSite
       
CleanupSite();

        // Store the IUnknown pointer to the new site
       
hr = IObjectWithSiteImpl<T>::SetSite(pUnkSite);

        if (SUCCEEDED(hr))
            hr = SetupSite();

        if (FAILED(hr))
           
// Our setup logic failed; the real failure that caused
           
// us to clean up here is more important, so propagate this one to the site
           
// Silently ignore any failure from CleanupSite;
           
CleanupSite();

        return hr;
    }

    // DWebBrowserEvents2
   
STDMETHOD(OnQuit)()
    {
       
return DetachBrowserEvents();
    }

protected:
    // Setup and cleanup on site change
   
STDMETHOD(SetupSite)()
    {
        HRESULT hr = S_OK;

        if (m_spUnkSite)
        {
            // We have a site; get the new browser pointer
           
m_pWebBrowser = m_spUnkSite;

            if (m_pWebBrowser == NULL)
            {
                hr = E_INVALIDARG;

                // We have a site, but it's not a browser; get the new browser pointer from the site's IServiceProvider
               
CComQIPtr<IServiceProvider> pServiceProvider = m_spUnkSite;

                if (pServiceProvider != NULL)
                    hr = pServiceProvider->QueryService(SID_SInternetExplorer, IID_IWebBrowser2, (
void**)&m_pWebBrowser);
            }

            if (m_pWebBrowser != NULL)
               
// Attach to the new browser events
               
hr = AttachBrowserEvents();
        }

        return hr;
    }

    STDMETHOD(CleanupSite)()
    {
       
if (m_pWebBrowser != NULL)
        {
           
// Detach from the current site events
           
HRESULT hr = DetachBrowserEvents();

            // IE7 disconnects our connection points _before_ calling us with SetSite(NULL)
            // so DispUnadvise() returns CONNECT_E_NOCONNECTION (0x80040200)
            if (hr == CONNECT_E_NOCONNECTION)
                hr = S_OK;
 

            // Release the pointer to the current hosting web browser
           
m_pWebBrowser.Release();
        }

        // We are done with the current site
       
return IObjectWithSiteImpl<T>::SetSite(NULL);
    }

    // Browser event connection methods
   
STDMETHOD(AttachBrowserEvents)()
    {
       
// ATL's event cookie is 0xFEFEFEFE, when the sink is not advised
       
if (m_dwEventCookie != 0xFEFEFEFE)
           
return S_OK;

        return DispEventAdvise(m_pWebBrowser, &DIID_DWebBrowserEvents2);
    }

    STDMETHOD(DetachBrowserEvents)()
    {
       
// ATL's event cookie is 0xFEFEFEFE, when the sink is not advised
       
if (m_dwEventCookie == 0xFEFEFEFE)
           
return S_OK;

        return DispEventUnadvise(m_pWebBrowser, &DIID_DWebBrowserEvents2);
    }

    // Hosting browser
   
CComQIPtr<IWebBrowser2> m_pWebBrowser;
}

Here's how you would use that template as a base of your CBho class. The magic numebr 1 in the template instantiation below is the ID of the browser control, by which we will reference its events in the sink map. If your class subscribes to events from other objects, like ActiveX controls, you need to make sure the browser control ID is unique an d does not collide with another one.

class CBho;

#define IDC_BROWSER 1

typedef IObjectWithBrowserSiteImpl<CBho, IDC_BROWSER> CBhoBaseBrowserSiteImpl;

class ATL_NO_VTABLE CBho :
   
public CComObjectRootEx<CComSingleThreadModel>,
   
public CComCoClass<CBho, &CLSID_Bho>,
   
public CBhoBaseBrowserSiteImpl

The typedef of CBhoBaseBrowserSiteImpl is not really necessary, but it does make the your code more readable when you need to call the methods of IObjectWithBrowserSiteImpl<>.

There are couple more things you need to do to get this working. First, you need to add IObjectWithSite to your interface map, so IUnknown::QueryInterface() implementation will return proper pointer. Second, you need to add sink map for your events. Unfortunately, ATL sink macros don't support chaining, so the whole DWebBrowserEvents2 sink map needs to be in your class. This means that while the IObjectWithBrowserSiteImpl<> template defines standard OnQuit() handler, it's not hooked by default to the DWebBrowserEvents2::OnQuit event. You need to do that yourself in order to get yourself detached from the browser events. (Note: Although IE7 will take care of that, your code should not rely on that and should behave well; plus you might be hosted in IE6 or another host) There are two options - you can hook your own method and call OnQuit() explicitly, or you can hook OnQuit() and optionally override it (not forgetting to call the base implementation yourself :-)).

Here are the bare minimum sink and interface maps you need to have. Note the magin number 1 in the

BEGIN_SINK_MAP(CBho)
    SINK_ENTRY_EX(IDC_BROWSER, DIID_DWebBrowserEvents2, DISPID_ONQUIT, OnQuit)
END_SINK_MAP()

BEGIN_COM_MAP(CBho)
    COM_INTERFACE_ENTRY(IObjectWithSite)
END_COM_MAP()

You don't need to implement SetSite() itself; you can see that I've split it into two separate methods - SetupSite() and CleanupSite() which you can override. Of course, you still have the choice to override the provided SetSite implementation and do whatever you need. I find this structure somewhat cleaner as the setup and the cleanup logic are strictly separated; plus, now on setup failure the code does automatic cleanup.

If you implement an IE toolbar instead of a BHO, the pointer you get on SetSite() is not a pointer to the browser control. You can still use the above template as a base IObjectWithSite class, though. The SetupSite() implementation will take care of getting the browser pointer through the toolbar site's IServiceProvider interface.

Posted by Franci Penov | 0 Comments
Filed under: ,

Enabling dragging in Canvas, part 2

Update: There were some bugs in the code, which caused funny behavior with elements that have a render transform applied to them. I've updated the code below to fix these.

In part 1 we covered how the dragging behavior will be attached to the Canvas object. Let's take a look now at the actual dragging code.

Before we get to the actual code, we'll need some data members to keep some state between the events. These are mostly self explanatory, with the exception of _canvasLeft and _canvasTop. We'll use these to keep the original position of the element that we drag around in case we cancel the drag.

protected Canvas owner;
protected Point _lastLeftDown;
protected double _canvasLeft;
protected double _canvasTop;
protected bool _isDragging = false;

The code that support the dragging is split into two groups - first is the logic that determines that we are dragging something and how far away and the second is how we apply this to the actual Canvas children. Let's first look into how we determine we are dragging someting. We start on mouse left button down event. We check that the element generating the event belongs to the Canvas and that it has the IsDraggable property set on it. Then we save the original position of the element and capture the mouse. There's an additional check that this lbutton event is not a double click event.

public void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
   
FrameworkElement dragElement = e.Source as FrameworkElement;

    if ((dragElement == null) || (dragElement == this.owner))
       
return;

    if (!GetIsDraggable(dragElement))
       
return;

    // Track mouse click position for offsetting the drag element position
   
_lastLeftDown = e.GetPosition(this.owner);

    if ((e.ClickCount == 1) && (!_isDragging))
   
{
        // Capture mouse only on the left button down for a single click
       
// We want to avoid dragging on double click
       
dragElement.CaptureMouse();
    }
   
else
   
{
       
// Cancel any pending drag
       
StopDragging(dragElement, Mouse.GetPosition(this.owner), false);
    }
}

Obviously, on mouse left button up we need to stop dragging as well.

public void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    FrameworkElement dragElement = e.Source as FrameworkElement;

    if ((dragElement == null) || (dragElement == this.owner))
        return;

    StopDragging(dragElement, Mouse.GetPosition(this.owner), false);
}

The actual stop logic is also very simple. All it does is release the mouse capture and raise the DragCompletedEvent with the point to which we've dragged the element and whether the drag was canceled.

/// <summary>
/// Stop dragging, raise the drag completed event is necessary and release the mouse capture
/// </summary>
/// <param name="point">Device coodinates for the drag completed event</param>
/// <param name="canceled">True if drag was canceled</param>
protected void StopDragging(FrameworkElement dragElement, Point point, bool canceled)
{
    _isDragging =
false;

    if (Mouse.Captured == dragElement)
    {
       
// We still have the mouse capture, release it
       
dragElement.ReleaseMouseCapture();
    }

    // Signal end dragging
   
dragElement.RaiseEvent(new DragCompletedEventArgs(point.X - _lastLeftDown.X, point.Y - _lastLeftDown.Y, canceled));
}

Most of the actual drag logic is in the mouse move event. In this code we determnie two things - have we actually started a drag and how far have we dragged, if so. The first one is determined by checking if the mouse has moved more than an arbitrary number of pixels in any direction (and obviously you can tweak how precise that logic is) and if so, we actually flag a drag start and raise the DragStart event on the drag element. The second is based on whether we have detected a drag start already and whether it's still the same element that initially was dragged (to deal with the mouse moving outside of the Canvas or the window and other odd situations) and raising the DragDelta event on the drag element.

public void OnMouseMove(object sender, MouseEventArgs e)
{
   
FrameworkElement dragElement = e.Source as FrameworkElement;

    if ((dragElement == null) || (dragElement == this.owner))
       
return;

    Point point = Mouse.GetPosition(this.owner);

    if (_isDragging && ((Mouse.Captured != dragElement) || (Mouse.LeftButton != MouseButtonState.Pressed)))
    {
        StopDragging(dragElement, point,
true);
    }
   
else
   
{
       
if (!_isDragging && (Mouse.Captured == dragElement))
        {
           
if (!AreReallyClose(point, _lastLeftDown))
            {
               
// we are now officially dragging, raise drag start
               
_isDragging = true;

                dragElement.RaiseEvent(new DragStartedEventArgs(point.X - _lastLeftDown.X, point.Y - _lastLeftDown.Y));
           
}
        }

        if (_isDragging)
        {
           
// Raise the drag delta
           
dragElement.RaiseEvent(new DragDeltaEventArgs(point.X - _lastLeftDown.X, point.Y - _lastLeftDown.Y));
        }
    }
}

protected static bool AreReallyClose(Point p1, Point p2)
{
   
return (Math.Abs(p1.X - p2.X) < .001 && Math.Abs(p1.Y - p2.Y) < .001);
}

Now comes the second part of the drag logic. We have the drag events raised on the canvas child we are dragging. This enables us to let the element deal with the fact that it's being dragged around before the canvas gets those events. If the element does not do anything, the events are bubbled up. A normal Canvas panel doesn't handle these events, normally they would bubble up to the canvas parent. However, our drag helper has attached itself to the events on the canvas and can update the dragged element position. This approach gives us the flexibility to later one attach the helper to a more advanced Canvas descendant that knows how to deal with drag events and can choose to react to them or let them go to our handler.

protected void OnDragStarted(object sender, DragStartedEventArgs e)
{
   
FrameworkElement dragElement = e.Source as FrameworkElement;

    if ((dragElement == null) || (dragElement == this.owner))
       
return;

    _canvasLeft = Canvas.GetLeft(dragElement);
    if (Double.IsNaN(_canvasLeft))
        _canvasLeft = 0.0;

    _canvasTop = Canvas.GetTop(dragElement);
   
if (Double.IsNaN(_canvasTop))
        _canvasTop = 0.0;

    e.Handled = true;
}

protected void OnDragDelta(object sender, DragDeltaEventArgs e)
{
   
FrameworkElement dragElement = e.Source as FrameworkElement;

    if ((dragElement == null) || (dragElement == this.owner))
       
return;

    Canvas.SetLeft(dragElement, _canvasLeft + e.HorizontalChange);
   
Canvas.SetTop(dragElement, _canvasTop + e.VerticalChange);
    this.owner.UpdateLayout();

    e.Handled = true;
}

protected void OnDragCompleted(object sender, DragCompletedEventArgs e)
{
   
FrameworkElement dragElement = e.Source as FrameworkElement;

    if ((dragElement == null) || (dragElement == this.owner))
       
return;

    if (e.Canceled)
    {
    
    Canvas.SetLeft(dragElement, _canvasLeft);
       
Canvas.SetTop(dragElement, _canvasTop);
       
this.owner.UpdateLayout();
    }

    e.Handled = true;
}

Of course, there are some improvements that could be done here, like handling keyboard modifiers to the dragging or constraining the drag position of the element in the visible portion of the Canvas. However, these can be easily added to the existing logic.

I've attached the full class code to this post, so you don't have to assemble it yourself from the pieces in the two posts.

Posted by Franci Penov | 1 Comments
Filed under:

Attachment(s): CanvasDragHelper.zip

Incoming posts

Here's short list of topics I am planning to write about in the next month after I finsh the CanvasDragHelper (and if I can find the time). I am going to use this post as a simple survey to see what people would like to see first. Of course, I might not get to some of these.

1. (COM/CLR/WPF) Scriptable WPF application - a sample WPF aplication that exposes automation objects and runs as a local COM server.
2. (COM/IE) IObjectWithBrowserSiteAndDispEventImpl - an ATL-like template that implements IObjectWithSite (by using IObjectWithSiteImpl), queries the site for IWebBrowser and subscribes to DWebBrowserEvents2. Can be used as a base class for IE BHOs and Toolbars.
3. (WPF) Pile panel - a Canvas based panel that add rotation property to it's children.
4. (WPF) Adding Adorners using a simple Ramora DP.
5. (WPF) Sample WPF app that draws in the non-client area. Well, it doesn't actualy draw in the NC area, but it simulates it quite well even under Vista (with all the DWM goodies)
6. (COM) ATL CAxWindow, WM_CREATE and CoInitialize/CoUnitialize balancing issue.

If you are interested in any of these topic, post a comment. (And yes, I'll be using this post also as a rude form of feedback on how many people still read me after nearly two years of silence)

Posted by Franci Penov | 0 Comments

Enabling dragging in canvas

When developing UI, most of the time we want the controls nicely sized, ordered and aligned automatically. But sometimes it's just fun to put them free floating and be able to push them around. The Canvas panel allows for absolute positioning of the controls on it; however, there's nothing in WPF that would allow us to interact with the control position on it.

Since the Canvas layout is using absolute positioning and we only want to allow changing of the position of, there is no need to implement new panel. We could implement the drag behavior and just attach it to the Canvas using the Ramora DP pattern. This can be done by creating a new class CanvasDragHelper, that will extend Canvas instances behavior.

public class CanvasDragHelper : DependencyObject
{
    protected Canvas owner;
}

We'll start by defining three dependency properties. We need one DP to attach the helper object to the canvas, one to attach to the elements we want to support being dragged around and one that we will set on the Canvas in our XAML in order to enable the behavior:

/// <summary>
/// Property used to cache the drag helper instance. It also removes the necessity of additional storage
/// to hold the drag helper instances
/// </summary>
protected static readonly DependencyPropertyKey CanvasDragHelperPropertyKey = DependencyProperty.RegisterAttachedReadOnly("CanvasDragHelper",
    typeof(CanvasDragHelper), typeof(CanvasDragHelper), new FrameworkPropertyMetadata(null));

public static readonly DependencyProperty CanvasDragHelperProperty = CanvasDragHelperPropertyKey.DependencyProperty;

public static CanvasDragHelper GetCanvasDragHelper(DependencyObject obj)
{
    return (CanvasDragHelper)obj.GetValue(CanvasDragHelperProperty);
}

protected static void SetCanvasDragHelper(DependencyObject obj, CanvasDragHelper value)
{
    obj.SetValue(CanvasDragHelperPropertyKey, value);
}

/// <summary>
/// This is property that specifies if a framework element is draggable
/// </summary>
public static readonly DependencyProperty IsDraggableProperty = DependencyProperty.RegisterAttached("IsDraggable",
   
typeof(bool), typeof(CanvasDragHelper), new FrameworkPropertyMetadata(false));

public static bool GetIsDraggable(DependencyObject obj)
{
   
return (bool)obj.GetValue(IsDraggableProperty);
}

public static void SetIsDraggable(DependencyObject obj, bool value)
{
    obj.SetValue(IsDraggableProperty, value);
}

/// <summary>
/// This is the main property that activates the drag helper. Set this property to true on the canvas to enable the behavior.
/// </summary>
public static readonly DependencyProperty AllowChildrenDragProperty = DependencyProperty.RegisterAttached("AllowChildrenDrag",
   
typeof(bool), typeof(CanvasDragHelper), new FrameworkPropertyMetadata(false, OnAllowChildrenDragChanged));

public static bool GetAllowChildrenDrag(DependencyObject obj)
{
   
return (bool)obj.GetValue(AllowChildrenDragProperty);
}

public static void SetAllowChildrenDrag(DependencyObject obj, bool value)
{
    obj.SetValue(AllowChildrenDragProperty, value);
}

The first two DP are really straightforward - they only hold data in them. The third DP on the other hand has some logic. In it's Changed event handler we need to create the CanvasDragHelper instance and attach it to the Canvas.

/// <summary>
/// Handler for when AllowChildrenDrag changes.
/// If AllowChildrenDrag is set to true, attach a CanvasDragHelper instance
/// (if there's one already, don't do anything)
/// </summary>
/// <param name="target">CanvasDragHelper target. Must be Canvas.</param>
/// <param name="e">Event args</param>
protected static void OnAllowChildrenDragChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
    Canvas element = target
as Canvas;

    Debug.Assert(element != null, "Can only use CanvasDragHelper with a Canvas");

    // Is there a drag helper instance attached?
   
CanvasDragHelper dragHelper = (CanvasDragHelper)GetCanvasDragHelper(element);

    if ((bool)e.NewValue == false)
    {
       
if (dragHelper != null)
        {
            // Yes, let's detach it from the canvas events
           
dragHelper.Detach();

            // ...and remove the instance from the canvas
           
SetCanvasDragHelper(element, null);
        }
    }

    if (dragHelper != null)
    {
       
// Yes, we already have a helper attached, so do noting
       
return;
    }

    // No, create new drag helper instance and attach it to the canvas events
   
dragHelper = new CanvasDragHelper(element);

    // ...and attach the instance to the canvas
   
SetCanvasDragHelper(element, dragHelper);
}

The handler logic creates a new instance and passes the Canvas element to it's constructor. In the constructor we'll subscribe to the Canvas events we are interested. We also need a method to detach from them, if the AllowChildrenDrag property on the Canvas is set to false. And to make GC's life easier, we will actually set that property to false on the Canvas' OnUnloaded event. :-)

protected CanvasDragHelper(Canvas owner)
{
   
this.owner = owner;

    Attach();
}

protected void Attach()
{
   
this.owner.AddHandler(Thumb.DragStartedEvent, new DragStartedEventHandler(OnDragStarted));
   
this.owner.AddHandler(Thumb.DragDeltaEvent, new DragDeltaEventHandler(OnDragDelta));
   
this.owner.AddHandler(Thumb.DragCompletedEvent, new DragCompletedEventHandler(OnDragCompleted));
   
this.owner.AddHandler(Canvas.MouseLeftButtonDownEvent, new MouseButtonEventHandler(OnMouseLeftButtonDown));
   
this.owner.AddHandler(Canvas.MouseLeftButtonUpEvent, new MouseButtonEventHandler(OnMouseLeftButtonUp));
   
this.owner.AddHandler(Canvas.MouseMoveEvent, new MouseEventHandler(OnMouseMove));
   
this.owner.AddHandler(FrameworkElement.UnloadedEvent, new RoutedEventHandler(OnUnloaded));
}

protected void Detach()
{
    this.owner.RemoveHandler(Thumb.DragStartedEvent, new DragStartedEventHandler(OnDragStarted));
    this.owner.RemoveHandler(Thumb.DragDeltaEvent, new DragDeltaEventHandler(OnDragDelta));
   
this.owner.RemoveHandler(Thumb.DragCompletedEvent, new DragCompletedEventHandler(OnDragCompleted));
   
this.owner.RemoveHandler(Canvas.MouseLeftButtonDownEvent, new MouseButtonEventHandler(OnMouseLeftButtonDown));
   
this.owner.RemoveHandler(Canvas.MouseLeftButtonUpEvent, new MouseButtonEventHandler(OnMouseLeftButtonUp));
   
this.owner.RemoveHandler(Canvas.MouseMoveEvent, new MouseEventHandler(OnMouseMove));
   
this.owner.RemoveHandler(FrameworkElement.UnloadedEvent, new RoutedEventHandler(OnUnloaded));
}

public void OnUnloaded(object sender, RoutedEventArgs e)
{
    SetAllowChildrenDrag(
this.owner, false);
}

Now the only thing left is the actual dragging logic, which as you can guess from the above code will be done on the mouse LeftButton and Move events. That code will come in the next post.

Posted by Franci Penov | 1 Comments
Filed under:

Windows Live Toolbar build 130

We just released the latest build of Windows Live Toolbar. There are no new features in this release, it's fucosed mostly on stability and performance improvements. If you use the toolbar, I'd suggest you upgrade. And if you're not using WLT, well, be a chap and give it a test drive, would you? :-)

You can grab the bits from the WLT web page. After the install, you should be running version 03.01.0000.0130. You can check your toolbar version on the General page of the Settings dialog.

Posted by Franci Penov | 3 Comments
Filed under: ,

ASP.NET Ajax 1.0 RTW

ASP.NET Ajax 1.0 (formerly "Atlas") has been released to the web. You can read more about the framework and the control toolkit at http://ajax.asp.net (including some really nice viedeo demos), or grab the bits from here and the toolkit from here.
Posted by Franci Penov | 0 Comments
Filed under:

Goodbye, Max!

This is a long overdue post. Couple of months ago Microsoft Max was closed. I was too busy with the transition to the new stuff I am working currently, though, so I neglected to post anything about this. 

The time I spent on Max was full of fun (great team!) and excitement (awesome new platform!...) mixed with some frustration (...still being developed...). I've learned a lot about WPF and got to play in the declarative UI land. I got to work and play with bunch of smart, hard-working and passionate people.

Unfortunately, every good thing must have an end and the project ended. The team got a new name and moved to the Windows Live division. We took over some existing pieces and started working over some new pieces. It's an interesting mix of ingredients, and we plan to bake something exciting out of it. :-)

On the programming side, I am back in the unmanaged world. Turned out my several-years stunt in the .Net world managed (pun intended) to blur out the majority of my C++ skills. It feels weird to deal with pointers and memory management, to initialize COM appartments and marshal objects around, and to loop message pumps and manage HWNDs. I definitely miss the simplicity of C# and the elegance of XAML.

Still, even though Max the project was closed, some pieces of it might yet show up in unexpected places... :-)

Posted by Franci Penov | 0 Comments

Windows Live Writer Beta announced

Grab the bits from here. For more details go to Writer Zone.

Posted using Windows Live Writer (Beta)

Posted by Franci Penov | 1 Comments
Filed under:

Goodbye, Indigo! Hello, Max!

The three years I worked on WCF were a fun ride, but it's time for me to move on to other challenges. Starting tomorrow I am leaving the Indigo team and moving on to the Max team as a dev.

Posted by Franci Penov | 1 Comments

Endpoints configuration

When you write self-hosted Indigo application, you need to define your endpoints yourself. You can do this through your code; however, every time you decide to change your binding or add new endpoint, you'll have to recompile your application. Or you can do it through your config file.

Here's a sample self-hosted Indigo application. To keep the post shorter, I skipped over the service contract and the service implementation.

ServiceHost<TestService> testService = null;
testService = new ServiceHost<TestService>();
testService.BaseAddresses.Add(new Uri("
http://localhost/TestService"));
testService.BaseAddresses.Add(new Uri("net.tcp://localhost/TestService"));
testService.Open();

Note that I add two base addresses for my service host. This is not necessary to get your endpoints to work, so forget about these for now. Here's how to define your endpoint in the config file:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.serviceModel>
    <services>
      <service serviceType="TestService">
        <endpoint address="http://localhost/Test/Service" bindingSectionName="basicProfileBinding" contractType="ITestService" />
      </service>
    </services>
  </system.serviceModel>
</configuration>

This will add one endpoint to your service listening at http://localhost/Test/Service using the BP 1.0 binding. If you want to add different bindings, you can add more endpoints:

<endpoint address="WSProfile" bindingSectionName="wsProfileBinding" contractType="ITestService" />
<endpoint address="TCPTransport" bindingSectionName="netProfileTcpBinding" contractType="ITestService" />

Now your service will listen on multiple endpoints with different bindings for each of them.

Notice that for the last two endpoints, I didn't specify full address. Instead, I specified relative address. Indigo will match the transport defined by the endpoint binding to the base addresses I added in the code that use the same protocol scheme. Thus, the WSProfile endpoint will listen at http://locahost/TestService/WSProfile address and the TCPTransport endpoint will listen at net.tcp://localhost/TestService/TCPTransport address.

That's enough for this post. Go ahead and experiment with your endpoint config. And in the next post I'll show you how to configure your service behavior through config.

Posted by Franci Penov | 0 Comments

Indigo CTP is out there!

Indigo CTP is out there! Well, at least for the MSDN subscribers. Go grab the bits from http://msdn.microsoft.com/subscriptions/default.aspx. If you also want documentation and samples, you'll need the WinFX SDK - http://winfx.msdn.microsoft.com/ as well.

Go play with your new toys and don't forget to tell us what you do like, what you don't like and what plain sucks.

Posted by Franci Penov | 0 Comments
More Posts Next page »
 
Page view tracker