LINQの資料
最近LINQを学び始めてなんとなーく使い始めてるけど使い方がわからない瞬間が多々。
WPFやC#の先駆者の方々のブログみてたら、参考になりそうな資料紹介の記事が
LINQのメソッド構文、クエリ式の構文 - かずきのBlog@hatena
SQLは少しかじってて、クエリ構文?ていうクエリみたいな形になってるやつはちょいちょい使えるんだけど、
メソッド構文?っていうメソッドとラムダ式の方はラムダ式に慣れてないせいか?使い方がいまいちよくわかってないことが多い
下のメソッド構文のよさそうな資料も紹介されてたし
Query Expression Syntax for Standard Query Operators
VisualStudioからサンプルソースみたいなの落とせるの知ってそこのLINQのやつも勉強になるし
ちょっとはかどりそう。
あんまりわかんなかったから、本も買おうかなーと思ったけどもうちょいこれで勉強してみるぞ
三脚新調
持ってる三脚が持ってるリュックに入らない大きさで、電車移動とかで大変だから買ってみたのだ
リソースディクショナリの分け方
最近リソースディクショナリが肥大化してきて、んーどう整理したものかと思ってたんだけど結局よくわからない
ので
以下のサイトの図を参考に、FrameworkElementの階層ごとに分けてみることにした。
連載 WPF/Silverlight UIフレームワーク入門:第4回 “見た目”を決めるコントロール・テンプレート (3/3) - @IT
連載:WPF入門:第8回 WPFの「コントロール」を学ぼう (1/2) - @IT
とりあえずこれて作ってってあとで困ったらそのときなんとかしよう。。。笑
子画面を閉じたとき、親画面で決まった処理を行いたい
子画面のClosedイベントにやりたい処理を登録したデリゲートを登録
でもよいんだけど、これだと毎回子画面追加(インスタンス)時にこのデリゲートも一緒に登録してあげないといけない。毎回。
なんかだるいなーおんなじ処理なのになーって思って
OwnedWindowsのChangedEventとかないのかなーとか調べたけど なさそう。。。
だから、自作イベントループ作ってOwnedWindowsの値監視して減少したら発火~なイベント用意。
private void CloseSubWindowEventloop() { int owNum = 1; while (true) { this.Dispatcher.Invoke(new Action(() => { if (owNum > this.OwnedWindows.Count) { this.RefreshListViewEventHandler(); } owNum = this.OwnedWindows.Count; })); // SleepはDispatcher外に置くのがポイント。Dispatcher.Invokeが開きっぱなしだと、メインスレッドに戻れる隙ができず、GUIが固まる。 System.Threading.Thread.Sleep(300); } }
かなり簡潔にかけて気持ちいい!
。。。けど、こんな気軽にワーカースレッド使ってよいものなのだろうか。。。
Prism.CommandsのDelegateCommand<T>で、VisualStudioのデザイナーが例外を吐く
また躓いたからメモ
現象
Windowクラス(を継承した独自のMyWindowクラス)内のButtonコントロールのCommandに、ViewModel側で用意したDelegateCommand
CommandParameterのバインドで、ElementName引数に渡したいWindowのElementNameを指定してバインドを試みるもVisualStudioのデザイナーがインスタンスを生成するときに例外を吐き、デザイナーが表示されない問題に遭遇した。
やりたかったこと
コマンドを実施するボタンが所属するウィンドウオブジェクト自体をCommandParameterとして渡したい。
背景
ボタンを処理内で、ボタンが所属するWindowクラス(を継承した独自のMyWindowクラス)をクローズしたかった。
例外がスローされました。
InvalidCastException: 型 'Microsoft.VisualStudio.DesignTools.WpfDesigner.InstanceBuilders.WindowInstance' のオブジェクトを型 'testNamespace.MyWindow にキャストできません。
StackTrace
場所 Prism.Commands.DelegateCommand`1.<>c__DisplayClass1_0.<.ctor>b__1(Object o)
場所 Prism.Commands.DelegateCommandBase.CanExecute(Object parameter)
場所 Prism.Commands.DelegateCommandBase.System.Windows.Input.ICommand.CanExecute(Object parameter)
場所 MS.Internal.Commands.CommandHelpers.CanExecuteCommandSource(ICommandSource commandSource)
場所 System.Windows.Controls.Primitives.ButtonBase.UpdateCanExecute()
場所 System.Windows.Controls.Primitives.ButtonBase.HookCommand(ICommand command)
場所 System.Windows.Controls.Primitives.ButtonBase.OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
場所 System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
場所 System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
場所 System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
場所 System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
場所 System.Windows.DependencyObject.InvalidateProperty(DependencyProperty dp, Boolean preserveCurrentValue)
場所 System.Windows.Data.BindingExpressionBase.Invalidate(Boolean isASubPropertyChange)
場所 System.Windows.Data.BindingExpression.TransferValue(Object newValue, Boolean isASubPropertyChange)
場所 System.Windows.Data.BindingExpression.Activate(Object item)
場所 System.Windows.Data.BindingExpression.AttachToContext(AttachAttempt attempt)
場所 System.Windows.Data.BindingExpression.MS.Internal.Data.IDataBindEngineClient.AttachToContext(Boolean lastChance)
場所 MS.Internal.Data.DataBindEngine.Task.Run(Boolean lastChance)
場所 MS.Internal.Data.DataBindEngine.Run(Object arg)
場所 MS.Internal.Data.DataBindEngine.OnLayoutUpdated(Object sender, EventArgs e)
場所 System.Windows.ContextLayoutManager.fireLayoutUpdateEvent()
場所 System.Windows.ContextLayoutManager.UpdateLayout()
場所 System.Windows.UIElement.UpdateLayout()
<Window x:Class="testNamespace.MyWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:testNamespace" x:Name="myWindow" Height="350" Width="500"> <Grid> <Button x:Name="button_Output" Content="出力" Style="{StaticResource commandButtonStyle}" IsEnabled="{Binding CommandButtonEnabled}" Command="{Binding CommandButtonClickCommand}" CommandParameter="{Binding ElementName=myWindow}" Margin="0,0,4,4" HorizontalAlignment="Right" VerticalAlignment="Bottom" /> </Grid>
発見した解決方法
- キャストさせないように、object型で受け取るようにした
なんだかよくわからないけどキャストに失敗してるらしいから、
キャストさせないように、XAML側のCommandParameterのobject型に合わせ、
パラメータ受け取り側のパラメータの型をobject型にしてみたらうまくいった
DelegateCommand<MyWindow> → DelegateCommand<object>
- XAML側のバインド指定方法で、RelativeSourceを使った
RelativeSourceで同じオブジェクト内の別プロパティ(ここではWindow)を見つけさせたらよくわからないけどうまくいった
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
キャストに失敗 という判定が下る理由がまだよくわかってない。
その他試したけど無駄だったこと
- CanExecuteを用意していなかったため用意して、問答無用でtrueを返すようにした
https://msdn.microsoft.com/en-us/library/gg431410%28v=pandp.50%29.aspx
The constructor deliberately prevents the use of value types. Because ICommand takes an object, having a value type for T would cause unexpected behavior when CanExecute(null) is called during XAML initialization for command bindings.
Examples
public MyClass()
{
this.submitCommand = new DelegateCommand(this.Submit, this.CanSubmit);
}private bool CanSubmit(int? customerId)
{
return (customerId.HasValue && customers.Contains(customerId.Value));
}
英語力ないので誤訳してそうでアレですが、
値型は使うな。null許容型をparameterで渡すとよからぬことが起こりかねないから。
もし使いたいなら、CanExecute的なとこでチェックしろ と言っているのだと理解した。(すごく自信がない)
そして、例を見た感じ、CanSubmit(CanExecuteにあたるもの)でnullじゃなかったらtrueみたいに渡してるので
true判定を出せばとりあえずインスタンス化はできるのではないかと考えて実施
思うところ
- Object型がキャストできない型ってあるの?
- XAML側で型を指定することってできるの?
(調べた感じマークアップを使うのかな?でもDelegateCommandにはマークアップ拡張が用意されてなさそうな感じする)
- RelativeSource指定でうまくいくのは、AncestorType={x:Type Window}と指定していて、CommandParameterに入れた時点でWindow型になっている(???)からか
- 「InvalidCastException: 型 'Microsoft.VisualStudio.DesignTools.WpfDesigner.InstanceBuilders.WindowInstance' のオブジェクトを型 'testNamespace.MyWindow にキャストできません。」の型 'Microsoft.VisualStudio.DesignTools.WpfDesigner.InstanceBuilders.WindowInstance'ってデザイナー用の型っぽい。デザイナーはなんか違うオブジェクトを渡そうとしてる???
- 自分はやはりサーチ力英語力が低い。なかなかこれだという情報にたどり着けない。
ローカルリソースを参照できない
定義したローカルリソースを参照できない場面があったのでメモ
家にソース内からうろおぼえでかく
EasingColorKeyFrame.Value
<!-- ローカルリソースの定義(WindowResources内) --> <SolidBorderBrush x:Name="hogeColor" Value="#FF000000" /> <SolidBorderBrush x:Name="hogeColor2" Value="#AA000000" /> <!-- ローカルリソースを使おうとした場所(Style内) --> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x: Type Button}"> <Border x Name="buttonborder" Background="Transparent" BorderBrush="Transparent" BorderThickness="1" SnapsToDevicePixels="True"> <Border.Effect> <DropShadowEffect Opacity="0" /> </Border.Effect> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x Name="Normal"> <Storyboard> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="buttonborder"> <EasingDoubleKeyFrame KeyTime="0" Value="0.6" /> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="MouseOver"> <Storyboard> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="buttonborder"> <EasingDoubleKeyFrame KeyTime="0" Value="1" /> </DoubleAnimationUsingKeyFrames> <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="buttonborder"> <EasingColorKeyFrame KeyTime="0" Value="{StaticResource hogeColor}" /> </ColorAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x Name="Pressed"> <Storyboard> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="buttonborder"> <EasingDoubleKeyFrame KeyTime="0" Value="1" /> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Effect).(DropShadowEffect.ShadowDepth)" Storyboard.TargetName="buttonborder"> <EasingDoubleKeyFrame KeyTime="0" Value="0" /> </DoubleAnimationUsingKeyFrames> <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Effect).(DropShadowEffect.Color)" Storyboard.TargetName="buttonborder"> <EasingColorKeyFrame KeyTime="0" Value="White" /> </ColorAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Effect).(DropShadowEffect.Opacity)" Storyboard.TargetName="buttonborder"> <EasingDoubleKeyFrame KeyTime="0" Value="0.6" /> </DoubleAnimationUsingKeyFrames> <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="buttonborder"> <EasingColorKeyFrame KeyTime="0" Value="{StaticResource hogeColor2}" /> </ColorAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x Name="Disabled" /> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <ContentPresenter x Name="buttoncontentPresenter" Focusable="False" Margin="{TemplateBinding Padding}" HorizontalAlignment="{ TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{ TemplateBinding VerticalContentAlignment}" /> </Border> </ControlTemplate> </Setter.Value> </Setter>
の
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="buttonborder"> <EasingColorKeyFrame KeyTime="0" Value="{StaticResource hogeColor}" /> </ColorAnimationUsingKeyFrames>
と
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="buttonborder"> <EasingColorKeyFrame KeyTime="0" Value="{StaticResource hogeColor2}" /> </ColorAnimationUsingKeyFrames>
あとこれもだめだったきがする。
DropShadowEffect.Color
<DropShadowEffect Color="{StaticResource hogeColor}" BlurRadius="{Binding ElementName=slider1, Path=Value}" ShadowDepth="{Binding ElementName=slider2, Path=Value}" Direction="{Binding ElementName=slider3, Path=Value}" />
この辺で躓いてるの、なんかXAMLの根幹をいろいろとわかってないのじゃないだろうか。。。
ResourceDictionary内でイベント的な処理を行う方法
- ResourceDictionaryのコードビハインドを用意する
- ResourceDictionaryのViewModelを用意し、Commandを実装する