LINQの資料

最近LINQを学び始めてなんとなーく使い始めてるけど使い方がわからない瞬間が多々。



WPFC#の先駆者の方々のブログみてたら、参考になりそうな資料紹介の記事が

LINQのメソッド構文、クエリ式の構文 - かずきのBlog@hatena


SQLは少しかじってて、クエリ構文?ていうクエリみたいな形になってるやつはちょいちょい使えるんだけど、
メソッド構文?っていうメソッドラムダ式の方はラムダ式に慣れてないせいか?使い方がいまいちよくわかってないことが多い

下のメソッド構文のよさそうな資料も紹介されてたし
Query Expression Syntax for Standard Query Operators

VisualStudioからサンプルソースみたいなの落とせるの知ってそこのLINQのやつも勉強になるし
ちょっとはかどりそう。

あんまりわかんなかったから、本も買おうかなーと思ったけどもうちょいこれで勉強してみるぞ

三脚新調

 
三脚新しく買った!
まだ使ってない。
 

持ってる三脚が持ってるリュックに入らない大きさで、電車移動とかで大変だから買ってみたのだ

 
開封の儀式
 
f:id:nexl:20160508141228j:image
 
f:id:nexl:20160508141322j:image
 
f:id:nexl:20160508141333j:image
 
f:id:nexl:20160508141340j:image
 
f:id:nexl:20160508141346j:image
 
f:id:nexl:20160508141635j:image
 
これだけコンパクトなのに、エレベーターなしの旧三脚の最伸長とほぼ同じなの良い
 
エレベーター使うと10cmくらい差があるかな
 
 
まあまあ調べて比較しまくってこれかなーってなって選んだ
 
逆縮長345mmは ほんと良い。
 
現時点で気に食わない妥協した部分はクイックシューと5段構造なくらい クイックシューは違うやつに変えられるから耐えられなくなったら買うかなたぶん
 
まぁ使ってみてかなー。
 
ちょっと軽くなってるし、車のとき以外は捗りそうだなーとは現時点で思う。

リソースディクショナリの分け方

最近リソースディクショナリが肥大化してきて、んーどう整理したものかと思ってたんだけど結局よくわからない


ので

以下のサイトの図を参考に、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>

発見した解決方法

  1. キャストさせないように、object型で受け取るようにした

なんだかよくわからないけどキャストに失敗してるらしいから、
キャストさせないように、XAML側のCommandParameterのobject型に合わせ、
パラメータ受け取り側のパラメータの型をobject型にしてみたらうまくいった

DelegateCommand<MyWindow> → 
DelegateCommand<object>
  1. XAML側のバインド指定方法で、RelativeSourceを使った

RelativeSourceで同じオブジェクト内の別プロパティ(ここではWindow)を見つけさせたらよくわからないけどうまくいった

CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"


キャストに失敗 という判定が下る理由がまだよくわかってない。

その他試したけど無駄だったこと

  1. 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判定を出せばとりあえずインスタンス化はできるのではないかと考えて実施



思うところ

  1. Object型がキャストできない型ってあるの?
  1. XAML側で型を指定することってできるの?

(調べた感じマークアップを使うのかな?でもDelegateCommandにはマークアップ拡張が用意されてなさそうな感じする)

  1. RelativeSource指定でうまくいくのは、AncestorType={x:Type Window}と指定していて、CommandParameterに入れた時点でWindow型になっている(???)からか
  1. 「InvalidCastException: 型 'Microsoft.VisualStudio.DesignTools.WpfDesigner.InstanceBuilders.WindowInstance' のオブジェクトを型 'testNamespace.MyWindow にキャストできません。」の型 'Microsoft.VisualStudio.DesignTools.WpfDesigner.InstanceBuilders.WindowInstance'ってデザイナー用の型っぽい。デザイナーはなんか違うオブジェクトを渡そうとしてる???
  1. 自分はやはりサーチ力英語力が低い。なかなかこれだという情報にたどり着けない。

ローカルリソースを参照できない

定義したローカルリソースを参照できない場面があったのでメモ

家にソース内からうろおぼえでかく


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内でイベント的な処理を行う方法

直面した問題
     自前で用意したCaptionButtonたちが操作されたときのイベントを拾った後、
     処理がしたいが、CaptiionButtonのスタイルがResourceDictionary内に記述されている。
     イベントを処理するハンドラを定義する場所をどうすればよいのかわからない。
 
背景
     Zuneのような見た目のWPFアプリケーションを作りたかった。
 
方法は
  1. ResourceDictionaryのコードビハインドを用意する
  2. ResourceDictionaryのViewModelを用意し、Commandを実装する
 
とかかなぁ?もし両方できても、理想はMVVM的に2番だなーと思って、実際にできるのか調査。
 
1.は、コードビハインドは実装が可能そうなことがわかった。
実際に、関連付けたコードビハインド(用意したResourceDictionaryクラスのPartialクラス)は用意でき、しっかり動くことを確認できた。
 
 
2.は、いろいろ調べたけどわからなかった。
 
ViewModelを設定することがわからなかった。
 
 
 
そもそも、ResourceDictionaryに書くような部分にイベントとかを仕込む思想が間違っているかもしれない。
(つまりResourceDictionaryというものには、コードビハインドもViewModelもあるべきではないのかもしれない)
 
 
機能自体は実装できたものの不完全燃焼。
 
XAML周り本当に難しい。