2001年06月12日(火) 寒いな
■ telnet mail 25 とやるとSMTPサーバから応答があるけれど、telnet gate4 25 とやったときは何も応答が無いのはなんでなのかわからない。送信元のIPアドレスをみてフィルタリングしているのでしょうか?
招待状を封筒へ入れて宛先シールを貼る作業をみんなでやった。全部で1500通。たまにこういうことをするのは楽しい。
2002年06月12日(水) I始まりの日
■ 会社
- 10:50 今日は朝から高本さんと打合せ。はずれプロジェクトが回ってきてしまった。
- セキュリティホールmemo MLでプライバシーマークの話題。弊社の個人情報保護方針も内容は何も無いですな。
- フラッシュはスプラッシュの一つの実現方式なのでは。
- 17:15 13時から15時まで水野さんの説明を聞く。はずれっぽい。その後は監視項目を考えていた。
- 18:25 眠くてどうしようもないので眠っていた。
- 20:05 RIPとOSPFを勉強した。あまり興味が無い。
- 21:45 監視項目考えるの終わり。後は明日だ。もう遅いなあ。
■ テレビ/本
- WEB+DB Press。石井さんがPostgreSQLのトランザクション処理を説明しているのが分かりやすくてよかった。
2003年06月12日(木)
■ [web] Apacheの日本語ドキュメント
Apacheのサイトは英語だけれど、サイドバーにあるDocumentation内のApache 1.3とApache 2.0は日本語で表示されることを知った。素晴らしい。
■ 生活
- 市民税・県民税の納税通知書が送られてきた。6月8月10月1月の末にそれぞれ6万円払えと。うそー、まじー、高ーい、うえーん。
- ノートPCの左下部が熱くなるので左手首が温まってしまう。長時間温まり続けるので、体に悪いのではないかと心配。心配していたらなんだか左腕が痺れてきたぞ。
- これってPCが壊れているのだろうか。
■ やること
CDROMを返送
2006年06月12日(月)
- ヘッダファイルを3人でレビュー。時間がかかる。
■ [サッカー] ワールドカップ 日本vsオーストラリア
先週末くらいからドイツにてワールドカップが始まっている。日本の初戦は格下のオーストラリア。先発は
柳沢 高原 中村 三都主 福西 中田 駒野 中澤 宮本 坪井 川口
オーストラリアはFWビドゥカの強さが脅威。坪井が局面で勝ててないなあと思っていたら足を痛めて交代してしまった。緊張したのかな。茂庭は強運だ。宮本は良いカバー。川口が良いセーブを連発。中田はあまり目立てず。中村も肝心な所でいいプレーができない。駒野はゴール前であっさりかわされてしまった。三都主は守備のときにもっとボールへ寄せないといけないのではないのか。
■ やること
- テーブル生成
- デザイン変更
- プリンタインク
- 保険
松井- PC接続用ケーブル
- ツールのDVDを観る
2017年06月12日(月)
■ [c#] WPFでICommandとINotifyPropertyChangedを使う
WPFではボタンやメニューをクリックしたときにICommandを実装したクラス(コマンド)のメソッドを呼び出す仕組みがある。また、INotifyPropertyChangedを実装することでViewModelの状態変化をViewへ通知することができる。これらの機能を使った次のプログラムを作ってみた。
- 追加ボタンをクリックするとテキストボックスの内容を画面下部に追加する。このときテキストボックスの内容はクリアする。
- テキストボックスが空だとボタンはクリックできない。
MainWindow.xaml
<Window x:Class="Hello.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:Hello" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525" FocusManager.FocusedElement="{Binding ElementName=textBox}"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition/> </Grid.RowDefinitions> <TextBox x:Name="textBox" Height="18" Margin="5" VerticalAlignment="Top" Text="{Binding InputText, UpdateSourceTrigger=PropertyChanged}"/> <Button x:Name="button" Content="追加" Grid.Row="1" Width="75" Height="20" Margin="5" HorizontalAlignment="Left" VerticalAlignment="Top" Command="{Binding AddCommand}"/> <ScrollViewer Grid.Row="2"> <TextBlock x:Name="textBlock" Text="{Binding Message}"/> </ScrollViewer> </Grid> </Window>
- 前回はDataContextをXAML上で指定したけど、これだとモデルが複雑になったときにうまくいかない気がしてきたので指定しないようにした。DataContextは後述のApp.xaml.cs上で指定する。
- 起動時にTextBoxにフォーカスを当てるよう、Window要素にFocusManager.FocusedElement属性を指定してみた。
- TextBoxのテキストが変化する度にViewModelが更新されるよう、TextBoxのText属性にて UpdateSourceTrigger=PropertyChanged を指定した。
- ButtonのCommand属性にAddCommandを指定した。これによりこのボタンとViewModelのAddCommandプロパティで参照できるコマンドが関連づく。
- TextBlockをScrollViewerで囲い、TextBlockのテキストが長くなった時にスクロールできるようにした。
App.xaml
<Application x:Class="Hello.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Hello" Startup="onStartup"> <Application.Resources> </Application.Resources> </Application>
- VisualStudioの生成するApp.xamlはApplication要素のStartupUri属性でXAMLファイルを指定しているが、それを削除しStartup属性を指定するように変更した。これで起動時にHello.AppクラスのonStartup()が呼ばれるようになる。
App.xaml.cs
using System.Windows; namespace Hello { /// <summary> /// App.xaml の相互作用ロジック /// </summary> public partial class App : Application { private void onStartup(object sender, StartupEventArgs e) { var mh = new MessageHolder("こんにちは"); var w = new MainWindow(); w.DataContext = new MainWindowViewModel(mh); w.Show(); } } }
- AppクラスにonStartup()を追加し、ここでMessageHolder(モデル)、MainWindowViewModel(ビューモデル)、MainWindow(ビュー)を生成して関連づけるようにした。
MessageHolder.cs
namespace Hello { class MessageHolder { public MessageHolder(string initialMessage) { Message = initialMessage; } public string Message { get; set; } } }
- モデルクラスを作った。文字列をプロパティに持つだけ。
MainWindowViewModel.cs
using System.ComponentModel; using System.Runtime.CompilerServices; using System.Windows.Input; namespace Hello { class MainWindowViewModel : INotifyPropertyChanged { private MessageHolder messageHolder; private ICommand addCommand; public MainWindowViewModel(MessageHolder mh) { messageHolder = mh; addCommand = new DelegateCommand( () => { Message = InputText + "\n" + Message; InputText = ""; NotifyPropertyChanged(nameof(InputText)); }, () => { return !string.IsNullOrEmpty(InputText); }); } public string Message { get { return messageHolder.Message; } set { messageHolder.Message = value; NotifyPropertyChanged(); } } public string InputText { get; set; } public ICommand AddCommand => addCommand; public event PropertyChangedEventHandler PropertyChanged = delegate {}; private void NotifyPropertyChanged([CallerMemberName] string name = "") { PropertyChanged(this, new PropertyChangedEventArgs(name)); } } }
- ViewModelはINotifyPropertyChangedを実装するようにした。最後にある NotifyPropertyChanged() を呼ぶと状態の変化がViewへ通知される。
- NotifyPropertyChanged()の引数には[CallerMemberName]属性を指定した。こうすると呼び出し元のメソッドorプロパティ名が設定されるみたい。
- フィールドaddCommandに後述するDelegateCommandのインスタンスを設定している。これをAddCommandプロパティで公開しViewがそれを参照している。
DelegateCommand.cs
using System; using System.Windows.Input; namespace Hello { class DelegateCommand : ICommand { private Func<bool> canExecute; private Action execute; public DelegateCommand(Action execute) : this(execute, () => true) {} public DelegateCommand(Action execute, Func<bool> canExecute) { this.execute = execute; this.canExecute = canExecute; } public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } public bool CanExecute(object parameter) { return canExecute(); } public void Execute(object parameter) { execute(); } } }
- コマンドの処理はViewModelのメソッドで処理するのが手軽なので、それを実現するためのコマンドDelegateCommandを定義する。MVVMフレームワークを使う場合はフレームワークが同様のクラスを用意しているみたい。
- ICommandで受け取れるパラメータはobject型なので使いづらい。なのでパラメータは使わないようにしている。コマンドの状態は処理が移譲されるViewModelの状態から取得する想定。
- ICommandはCanExecute()を持つが、これが有効に機能するにはこのメソッドの返す値が変わったときにコマンドの送信元(今回の場合はButton)に状態変化を通知する必要がある。で、上では状態通知のイベントハンドラをCommandManagerに登録しているが、こうすると自動的に状態変化通知がされるみたい。仕組みがよくわからないけれど適切なタイミングでポーリングしているのだろうか。
TODO CommandManager.RequerySuggestedはハンドラを弱参照で持つからハンドラがGCで回収されてしまわないように気をつけろという注意が書かれていた。修正しないと駄目そう。- 別に問題ない気がしてきた。DelegateCommandへイベントハンドラを登録するのはボタンなどのWPFのコンポーネントで、これらはイベントハンドラへの参照を保持しているはずだと思う。もし問題が発生したら調べる。
● 浅海 [小川さんと同じマシンをこの間初めて長時間使用しました。 やっぱり左側がすごーく熱くなりました。 何なんでしょうねぇ。]
● 小川 [熱くなるのはノートPCの宿命なのでしょうか。それにしても熱い。今までは袖があったが夏は袖がないので余計気になります。..]