ラベル WPF の投稿を表示しています。 すべての投稿を表示
ラベル WPF の投稿を表示しています。 すべての投稿を表示

2015年2月19日木曜日

.NETプログラム実行時にDLLの参照エラーになったときようのツール

実行時にDLLが見つからずにエラーになるのはままあることで原因を特定するのはさほど難しくないけれど面倒な作業である。とくにライブラリで参照しているDLLと呼び出し元のプロジェクトで参照しているDLLに差が出ることは、Nugetでパッケージを取得することが増えた昨今はよく起こる。

ということが今の職場でよく起きてそれを調べる用のツールを作ったので公開する。

Missing Reference Finder
https://github.com/yooontheearth/missing-reference-finder



使い方はいたって簡単。Pathにbin等のフォルダを指定してcheckボタンを押下するだけ。指定のフォルダかGACにないものはMISSING扱いになる。

2011年1月6日木曜日

Canvasにリストの中身をBindingする方法

ObservableCollectionでもなんでもいいのだが、リストで保持している内容をCanvasに表示したいことはままある。しかしCanvas.Childrenに対して単純にBindingしても予期した動作にはならない。そんな場合はItemsControlのItemsPanelTemplateを使用する。

こんな風にデータをCanvas上に表示したい。

public ObservableCollection<TextData> TextList { get; set; }
TextList = new ObservableCollection<TextData>(
                new List<TextData>
                {
                    new TextData{ Left=110, Top=110, Text="嘘だと" },
                    new TextData{ Left=210, Top=210, Text="言ってよ" },
                    new TextData{ Left=310, Top=310, Text="バーニィ" },
                });
各TextDataクラスはLeft, TopでCanvas上の位置を保持している。

では、上記のリストをCanvas上に配置するためのXamlを見てみよう。
<ItemsControl ItemsSource="{Binding Path=TextList}"
                      VerticalAlignment="Stretch"
                      HorizontalAlignment="Stretch">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Text}" />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemContainerStyle>
                <Style>
                    <Setter Property="Canvas.Top" Value="{Binding Path=Top}" />
                    <Setter Property="Canvas.Left" Value="{Binding Path=Left}" />
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ItemsControl>
ポイントはItemsControl.ItemsPanelのItemsPanelTemplateを使用している箇所。そこでアイテム配置用のパネルにCanvasを指定しているので、下のほうのItemsControl.ItemContainerStyleで各アイテムのLeft, Topを指定して任意の位置にデータを表示させることが可能となる。

サンプルコードはこちら。
(Chromeだと下記iframeが表示されない模様。FireFoxでは表示確認済み)

2011年1月5日水曜日

RelayCommandの実装いろいろ MVVM Light Toolkit

MVVM Light ToolkitのRelayCommandの実装方法を色々と紹介する。今回はWPFで実装している。


1、コマンドパラメータなし
何も特別なことはなし
Xaml
<Button Command="{Binding ClickCommand}" 
        Content="Click Me" />

ViewModel
public RelayCommand ClickCommand { get; private set; }
ClickCommand = new RelayCommand(() =>
{
  // Do something...
});


2、コマンドパラメータあり
ItemsControlのDataTemplate内要素のイベントをフックしている。ここでのDataTemplateのDataContextはDataItemになるので、単純にClickCommandをBindingしても期待した動作にはならない。そのためRelativeSrouceで一番上の親要素(Window)までたどってViewModelを取得している。CommandParameterにDataTemplateのDataContextをBindingしている。

Xaml
<ItemsControl ItemsSource="{Binding DataList}">
 <ItemsControol.ItemTemplate>
  <DataTemplate>
   <Button Command="{Binding DataContext.ClickCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
       CommandParameter="{Binding}"
           Content="Click Me" />
   </DataTemplate> 
 </ItemsControol.ItemTemplate>
</ItemsControl>

ViewModel
public ObservableCollection<DataItem> DataList { get; set; }
public RelayCommand<DataItem> ClickCommand { get; private set; }
ClickCommand = new RelayCommand(x =>
{
  // Do something...
});


3、ICommand以外をBinding
WindowのLoadedイベントなどをBindingしたい場合はEventToCommandを使用する。
・まずXAMLのネームスペースに下記を追加する:
 xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
 xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4"
・後はフックしたいイベントを下記の要領で記述する

Xaml
<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4"
        DataContext="{Binding ViewModel, Source={StaticResource Locator}}">
  <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <cmd:EventToCommand Command="{Binding WindowLoadedCommand}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>

ViewModel
public RelayCommand WindowLoadedCommand{ get; private set; }
WindowLoadedCommand= new RelayCommand(() =>
{
  // Do something...
});


4、ICommand以外をBinding パラメータあり
WindowのClosingイベントなどでパラメータを渡したい場合はPassEventArgsToCommandをTrueに設定する。

Xaml
<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4"
        DataContext="{Binding ViewModel, Source={StaticResource Locator}}">
  <i:Interaction.Triggers>
        <i:EventTrigger EventName="Closing">
            <cmd:EventToCommand Command="{Binding WindowClosingCommand}" PassEventArgsToCommand="True" />
        </i:EventTrigger>
    </i:Interaction.Triggers>

ViewModel
public RelayCommand WindowClosingCommand<System.ComponentModel.CancelEventArgs>{ get; private set; }
WindowClosingCommand= new RelayCommand(x =>
{
  // Do something...
});

今回解説したようにEventToCommandを使用するとICommandに対応していないイベントもBinding可能となる。Window要素のイベントに限らずどのような要素にも適用できるのでEventToCommandを使用すればコードビハインドを経由してイベントをリレーする必要がなくなる。

2010年5月24日月曜日

WPFを難読化しよう ~ Obfuscator

.NETのアセンブリはIL化されているので、普通にコンパイルしただけではアセンブリから逆アセンブルしてコードの中身をそっくりそのまま見れるようになっている。それはもうReflectorなどのツールを使ってしまえばコードの隅々まで手に取るように分かってしまう。

これではコードの知的財産権を意図せずに放棄しているようなもので、重要なビジネスロジックを実装している場合や、ビジネス的に価値のある高度なプログラミングをしている場合などは危なっかしくてアセンブリを頒布することができない。

そのため.NETでは商用のアセンブリなどをリリースする際には、アセンブリを逆アセンブルされても良いように、ILのコードを意味不明な変数名などに置き換えるコード難読化ツール(英語では大体「ほにゃらら obfuscator」という名称が用いられている。obfuscate=分かりにくくする)が利用されてきた。

今回はWPFアプリ用のObfuscatorで良いなと思ったツールを紹介したい。

商用のObfuscatorは大体$100~$400ぐらいと、個人で開発している開発者にとっては結構な値をはるものが多いが機能は異常に豊富だ。フリーのツールでも充分用を足せるものがあるので、ガチガチにかためる必要がない場合は「.net obfuscate free」などでGoogleしてもらいたい。

実はWPFアプリの難読化には一つ問題があり、他の.NETアセンブリと同様に難読化を行うとアプリが起動しなくなる。というのはCode Behindのコードは難読化されるのにXAMLは通常無視されるので起動時にプロパティなりクラスなりが見つからずに例外が発生して終了してしまう。それなのでWPFアプリにはそれに対応したObfuscatorが必要になる。


で、いくつか評価したフリーのツールで一番のお勧めObfuscatorはcodefort.orgだ。実際には完全にフリーなわけではなく、Free版とProfessional版に分かれている。XAMLまで含めた完璧な難読化を行いたい場合はProfessional版を買い求める必要があるが、簡易な難読化でよい場合はFree版でことたりる。使い方もいたって簡単で、難読化したいdllなりexeなりを選択すれば勝手に依存しているアセンブリを探してきてくれて、Buildタブをクリックすると任意のフォルダへ必要最小構成でアプリをパッキングしてくれる。Build後のアセンブリを前述のReflectorなどで参照してみれば変数名等が変更されているのが分かると思う。

2010年5月11日火曜日

UserControlのCode behindをDataContextに指定する方法

一つ前のポストでUserControlを独自の基本クラスから継承する方法を解説したが、そのクラスをDataContextに指定する方法を解説する。

と言ってもやることは至極簡単で下記のようにDataContextに対してRelativeSource Selfを指定するだけだ。

<localModel:BaseUserControl x:Class="MyWebCrawler.Controls.Keyword"
xmlns:localModel="clr-namespace:MyWebCrawler.Models"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
</localModel:BaseUserControl>

これでそのXAML自身をDataContextとすることができたので基本クラスなどに実装されているPropertyをBinding先として指定できる。ただ、DataContextに指定するクラスがINotifyPropertyChangedを継承するか、またはDependency Propertyを使用することを忘れないように注意しよう。

UserControlを独自の基本クラスから継承する方法

下図のように似たような構造のUI要素が多い場合は、UserControlを作るにしてもまとめた機能を基本クラスにおきたくなる。


しかし、Code behindで継承元をUserControlから独自の基本クラス(BaseUserControl)へと下記のように変更しても「Partial Declaration may not have different base classes.」とコンパイルエラーが発生する。

// これだけじゃ駄目
public partial class Keyword : BaseUserControl{ }

この問題の解決方法はXAMLを下記のように変更する必要がある。

<localModel:BaseUserControl x:Class="MyWebCrawler.Controls.Keyword"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:localModel="clr-namespace:MyWebCrawler.Models">
</localModel:BaseUserControl>

2010年4月15日木曜日

WPF:デザインビューでカスタムコントロールがロードされない

WPFでカスタムコントロールを作成し、それをPage(またはWindow)に貼り付けると、Pageのデザインビューでカスタムコントロールが正しくロードされずに下図のような状態になることがままある。


で、エラーも一緒に検出されていて「Could not create an instance of 'Your control'」とロードに失敗したカスタムコントロールはこれだ!と指摘してくれている。が、個々のカスタムコントロールのデザインビューでは正常にUIを確認できるため、なぜPageに貼り付けた途端に動作しなくなるのかは皆目検討がつかない。それなので長々とGoogleした結果、やっと正解にたどり着いた。

Troubleshooting WPF Designer load failures

理由は色々と考えられるようなので、かなりの確認事項があるけれど、Expression Blendなんかを使ってデザイナーと共同作業する人はデザインビューが使えないと死亡遊戯だと思うので一読をおすすめする。

ちなみに私の場合は、別ファイルに保存してApp.xamlで統合されているResourceDictionaryがデザインタイムにロードできないせいで読み込みエラーが発生していた。そのような場合は個々のカスタムコントロールのXAMLにResourceDictionaryへの参照を以下のように追加してやれば良い。

<UserControl.Resources>
 <ResourceDictionary>
 <ResourceDictionary.MergedDictionaries>
 <ResourceDictionary Source="/Resources/MainWindowRes.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>

て、あああああああ、XAMLの構成を確認しようとしたらまたVS2008がハングった。むかつく。こいつはApp.xamlをいじるとすぐにハングしやがる。VS2010では解決済みらしいけれど、VS2010にファイルをアップデートするとExpression Blend 2が動かないというジレンマ。

2010年3月2日火曜日

Mr. Gengo(英単語勉強用アプリ)作成シリーズ ~ ListBoxにネストしたListをBindする方法

下記の画像のようなネストしたListのBindをXAML上で行う方法を解説する。



Mr. Gengoのデータ構造は下記のようになる。

// 以下、このブログポストのコードは必要な箇所以外をすべて省略している
public class Entry
{
   public ObservableCollection<entrysense> Senses { get; set; }
}

public class EntrySense
{
   public ObservableCollection<gloss> Glosses { get; set; }
}

public class Gloss
{
   public string Text{ get; set; }
}

では実際のXAMLとDataContextに渡すクラスであるJMDictModifierPresenterを見てみよう。

public class JMDictModifierPresenter
{
   public ObservableCollection<Entry> CurrentEntries { get; set; }
}

<ListBox
    ItemsSource="{Binding CurrentEntries}"
    IsSynchronizedWithCurrentItem="True" >
   <!--省略-->
</ListBox>

<ListBox
    ItemsSource="{Binding CurrentEntries.CurrentItem.Senses}"
    IsSynchronizedWithCurrentItem="True" >
   <!--省略-->
</ListBox>

<ListView ItemsSource="{Binding Path=CurrentEntries.CurrentItem.Senses.CurrentItem.Glosses}" >
 <ListView.View>
  <!--省略ここから-->
  <TextBox Text="{Binding Text}" />
  <!--省略ここまで-->
 </ListView.View>
</ListView>

ポイントはItemsSourceにBindしているCurrentItemだ。一つ目のListBoxのItemsSourceにBindしているのはDataContext(JMDictModifierPresenter)のCurrentEntries、二つ目のListBoxのItemsSourceではCurrentEntriesで現在選択されているアイテムのSensesが取得したいのでCurrentEntries.CurrentItem.Sensesとしている。ListViewのItemsSourceも同様なので説明は不要だろう。

また、それぞれのListBoxでIsSynchronizedWithCurrentItem=Trueを設定しておかないと予期した動作にならないので忘れずに設定しておこう。

英単語勉強用アプリ作成シリーズ ~ ListBoxでの思わぬ落とし穴

基本機能はほぼ網羅し、着々とアプリのほうは仕上がりつつあるのだが、こまごまとしたところで問題があるので暇を見つけてはプチプチとつぶしている日々の中で思わぬ落とし穴に数日間はまったのでそれの解説をしようと思う。


上記の画像にあるように、英単語勉強用アプリ(を改めてMr. Gengo)は辞書データを自分の好みの形へ改修できるようにしてある。それが上記のJMDictModifierなのだが、かなり困った問題があった。それは「Add Entry」「Add POS」でそれぞれのListBoxへアイテムを追加すると、追加されたアイテムがそれぞれのListBox内でまとめて選択されてしまうという現象だった。その現象は下記の画像の通り。


Ohとうならざるを得ない現象なのだが、複数行を同時に選択してしまうのはひとまず良しとして(SelectionMode=Singleにしているのでアプリ的にはまったくよくないのだが)、もっと深刻な問題はDeselectできないこと。選べるのに一度選ぶとはずせない。アイテムの追加はXAML上ではなくプログラム上で行っているのでその追加のコードが悪いのかと色々と試してみたけれど効果がない。まぁ悪いも何もObservableCollectionに対してアイテムを追加しているだけのプログラムなので試す方法も限られている。仕方がないのでGoogle先生に尋ねてみたら同じような現象で悩んでいる人が居た。

WPF ListBox Selection Problem when changing an item - Stackoverflow

実装している動作は違うようだが、発生している現象は同じようだ。それなので回答に寄せられている方法を色々ためしてみた。しかしどれも効果がない。それはそのはずで、そもそもIsSynchronizedWithCurrentItem=Trueってのは言われなくともやっているし、CollectionViewSourceを作れ!というのも元になるListをObservableCollectionでラップしてからBindしているのでやろうとしていることは同じだ。

万策尽き果てて困りつつも、ポチポチと動かないときの動作確認をしていたら、あるときは動作することを見つけた。それは、アイテムに表示されている内容がすべて異なる場合に正常動作していた。The fuck?と思いつつもどうやらListBox内でアイテムを一意に特定することができずに、同一データ内容のアイテムがまとめて選択されているようだった。「えー?!ListBox内のアイテム比較をデータ内容でやってるのー?まじー?ありえなーい」と驚愕していたら自分の過ちに思い至った。過ちは以下のコード。EntryクラスはListBoxのItemsSourceにBindされているObservableCollectionのアイテムクラスだ。

public class Entry {
// ---省略---
        public override bool Equals(object obj)
        {
            if (obj is Entry)
                return (obj as Entry).Kanji == this.Kanji;
            return false;
        }
// ---省略---
}

ひゃーーー!!!!!恥ずかしいっ!!!アイテムの比較をデータ内容でやっているのは私でしたっ!!

他の箇所でEntryクラスのデータ内容で比較するプログラムがあったので格好つけて「Equalsクラスを上書きしてスマートに比較するかな!キリッ」とかやったらご覧の有様でした。こんなくだらんことで四日間ほども悩んでいたのがアホらしすぎて泣けてくる。

2010年1月29日金曜日

英単語勉強用アプリ作成シリーズ ~ WatermarkTextBox

TextBox上に何を入力するべきかのヒントとなる文字列を表示したいと思い、色々と探し回ったけれどよい物がなく、困っていたら下記のブログポストを見つけた。

WPF TextBox Watermark – The Easy Way!

上記のサイトのコードを使用してできたのが下記の画像、右上部のTextBoxだ。


解説は参照先のサイトにStyleと実際の使用方法のマークアップがあるのでそちらを参照してほしい。

他の場所で見つけたものよりかなり簡素にできて、それでいて期待通りの動作をしてくれたので大変満足だ。

英単語勉強用アプリ作成シリーズ ~ WPF雑感

WPFの懐が想像以上に深く、勉強すればするほど色々と新しい機能が発見されるので、これを存分に使いこなせるようになるにはかなり長いことかかりそうだ。コーディングをせずにXAML上のマークアップのみで大概の機能がまかなえるので、使いこなせれば開発速度の上昇はかなり見込めるけれど、経験の浅い開発者が手を出すと、手に負えないスパゲッティコードを生み出す可能性も存分に秘めている諸刃の剣だと感じた。

一通り触ってみての感触として、WPFはHTMLとの類似点が多い。HTML + Javascript + CSSに慣れている開発者ならばとっつきやすいと思う。ButtonやTextBoxなどの基本的な部品だけに限らず、コントロールとして提供されているすべての部品の描画をWindows Formsとは違い容易に劇的に変更できるのもその思いを後押ししている。

また、マークアップのみで動的な振る舞いを実現するために用意されているTrigger、Bindingを使用すれば、従来のようなイベントドリブンなコーディングをまったく行う必要がないことも特筆すべきだ。

正直敷居は高いけれど、現代的なおしゃれなアプリを簡単に実装するためのライブラリは想像以上に充実している。

2010年1月15日金曜日

英単語勉強用アプリ作成シリーズ ~ Study WPF

前回Linq To XSDについて調査をし、いよいよ実装に入ろうとしたのだが、UI周りを通常のWindowsアプリとは違う動きのあるものにしたいな、と思い立ちWindows Presentation Foundationを使うことにした。

で、MSDNのGetting Startedにも目を通したけれど、順を追っての説明がなかったのでいまいち要領を得ない。というわけで本を購入した。



すこぶる良書なのでWPFの勉強を1から行いたい人にお勧めだ。サンプルを作りながら一つ一つの機能について詳しく説明してくれるので慣れながら学べる。
※注:.Net Framework初心者用の本ではないので、読者にはC#と.Net Framework 3.5の知識が多少なりともないと厳しいと思う。