WPF ListView DataBinding for IronPython

ぱぇぽぃ2 ? Blog Archive ? Python getter, setter
の続き、上手くいったので覚書に。
ObservableCollection を作って ListView で DataBinding させる。

Silverlight Dynamic Languages SDK – Discussions

ほう、こうすればいいんだ。
コレを IronPython 2.6 から使える property setter でやってみる。

{ironpythondir}/Tutorial/pyevent.py
をソースコードと同じ場所にコピー

追記と小変更

pyevent.py の import はこうすればコピー不要

import sys
tutorial_path = IO.Path.GetDirectoryName(sys.executable) + "\\Tutorial"
sys.path.insert(0, tutorial_path)
from pyevent import *

データトリガーなんかをコードで行う方法が解らないので XAML で。
以下ソースコード

#-*- coding:utf-8 -*-

import clr

clr.AddReferenceByPartialName("PresentationCore")
clr.AddReferenceByPartialName("PresentationFramework")
clr.AddReferenceByPartialName("WindowsBase")

from System import *
from System.Windows import *
from System.Windows.Controls import *
from System.Collections.ObjectModel import *
from System.ComponentModel import *
from System.Windows.Markup import XamlReader

# Search pyevent.py
import sys
tutorial_path = IO.Path.GetDirectoryName(sys.executable) + "\\Tutorial"
sys.path.insert(0, tutorial_path)
from pyevent import *

listview_str = """<ListView
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <ListView.Resources>
        <Style x:Key="sensitive" TargetType="TextBlock">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=show}" Value="False">
                    <Setter Property="Foreground" Value="Silver" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
        <DataTemplate x:Key="tpl_show">
            <CheckBox IsChecked="{Binding Path=show}" />
        </DataTemplate>
        <DataTemplate x:Key="tpl_name">
            <TextBlock Text="{Binding Path=name}" TextTrimming="CharacterEllipsis"
                Style="{StaticResource sensitive}" />
        </DataTemplate>
    </ListView.Resources>
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Show" CellTemplate="{StaticResource tpl_show}" />
            <GridViewColumn Header="Name" CellTemplate="{DynamicResource tpl_name}" />
        </GridView>
    </ListView.View>
</ListView>"""

class NotifyPropertyChangedBase(INotifyPropertyChanged):
    """
        http://sdlsdk.codeplex.com/Thread/View.aspx?ThreadId=30322
    """
    PropertyChanged = None
    def __init__(self):
        (self.PropertyChanged, self._propertyChangedCaller) = make_event()

    def add_PropertyChanged(self, value):
        self.PropertyChanged += value

    def remove_PropertyChanged(self, value):
        self.PropertyChanged -= value
    
    def OnPropertyChanged(self, propertyName):
        self._propertyChangedCaller(self, PropertyChangedEventArgs(propertyName))


class MyData(NotifyPropertyChangedBase):
    """
        use setter getter.
        IronPython 2.6 or later.
    """
    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value
        self.OnPropertyChanged("name")

    @property
    def show(self):
        return self._show

    @show.setter
    def show(self, value):
        self._show = value
        self.OnPropertyChanged("show")

class DataBindingTest(Window):
    """
        ListView DataBinding Test class
    """
    def __init__(self):
        self.data = ObservableCollection[MyData]()
        self.Resources["data"] = self.data
        self.listview = XamlReader.Parse(listview_str)
        self.listview.ItemsSource = self.data
        # Temporary Data
        datadict = {"IronPython": True,
                    "C#": False,
                    "F#": True,
                    "Python": True,
                    "Ruby": False,
                    "Perl": False,
                    "C/C++": True }
        for key in datadict.keys():
            en = MyData()
            en.name = key
            en.show = datadict[key]
            self.data.Add(en)
        self.Content = self.listview
        self.Title = "DataBindingTest"
        self.Width = 300
        self.Height = 300
        # Data Check
        self.Closed += self.on_closed

    def on_closed(self, sender, e):
        """
            Checking self.data
        """
        s = ""
        for data in self.data:
            s += "%s: %s\r\n" % (data.name, data.show)
        MessageBox.Show(s)

if __name__ == "__main__":
    a = Application()
    a.Run(DataBindingTest())

data_binding_test

見事にデータ内容と表示が一致するようになりました。
CheckBox On Off で TextBlock の色が即座に変更されます。
Ruby だけ Off に変更してみた例

ruby_off

終了時に ObservableCollection の内容を確認。

off_result

CheckBox の値がそのままデータとなっていることが解ります。
IronPython でもコレが可能なんですね、2.6 正式版が待ちどおしい。

Enjoy!