Archive for the 'コードサンプル' Category

コード側からUI上のデータを取得しようとすると何も返ってこないサンプル

C#, コードサンプル No Comments »

「データバインディングは使わないといけないんですよ」とのコメントがありました。

いつからデータバインディングしていないと錯覚していた?

というわけでデータバインディングしててもコード側からデータを取得できないサンプル。

本当は取れますが、取得方法に気づくまで2ヶ月くらいかかりました。

xaml

<Window x:Class="wpftest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">

<Window.Resources>
<ControlTemplate x:Key="datagridTemplate" TargetType="DataGrid">
<DataGrid
ItemsSource="{Binding}" MouseRightButtonDown="DataGrid_MouseRightButtonDown">
<DataGrid.Columns>
<DataGridTextColumn Header="test" Binding="{Binding [0]}" />
</DataGrid.Columns>
</DataGrid>
</ControlTemplate>
</Window.Resources>

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Menu Grid.Row="0">
<MenuItem Header="タブ追加" Click="MenuItem_Click"  />
<MenuItem Header="選択アイテム表示" Click="MenuItem_Click1"  />
</Menu>
<TabControl Grid.Row="1" Name="maintab" />
</Grid>
</Window>

コード(cs)

using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;

namespace wpftest
{
public partial class MainWindow : Window
{
private ObservableCollection<TabItem> mainCollection = new ObservableCollection<TabItem> ();
public MainWindow()
{
InitializeComponent();
maintab.ItemsSource = mainCollection;
}
private void AddTabItem()
{
mainCollection.Add(CreateTabItem());
}
private TabItem CreateTabItem()
{
TabItem ti = new TabItem();
ti.Content = CreateDataGrid();
return ti;
}
private DataGrid CreateDataGrid()
{
DataGrid dg = new DataGrid();
dg.Template = (ControlTemplate)this.FindResource("datagridTemplate");
dg.DataContext = CreateCollection();
return dg;
}
private ObservableCollection<string> CreateCollection()
{
ObservableCollection<string> test  = new ObservableCollection<string> ();
Random random = new Random();
for(int i = 0 ;i<100;i++)
{
test.Add(random.Next().ToString());
}
return test;
}
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
AddTabItem();
}
private void MenuItem_Click1(object sender, RoutedEventArgs e)
{
TabItem ti = maintab.SelectedItem as TabItem;
DataGrid dg = ti.Content as DataGrid;
MessageBox.Show(dg.SelectedIndex.ToString());
}
private void DataGrid_MouseRightButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
DataGrid dg = sender as DataGrid;
MessageBox.Show(dg.SelectedIndex.ToString());
}
}
}

DataGridの行をどこか選択して右クリックすると選択行のインデックス番号がメッセージボックスで出ますが、メニューから選択アイテム表示を押しても-1しか返ってこないサンプルです。

普通の感覚ではこのコードでselectedindexが常に-1になるとか理解不能だと思います。

分かる人には分かるんでしょうけど、私には分かりませんでした。

まぁ結局は実装の問題です。

本来はこういうコードを書かずに、header.templateとcontent.templateをxamlで定義してやるのが正しいのでしょうが、それは別の問題を引き起こすのでやめました。多分その問題も解決できる人にとってはスパっと解決できるのでしょうが。

要するにxamlとコードでサンプルが分散していて、どうにもならないのが現状です。

Aという機能とBという機能を両方実装しようとしてサンプルを探すと、Aはxamlのサンプルしかなく、Bはコードのサンプルしかない、などという事態はザラです。そして両方を同時に使う方法が分からなくてどうにもならずに詰んでしまう。

しかしそういったコードサンプルが分散してどうしようもない事態も少しずつ改善されてきています。3年くらい前はWPFのコードを教えてくれ、という掲示板の質問に対して、WPFを知らない人がFormsのコードを返信するというのを結構見かけましたが、このごろはそういうことは無くなったように見えます。

あと3年もすれば、不自由なくなるのかもしれません。

.netで実行状況を表示しながらファイルをコピーする第3の方法

C#, コードサンプル No Comments »

おそらく知っている人は昔から知っていたであろう、ただし海外サイトを含めてもほとんど解説されていない手法を発見、または再発見したので、ここに残しておくこととします。

どなたかの役に立てれば幸いです。

第3の方法を解説する前に、第1と第2について触れておきます。

第1は言うまでも無くWin32APIの利用です。具体的にはxp以前はshfileoperation、vista以降だとifileoperationです。

この方法がもっとも確実かつ高速ですが難解です。

やたらめったら定数をたくさん定義してあれこれ調整しないと使えるようになりません。

.netでC#とかVB、あるいはjavaとかの高級言語しか触ってない人間にとっては面倒くさいことこの上なし。

コードサンプルはたくさんあるものの、使い方もわかりにくい。

というわけでおそらく皆、このAPIを使う方法はできれば避けたいはず。

第2の方法はVBのMy機能(Microsoft.VisualBasic.FileIO)を使うことです。

実態はWin32API(shfileoperation)のwrapperですが、第1の方法とは違い、ファイルのコピーを1行で書けるので、非常に重宝します。

C#でもVBのdllを参照で追加すれば使えます。

これで全部解決したかのように思えますが、大きな制限が一つあって、ファイルのコピーにしても移動にしても削除にしても、ファイルを1つずつしか処理できません。

小さいファイルを大量に処理する場合などオーバーヘッドが大きく速度的な面で使い物になりません。

番外としてはProgressBarなどを自力で実装する方法などもありえますが、そんなことするくらいだったらWin32APIを使ったほうがまだマシというものです。

また、2007年のMSDNマガジンでMSの中の人によるifileoperationの.net用wrapperが公開されているのですが、ライセンスがかなり厳しく、自分で試す分にはいいとしても、そのほかの用途にはいろいろと厳しそうです。

そして今回の第3の方法ですが、これはShell機能をC#から呼び出すというものです。

おそらくShell32.dllからでも使えるはずですが、C#でcomのshell32.dllを参照に追加すると漏れなくshell.interop.dllが生成されてしまって気持ち悪いので、別の方法を考えます。

今回はPowerShellを使った方法を書きます。ただし、同様の手法がWSH、VBScriptなどでも使えるようです。

System.Management.Automation.dllを参照に加えておきます。

最初に貼り付け、または移動したいファイルをクリップボードに貼り付けます。

クリップボードにファイルをコピーまたは切り取りする方法はその辺で探してください。

クリップボードに格納したら、PowerShellでシェルのpasteコマンドを実行します。

using System.Management.Automation;

using (var pInvoker = new RunspaceInvoke())
{
pInvoker.Invoke("$objShell = new-object -comobject shell.application");
pInvoker.Invoke("$objShell.NameSpace(17).ParseName(\""
+  "xxxxxxxx"
+ "\").InvokeVerb(\"paste\")");
}

"xxxxxxxx"の部分はファイルのコピー先を絶対パスで指定してください。

以上で完了です。

My機能との違い

1

複数ファイル・フォルダを一度に処理できる

2

My機能では表示ダイアログをオプションによりエラーのみなどと指定できたが、この方法では指定できずエクスプローラーでのコピーと同じになる。

これでファイルのコピーと移動は問題なくなりましたが、ファイルの削除はMy機能と同じく1ファイルまたは1フォルダごとの処理になってしまい、複数ファイル・フォルダを一度に削除しようとすると、相変わらずWin32APIを利用するしかありません。

うまいことShellFolderItemsを任意のファイルとフォルダから生成できればいいんでしょうが、これが難しい。

削除機能はしょうがなくMy機能を利用しております。

ホットワード フリーソフト 公開 サンプル ファイラ
割引クーポンまとめ情報 - クー割