かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

.NET Framework 4.5でのWPFの強化点!非同期処理との親和性UP

Visual Studio 11を落として少し遊んでみました。前々から新機能としてUIスレッド以外からItemsControlにバインドされたObservableCollectionを操作すると例外が出るのをなんとかするというのが言われていましたので試してみました。

まずは、既存のVisual Stduio 2010で.NET Framework 4でお試しです。MVVM Light Toolkitを使ってさくっと以下のようなViewModelを作ります。コマンドが実行されるとUIスレッド以外からコレクションに要素が追加されるという単純なものです。

namespace CollectonChangedSample.ViewModel
{
    using System.Collections.ObjectModel;
    using System.Threading.Tasks;
    using GalaSoft.MvvmLight;
    using GalaSoft.MvvmLight.Command;

    public class MainViewModel : ViewModelBase
    {
        public MainViewModel()
        {
        }

        #region Peopleプロパティ
        private ObservableCollection<Person> _people = new ObservableCollection<Person>();
        public ObservableCollection<Person> People
        {
            get { return _people; }
        }
        #endregion

        private RelayCommand _addPersonAsyncCommand;

        /// <summary>
        /// Gets the AddPersonAsyncCommand.
        /// </summary>
        public RelayCommand AddPersonAsyncCommand
        {
            get
            {
                return _addPersonAsyncCommand ?? (_addPersonAsyncCommand = new RelayCommand(
                    () =>
                    {
                        // UIスレッド以外からコレクションにアイテムを追加
                        Task.Factory.StartNew(() =>
                        {
                            this.People.Add(new Person { Name = "sample" });
                        });
                    }));
            }
        }
    }

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

XAMLでは単純にCommandとコレクションをバインドするためのコントロールを置いてます。

<Window x:Class="CollectonChangedSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" 
        DataContext="{Binding Source={StaticResource Locator}, Path=Main}">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <StackPanel Name="stackPanel1">
            <Button Content="Async" Name="button1" Command="{Binding Path=AddPersonAsyncCommand}" />
        </StackPanel>
        <ListBox Grid.Row="1" Name="listBox1" ItemsSource="{Binding Path=People}" DisplayMemberPath="Name" />
    </Grid>
</Window>

これを実行してボタンを押すと下図の通り例外が出てしまいます。これが今までの動作です。

では、新機能とやらを試してみましょう。Visual Studio 2010で作成したプロジェクトをVisual Studio 11で開きます。そしてプロジェクトのプロパティで.NET Framework 4から4.5に変更します。

そして、リビルドして実行して同じ操作をするとうまくいくはず・・・・!!と思ってたら以下のように同じエラーがorz

むぅ。MSDNのこの英文をTOEIC400点に届かない英語力で見る限り大丈夫そうなのに・・・。

Accessing collections on non-UI Threads

WPF enables you to access and modify data collections on threads other than the one that created the collection. This enables you to use a background thread to receive data from an external source, such as a database, and display the data on the UI thread. By using another thread to modify the collection, your user interface remains responsive to user interaction.

読み間違えてるのかな?それとも、まだ実装されてない?