WTF of WPF [Part One]- Templating & Styling

| 0 comments

Have you ever been bugged by WPF's strange behaviour for a long time and cannot stop asking:"WTF?".  Douglas Stockwell just met one of those confusing aspects of WPF, I just generalize his confusion about WPF's templating and styling into the following XAML repro code:

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <
Page.Resources>
        <
Style TargetType="{x:Type TextBlock}">
            <
Setter Property="TextElement.Foreground" Value="Cyan"/>
        </
Style>
        <
Style TargetType="{x:Type ContentPresenter}">
            <
Setter Property="TextElement.Foreground" Value="Cyan"/>
        </
Style>
        <
Style TargetType="{x:Type  AccessText}">
            <
Setter Property="TextElement.Foreground" Value="Cyan"/>
        </
Style>
        <
Style TargetType="{x:Type Rectangle}">
            <
Setter Property="Fill" Value="Cyan"/>
        </
Style>
        <
Style TargetType="{x:Type Image}">
            <
Setter Property="Source" Value="C:\WINDOWS\Web\Wallpaper\Home.jpg"/>
        </
Style>
        <
Style TargetType="{x:Type MediaElement}">
            <
Setter Property="Source" Value="C:\WINDOWS\clock.avi"/>
        </
Style>
        <
Style TargetType="{x:Type InkCanvas}">
            <
Setter Property="Background" Value="Cyan"/>
        </
Style>
        <
Style TargetType="{x:Type Border}">
            <
Setter Property="BorderThickness" Value="10"/>
            <
Setter Property="BorderBrush" Value="Cyan"/>
        </
Style>
        <
Style TargetType="{x:Type StackPanel}">
            <
Setter Property="Background" Value="Green"/>
        </
Style>
        <
Style TargetType="{x:Type Control}">
            <
Setter Property="Template">
                <
Setter.Value>
                    <
ControlTemplate>
                        <
Label>Inside Control</Label>
                    </
ControlTemplate>
                </
Setter.Value>
            </
Setter>
        </
Style>
        <
ControlTemplate x:Key="template">
            <!--
WTF, StackPanel, TextBlock, AccessText, ContentPresenter, InkCanvas,  Image, Rectangle,
              MediaElement etc cannot pick up their respective implicit styles here,
              one takeway from this empircal discovery is that Elements directly derived from FramworkElement cannot work in this scenario.
-->
            <
StackPanel>
                <
TextBlock Name="tb">inside TextBlock</TextBlock>
                <
AccessText>inside AccessText</AccessText>
                <
ContentPresenter>
                    <
ContentPresenter.Content>
                    inside ContentPresenter
                    </ContentPresenter.Content>
                </
ContentPresenter>
                <
InkCanvas/>
                <
Image/>
                <
Rectangle Width="40" Height="40"/>
                <
MediaElement/>
                <!--
WTF, Border can pick up implicit style here.-->
                <
Border Width="40" Height="40"/>
                <!--
WTF, Control can pick up implicit style here, since Border and Control can work here, our previous hypothesis breaks.-->
                <
Control/>
            </
StackPanel>
        </
ControlTemplate>
    </
Page.Resources>
    <
Control Template="{StaticResource template}"/>
</
Page>

When you have above XAML code runnning in XAMLPad, you will see that StackPanel((or other Panel derivatives), TextBlock, AccessText, ContentPresenter, InkCanvas, Image, Rectangle(or other Shape derivatives), MediaElement et al cannot pick up the implicit styles, well, this gives us an empirical understanding that elements directly derived from FrameworkElement cannot pick up implicit styles when placed inside FrameworkTemplate(DataTemplate, ControlTemplate, ItemsPanelTemplate), but how about Border and Control, those two privileged guys works quite different from their siblings, Anyway, Control derivatives(including Control itself) all work pretty well in this scenario. so question comes, why those aforementioned elements aren't created equal? Can we perceive this aspect of WPF as another example of breaking the principle of consistency.

Edit: Thanks for Doug's great find, if you look at the implementation of System.Windows.FrameworkElement.FindImplicitStyleResource() method from reflector, you will find that if the elements aren't Controls or Control derivatives, style lookup is limited within the templated parent boundary(In my example, the templated parent is Control), and as to the Border, the Border within the template still cannot pick up its implict style, but since Page's template has Border defined there, and in the Page case, Page is the templated parent, so the implicit style in Page's resources is within the search boundary for Border, so the Border can pick up the style there, to put it simple: the resolution of non-Control element's implicit style are limited within its templated parent boundary.

But still, my confusion remains, why the logic of style lookup works the way it is now, why grant Controls more privileges than other equal citizens of WPF?

DataBinding & Multi-Threading In WPF [Part Two]

| 1 comments

In my last post, I talked about a solution to overcome the current WPF limitation to enable multi-threaded data binding.

But there are one of drawback which I introduced in that solution. In that version of BindingCollection, I just simply marshal the collection changed notification back to UI thread, this sounds innocuous, but sometimes, this is not what we really expect, imagine that I have a bunch of listeners listening to BindingCollection.CollectionChanged events, some of them are free-threaded objects, but some of them would be single-threaded, actually CollectionView is the primary listener to this event. free threaded listeners can run on any threads, they are not bound to UI threads, so we shouldn't force them to run in the UI threads, but single-threaded listeners(DispatcherObjects etc) are actually bound to UI threads, so we should let them running inside UI threads as we previously did, to put it simple, we should treat free-threaded listeners and single-threaded listeners differently, so here comes another refined implementation of this collection, this time I call it BindableCollection, because I find that the name of BindingCollection is a bit misleading indeed, It could imply "a collection of Binding" which apparently is not what I want to do with it.

using System;
using System.Threading;
using System.ComponentModel;
using System.Collections.Generic;
using System.Windows.Threading;
using System.Collections.Specialized;
using System.Collections.ObjectModel;

namespace Sheva.Windows.Data
{
    /// <summary>
    /// An ObservableCollection&lt;T&gt; enhanced with capability of free threading.
    /// </summary>
    [Serializable]
    public class BindableCollection<T> : ObservableCollection<T>
    {
        /// <summary>
        /// Initializes a new instance of the<see cref="BindingCollection&lt;T&gt;">BindingCollection</see>.
        /// </summary>
        public BindableCollection() : base() {}

        /// <summary>
        /// Initializes a new instance of the<see cref="BindingCollection&lt;T&gt;">BindingCollection</see>
        /// class that contains elements copied from the specified List&lt;T&gt;.
        /// </summary>
        /// <param name="list">The list from which the elements are copied.</param>
        /// <exception cref="System.ArgumentNullException">The list parameter cannot be null.</exception>
        public BindableCollection(List<T> list) : base(list) {}

        /// <summary>
        /// Initializes a new instance of the<see cref="BindingCollection&lt;T&gt;">BindingCollection</see>
        /// class that contains elements copied from the specified IEnumerable&lt;T&gt;.
        /// </summary>
        /// <param name="list">The list from which the elements are copied.</param>
        /// <exception cref="System.ArgumentNullException">The list parameter cannot be null.</exception>
        public BindableCollection(IEnumerable<T> list)
        {
            if (list == null) throw new ArgumentOutOfRangeException("The list parameter cannot be null.");
            foreach (T item in list)
            {
                this.Items.Add(item);
            }
        }

        /// <summary>
        /// Occurs when an item is added, removed, changed, moved, or the entire list is refreshed.
        /// </summary>
        public override event NotifyCollectionChangedEventHandler CollectionChanged;

        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            if (this.CollectionChanged != null)
            {
                using (IDisposable disposable = this.BlockReentrancy())
                {
                    foreach (Delegate del in this.CollectionChanged.GetInvocationList())
                    {
                        NotifyCollectionChangedEventHandler handler = (NotifyCollectionChangedEventHandler)del;
                        DispatcherObject dispatcherInvoker = del.Target as DispatcherObject;
                        ISynchronizeInvoke syncInvoker = del.Target as ISynchronizeInvoke;
                        if (dispatcherInvoker != null)
                        {
                            // We are running inside DispatcherSynchronizationContext,
                            // so we should invoke the event handler in the correct dispatcher.
                            dispatcherInvoker.Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadStart(delegate
                            {
                                handler(this, e);
                            }));
                        }
                        else if (syncInvoker != null)
                        {
                            // We are running inside WindowsFormsSynchronizationContext,
                            // so we should invoke the event handler in the correct context.
                            syncInvoker.Invoke(del, new Object[] { this, e });
                        }
                        else
                        {
                            // We are running in free threaded context, so just directly invoke the event handler.
                            handler(this, e);
                        }
                    }
                }
            }
        }
    }
}

I've update the test sample to include this new version of BindableCollection, you can download it from here.

attachment:MultiThreadingInWPF.zip


 

DataBinding & Multi-Threading In WPF [Part One]

| 6 comments

One of the scenario I think the current version of WPF hasn't done enough is the multi-threaded data binding. Imagine your UI is data bound to a data source collection whose items are modified(added, removed, cleared etc) from another thread rather than the UI thread. you will get the following exception:

This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.

Actually, generic data source collections are actually free-threaded(I mean free-threaded not thread safe), but WPF's implementations of CollectionView (such as BindingListCollectionView for ADO.NET data source and  ListCollectionView for general purpose data collections) are actually single-threaded, If you look at the base class of CollectionView, you can see that It directly derives from DispatcherObject, this tells us that CollectionViews are actually Disptacher affinitized, because Dispatchers are tied to the thread which creates them, so CollectionViews end up tying to the their spawning threads. This type of restriction makes multi-threaded data binding a bit more cumbersome and complicated. Beatriz Costa who's working on WPF's data binding feature wrote up a great article ages ago talking about how to do multi-threaded data binding using current version of WPF. She sincerely admit the drawbacks and limitations of current WPF data binding mechanism when it comes to multi-threading. And she came up with an approach to make this type of binding work properly. But one of the things I don't like about her solution is to marshal all the collection modification operations back to the UI thread. I don't think this type of technique can fully leverage the capabilities of worker threads, and much of the benefits inherent with multi-threading are depreciated. How about if the operation you post to UI thread takes a long time to complete, the UI thread will get busy processing this operation, and your UI gets frozen for quite a bit. In this post, I will show another way of doing multi-threaded data binding, and my solution will make as best use of worker threads as possible, enter BindingCollection.

The following code illustrates the implementation of BindingCollection:

using System;
using System.Threading;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Collections.ObjectModel;
using System.Runtime.CompilerServices;

namespace Sheva.Windows.Data
{
    /// <summary>
    /// An ObservableCollection&lt;T&gt; enhanced with cross threads marshaling.
    /// </summary>
    [Serializable]
    public class BindingCollection<T> : ObservableCollection<T>
    {
        private SynchronizationContext creationSyncContext;
        private Thread creationThread;

        /// <summary>
        /// Initializes a new instance of the<see cref="BindingCollection&lt;T&gt;">BindingCollection</see>.
        /// </summary>
        public BindingCollection() : base()
        {
            InstallSynchronizationContext();
        }

        /// <summary>
        /// Initializes a new instance of the<see cref="BindingCollection&lt;T&gt;">BindingCollection</see>
        /// class that contains elements copied from the specified list.
        /// </summary>
        /// <param name="list">The list from which the elements are copied.</param>
        /// <exception cref="System.ArgumentNullException">The list parameter cannot be null.</exception>
        public BindingCollection(List<T> list) : base(list)
        {
            InstallSynchronizationContext();
        }

        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            if (this.creationThread == Thread.CurrentThread)
            {
                // We are running in the same thread, so just directly fire the collection changed notification.
                this.OnCollectionChangedInternal(e);
            }
            else if (this.creationSyncContext.GetType() == typeof(SynchronizationContext))
            {
                // We are running in free threaded context, also directly fire the collection changed notification.
                this.OnCollectionChangedInternal(e);
            }
            else
            {
                // We are running in WindowsFormsSynchronizationContext or DispatcherSynchronizationContext,
                // so we should marshal the collection changed notification back to the creation thread.
                // Note that we should use the blocking SynchronizationContext.Send operation to marshal the call,
                // because SynchronizationContext.Post will fail under heavy usage scenario.
                this.creationSyncContext.Send(new SendOrPostCallback(delegate
                {
                    this.OnCollectionChangedInternal(e);
                }), null);
            }
        }

        private void InstallSynchronizationContext()
        {
            this.creationSyncContext = SynchronizationContext.Current;
            this.creationThread = Thread.CurrentThread;
        }

        internal void OnCollectionChangedInternal(NotifyCollectionChangedEventArgs e)
        {
            base.OnCollectionChanged(e);
        }
    }
}

Note that the reason I use SynchronizationContext rather than Dispatcher here is that I want this collection to be usable in different presentation technologies such as Windows Forms, WPF, and ASP.NET(You might think I am too of frameworkitis, actually I am). I really hope that the logic I create in the OnCollectionChanged method can make into the future implementation of CollectionViews, so that we don't need to worry about the threading problem illustrated above. I've created a simple WPF application to demonstrate how to use this collection, here is the vs project for this little app.

You can make this test more demanding by pressing the "Add Items" and "Remove Items" buttons multiple times to create more threads for testing.

Window Message Based Communication Between Different Applications

There are a number of ways to make inter-process communications on Windows platform, some of which are memory mapped files, mailslots, named pipes etc. But there is another way of doing inter-process communications. the WM_COPYDATA window message enables you to send data to other applications, the only requirement on the receiving applications is that the receiving application should have a thread running message pump, running message pump is the only way to process window messages. I've created a sample application on how to use this technique to send string messages between two Windows Forms applications.

I have two Windows Forms applications, one is called SenderApp which is responsible to send messages, it typically uses the Win32 SendMessage to send WM_COPYDATA messages to the receiving application called ReceiverApp, in the SendMessage method, you should specify lParam arugment a pointer to a COPYDATASTRUCT structure, COPYDATASTRUCT carries on the data you want to send to other applications.

[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT{
    /// <summary>
    /// Specifies data to be passed to the receiving application.
    /// </summary>
    public Int32 dwData;
    /// <summary>
    /// Specifies the size, in bytes, of the data pointed to by the lpData member.
    /// </summary>
    public Int32 cbData;
    /// <summary>
    /// Pointer to data to be passed to the receiving application. This member can be IntPtr.Zero.
    /// </summary>
    public IntPtr lpData;
}

[DllImport("user32.dll")]
public static extern Boolean SendMessage(IntPtr hwnd, UInt32 Msg, IntPtr wParam, ref COPYDATASTRUCT lParam);

Here is the code for the SenderApp:

public partial class SenderForm : Form
{
    public const UInt32 WM_COPYDATA = 0x004a;

    public SenderForm()
    {
        InitializeComponent();
    }

    private void sendButton_Click(Object sender, EventArgs e)
    {
        IntPtr receiverHwnd = FindWindow(null, "Receiver Form");
        if (receiverHwnd == IntPtr.Zero)
        {
            MessageBox.Show("Receiver App hasn't start yet!");
            return;
        }

        String msg = msgTextBox.Text;

        // Allocate unmanaged memory.
        IntPtr msgPtr = Marshal.AllocHGlobal(msg.Length);

        // Convert string to a sequence of bytes.
        Byte[] msgData = Encoding.Default.GetBytes(msg);
       
        // Copy the bytes from managed heap to unmanaged memory.
        Marshal.Copy(msgData, 0, msgPtr, msgData.Length);

        COPYDATASTRUCT copyData = new COPYDATASTRUCT();
        copyData.dwData = Encoding.Default.CodePage;
        copyData.cbData = msgData.Length + 1; //We should take the null-terminator into consideration.
        copyData.lpData = msgPtr;
        SendMessage(receiverHwnd, WM_COPYDATA, this.Handle, ref copyData);
        Marshal.FreeHGlobal(msgPtr);
    }

    /// <summary>
    /// The main entry point for the SenderApp.
    /// </summary>
    [STAThread]
    public static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new SenderForm());
    }
}

And this is code for ReceiverApp:

public partial class ReceiverForm : Form
{
    public const UInt32 WM_COPYDATA = 0x004a;

    public ReceiverForm()
    {
        InitializeComponent();
    }

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_COPYDATA)
        {
            COPYDATASTRUCT copyData = (COPYDATASTRUCT)m.GetLParam(typeof(COPYDATASTRUCT));
            Byte[] data = new Byte[copyData.cbData - 1]; //CLR doesn't recognize the null-terminator, so we should strip it.
            Marshal.Copy(copyData.lpData, data, 0, data.Length);
            String msg = Encoding.GetEncoding(copyData.dwData).GetString(data, 0, data.Length);
            msgTextBox.Text = msg;
        }
        base.WndProc(ref m);
    }

    /// <summary>
    /// The main entry point for the ReceiverApp.
    /// </summary>
    [STAThread]
    public static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new ReceiverForm());
    }
}

Put the above code up and running, you get the following screenshot:

 

You can download the full vs project from here.

AOP Style Thread Synchronization

| 0 comments

Whenever I encounter with the problem of threading, I always think that this aspect of programming problem should be directly built into the framework, so we shouldn't need to understand things like STA, MTA, cross threading marshaling etc. the System.Threading.SynchronizationContext and System.ComponentModel.AsyncOperationManager are designed just for this very purpose. But this solution is far from optimal, so I dig into it, and with the help of Don Box's Book Essential .NET-Common Language Runtime, I come up with an approach using CLR's method interception mechanism.

using System;
using System.Threading;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Proxies;
using System.Runtime.Remoting.Services;
using System.Runtime.Remoting.Contexts;
using System.Runtime.Remoting.Activation;
using System.Runtime.Remoting.Messaging;

namespace ThreadBoundObjectDemo
{
    internal class SynchronizationProxy : RealProxy
    {
        private SynchronizationContext constructionContext;
        private readonly MarshalByRefObject target;
        private readonly Object syncRoot;
        public SynchronizationProxy(MarshalByRefObject target) : base(typeof(ThreadBoundObject))
        {
            this.target = target;
            this.syncRoot = new Object();
        }

        public override IMessage Invoke(IMessage msg)
        {
            IMethodCallMessage call = (IMethodCallMessage)msg;
            IMessage response = null;
            if (call is IConstructionCallMessage)
            {
                IConstructionCallMessage ctor = (IConstructionCallMessage)msg;
                constructionContext = SynchronizationContext.Current;
                RealProxy defaultProxy = RemotingServices.GetRealProxy(target);
                defaultProxy.InitializeServerObject(ctor);
                response = EnterpriseServicesHelper.CreateConstructionReturnMessage(ctor, (MarshalByRefObject)this.GetTransparentProxy());
            }
            else
            {
                if (constructionContext == null || constructionContext.GetType() == typeof(SynchronizationContext))
                {
                    lock (this.syncRoot)
                    {
                        response = RemotingServices.ExecuteMessage(target, call);
                    }
                }
                else if (constructionContext != SynchronizationContext.Current)
                {
                    constructionContext.Send(new SendOrPostCallback(delegate
                    {
                        response = RemotingServices.ExecuteMessage(target, call);
                    }), null);
                }
                else
                {
                    response = RemotingServices.ExecuteMessage(target, call);
                }
            }

            return response;
        }
    }

    /// <summary>
    /// Indicates that all access on an object members(including fields, methods, properties, indexers etc) is thread safe.
    /// </summary>
    [AttributeUsage(AttributeTargets.Class)]
    public class SynchronizedAttribute : ProxyAttribute
    {
        public override MarshalByRefObject CreateInstance(Type serverType)
        {
            MarshalByRefObject target = base.CreateInstance(serverType);
            SynchronizationProxy rp = new SynchronizationProxy(target);
            return (MarshalByRefObject) rp.GetTransparentProxy();
        }
    }
}

So if you want an object to be thread safe, all you need to do is to have a class subclass ContextBoundObject and apply the SynchronizedAttribute to it:

using System;
using System.Threading;
using System.Windows.Forms;

namespace ThreadBoundObjectDemo
{
    public class Program
    {
        [STAThread]
        public static void Main()
        {
            Button btn = new Button();
            btn.Text = "Text set on creation thread";
            Form frm = new Form();
            frm.Controls.Add(btn);
            btn.Dock = DockStyle.Top;
            ThreadBoundObject obj = new ThreadBoundObject(btn);
            btn.Click += delegate
            {
                Thread t = new Thread(new ThreadStart(delegate
                {
                    obj.ButtonText = "Text set from another thread";
                }));
                t.Start();
            };
            Application.Run(frm);
        }
    }

    [SynchronizedAttribute]
    public class ThreadBoundObject : ContextBoundObject
    {
        Button btn;
        public ThreadBoundObject(Button btn)
        {
            this.btn = btn;
        }

        public String ButtonText
        {
            get { return this.btn.Text; }
            set { this.btn.Text = value; }
        }
    }
}

 

To be honest, I don't like the design of ContextBoundObject, but suffice it to say, with the power of CLR's method interception mechanism, you can write AOP styled programme much easier, It looks like that you need to write more plumbing code to implement the AOP components, but you would wind up getting the benefit of easy usage pattern.

AOP Style Thread Synchronization

| 0 comments

Whenever I encounter with the problem of threading, I always think that this aspect of programming problem should be directly built into the framework, so we shouldn't need to understand things like STA, MTA, cross threading marshaling etc. the System.Threading.SynchronizationContext and System.ComponentModel.AsyncOperationManager are designed just for this very purpose. But this solution is far from optimal, so I dig into it, and with the help of Don Box's Book Essential .NET-Common Language Runtime, I come up with an approach using CLR's method interception mechanism.

using System;
using System.Threading;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Proxies;
using System.Runtime.Remoting.Services;
using System.Runtime.Remoting.Contexts;
using System.Runtime.Remoting.Activation;
using System.Runtime.Remoting.Messaging;

namespace ThreadBoundObjectDemo
{
    internal class SynchronizationProxy : RealProxy
    {
        private SynchronizationContext constructionContext;
        private readonly MarshalByRefObject target;
        private readonly Object syncRoot;
        public SynchronizationProxy(MarshalByRefObject target) : base(typeof(ThreadBoundObject))
        {
            this.target = target;
            this.syncRoot = new Object();
        }

        public override IMessage Invoke(IMessage msg)
        {
            IMethodCallMessage call = (IMethodCallMessage)msg;
            IMessage response = null;
            if (call is IConstructionCallMessage)
            {
                IConstructionCallMessage ctor = (IConstructionCallMessage)msg;
                constructionContext = SynchronizationContext.Current;
                RealProxy defaultProxy = RemotingServices.GetRealProxy(target);
                defaultProxy.InitializeServerObject(ctor);
                response = EnterpriseServicesHelper.CreateConstructionReturnMessage(ctor, (MarshalByRefObject)this.GetTransparentProxy());
            }
            else
            {
                if (constructionContext == null || constructionContext.GetType() == typeof(SynchronizationContext))
                {
                    lock (this.syncRoot)
                    {
                        response = RemotingServices.ExecuteMessage(target, call);
                    }
                }
                else if (constructionContext != SynchronizationContext.Current)
                {
                    constructionContext.Send(new SendOrPostCallback(delegate
                    {
                        response = RemotingServices.ExecuteMessage(target, call);
                    }), null);
                }
                else
                {
                    response = RemotingServices.ExecuteMessage(target, call);
                }
            }

            return response;
        }
    }

    /// <summary>
    /// Indicates that all access on an object members(including fields, methods, properties, indexers etc) is thread safe.
    /// </summary>
    [AttributeUsage(AttributeTargets.Class)]
    public class SynchronizedAttribute : ProxyAttribute
    {
        public override MarshalByRefObject CreateInstance(Type serverType)
        {
            MarshalByRefObject target = base.CreateInstance(serverType);
            SynchronizationProxy rp = new SynchronizationProxy(target);
            return (MarshalByRefObject) rp.GetTransparentProxy();
        }
    }
}

So if you want an object to be thread safe, all you need to do is to have a class subclass ContextBoundObject and apply the SynchronizedAttribute to it:

using System;
using System.Threading;
using System.Windows.Forms;

namespace ThreadBoundObjectDemo
{
    public class Program
    {
        [STAThread]
        public static void Main()
        {
            Button btn = new Button();
            btn.Text = "Text set on creation thread";
            Form frm = new Form();
            frm.Controls.Add(btn);
            btn.Dock = DockStyle.Top;
            ThreadBoundObject obj = new ThreadBoundObject(btn);
            btn.Click += delegate
            {
                Thread t = new Thread(new ThreadStart(delegate
                {
                    obj.ButtonText = "Text set from another thread";
                }));
                t.Start();
            };
            Application.Run(frm);
        }
    }

    [SynchronizedAttribute]
    public class ThreadBoundObject : ContextBoundObject
    {
        Button btn;
        public ThreadBoundObject(Button btn)
        {
            this.btn = btn;
        }

        public String ButtonText
        {
            get { return this.btn.Text; }
            set { this.btn.Text = value; }
        }
    }
}

 

To be honest, I don't like the design of ContextBoundObject, but suffice it to say, with the power of CLR's method interception mechanism, you can write AOP styled programme much easier, It looks like that you need to write more plumbing code to implement the AOP components, but you would wind up getting the benefit of easy usage pattern.