follow me on Twitter

    2009年11月28日

    移転します

    以前からこっそり作業を進めていたのですが、はてなに移住することにしました。
    ブックマークやリンクをしてくれている方はアドレスの変更をお願いします。

    貧脚レーサーのサボり日記inはてな
     
    Posted by あにす at 18:08  |Comments(0)TrackBack(5) | 日記

    2009年11月04日

    言語バーが空気読んでキャレットに付いて来るアプリ作ってみた

    言語バーって空気読まないよね。タスクバーに入れてると作業中の視線移動が多くなるし、デスクトップに置いとくと見失う。
    だからキャレットを追い掛けるようにしてみた。

    http://www.k4.dion.ne.jp/~anis7742/Application/langbertool/langbertool_ver_1.02.zip

    もっと空気読める仕様があるんじゃないかと思案して試作品を作った。
    常にキャレットを追い掛けるのではなく、フォーカスが移ったタイミングで言語バーをキャレットの位置に移動して、数秒経ったらウィンドウの外に退避する挙動にしてみた。

    http://www.k4.dion.ne.jp/~anis7742/Application/langbertool/release.zip
     
    Posted by あにす at 09:46  |Comments(0)TrackBack(0) | プログラミングメモ

    2009年11月01日

    C#|WebBrowserで表示中のWebページのファビコンを取得

    Visual Basic 中学校 掲示板
    http://rucio.groupsite.jp/commu/ThreadDetail.aspx?ThreadId=9423

    こんなやりとりがありまして。
    最初の返信でWikipediaのリンクだけ示したのは、眠くてまともにコードが書けなかったからです。
    その後、管理人のるきおさんがコードを書いていたので、僕も書きたくなったわけです。
    眠くなかったのでサクっと出来ました。折角なのでC#に書き直してここに記録。

    using System;
    using System.Drawing;
    using System.IO;
    using System.Net;
    using System.Windows.Forms;

    public partial class Form1:Form {
    public Form1() {
    InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e) {
    this.webBrowser1.Navigate("http://blogs.dion.ne.jp/anis7742/");
    }

    private void webBrowser1_DocumentCompleted(
    object sender,
    WebBrowserDocumentCompletedEventArgs e) {

    foreach(HtmlElement linkTag
    in webBrowser1.Document.GetElementsByTagName("link")) {

    string relAttribute = linkTag.GetAttribute("rel");
    string iconUrl;

    if(relAttribute == "shortcut icon" || relAttribute == "icon") {
    iconUrl = linkTag.GetAttribute("href");

    if(iconUrl.StartsWith("http")) { //完全なURLの場合
    this.Icon = getIconFromUrl(iconUrl);
    } else if(iconUrl.StartsWith("/")) { //絶対パスの場合
    this.Icon = getIconFromUrl("http://" + e.Url.Host + iconUrl);
    } else { //相対パスの場合
    this.Icon = getIconFromUrl(e.Url.ToString() + iconUrl);
    }

    break;
    }
    }

    //タグでの指定が無い場合
    this.Icon = getIconFromUrl("http://" + e.Url.Host + "/favicon.ico");
    }

    private Icon getIconFromUrl(string url) {
    using(WebClient webClient = new WebClient())
    using(MemoryStream stream = new MemoryStream(webClient.DownloadData(url))) {
    return new Icon(stream);
    }
    }
    }

     
    Posted by あにす at 19:17  |Comments(0)TrackBack(0) | プログラミングメモ

    2009年10月21日

    2009年10月15日

    TwitterにWMPで再生中の曲を自動投稿するBOT作ってみた


    listeningnowSS

    作成に至った経緯はこんな感じで超抜粋。

    kyubot 【 @mitsugogo より急募】WMPで再生中の曲をTwitterに投稿するプラグ印
    anis774 @mitsugogo 詳細希望。面白そうなら作ります
    mitsugogo @anis774 Windows Media Playerで再生している曲名やアルバム名などをTwitterに流したいんです。今使ってみているプラグインですと、日本語で若干文字化けが発生するようなので・・・
    anis774 @mitsugogo ちょっとやってみますね
    sedna2501 @anis774 欲張り言うとこんな感じの無理でしょうか? ちょっとtext編集出来ると嬉しい・・・ http://d.hatena.ne.jp/brazil/20070413/1176394371

    てな感じ。

    listeningnow.zip

    作者:あにす
    連絡先:anis774_for2ch@yahoo.co.jp
    ライセンス:NYSL

    【りすにんぐなうってなに?】
    Windows Media Player で再生中の曲名をTwitterに投稿するBOTだよ。

    【使い方】
    WMPをメッセンジャーに曲名が送信されるように設定してね。
    settings.iniを適当に編集してね。
    intervalの単位は秒だよ。
    りすにんぐなう.exeを起動してね。
    起動したらintervalで設定した秒数ごとにタイマーが走るよ。
    WMPから曲名を受信したらテキストボックスの内容が更新されるよ。
    タイマーの時間が来たらテキストボックスの内容がTwitterに投稿されるよ。
    それまでの間に投稿する内容を編集出来るよ。
    タイマーの時間が来てもテキストボックスの内容が前回の投稿時から変わってなかったら投稿されないよ。
    テキストボックスにフォーカスがある間はタイマーの時間が来ても投稿されないからゆっくり編集出来るよ。
    テキストボックスの内容の投稿に成功するとボタンが押せなくなるよ。
    settings.iniは正しく編集しないと容赦なくエラーで落るよ。
    %0は曲名、%1はアーティスト名に置換されるみたいだよ。

    何かあったらTwitterで@anis774に突っ込んでね。メール送っても気付かないかもね。

    ※2009/10/15 7:00頃
    書き込みに失敗すると残り時間の表示が恥しいことになるのを修正してコッソリと差し替え
    ※2009/10/17 23:30頃
    自動投稿までの残り時間の表示が大変なことになる不具合を修正、ついてにわかりやすい表示に変更。こっそり差し替え
     
    Posted by あにす at 04:46  |Comments(0)TrackBack(1) | プログラミングメモ

    2009年10月12日

    どう書く?org|居眠り床屋問題

    居眠り床屋問題に投稿しました。久し振りのどう書くです。
    マルチスレッドはちょっと苦手意識があるんだけど、動いてるからきっと合ってる。
    一番乗りぃ!
    http://ja.doukaku.org/comment/9628/
     
    Posted by あにす at 09:41  |Comments(0)TrackBack(0) | プログラミングメモ , 日記

    2009年10月09日

    2009年09月01日

    あの楽器じゃないけどC#で楽器作ってみた

    普通のPCを使ってガチ演奏って出来ないかなぁと思って色々試してみました。

    途中経過はmixiで全体に公開の設定で書いたので、アカウントをお持ちの方はそっちで。
    http://mixi.jp/view_diary.pl?id=1264945935&owner_id=2620995
    http://mixi.jp/view_diary.pl?id=1265825769&owner_id=2620995
    http://mixi.jp/view_diary.pl?id=1265873603&owner_id=2620995
    http://mixi.jp/view_diary.pl?id=1267163178&owner_id=2620995
    http://mixi.jp/view_diary.pl?id=1270510815&owner_id=2620995

    つまりはソフトウェアキーボードを作ろうってことです。あれはMIDIというプロトコルで音源にメッセージを送ることで音を出しているようです。音源はWindowsに標準で付いています。MIDI規格で通信する為のWindowsAPIがあることもわかりました。

    作り方を超簡単に書くと、midiOutOpen関数でMIDIデバイスを開いてmidiOutShortMsg関数で音を出す等のメッセージを送って、使い終わったらmidiOutResetしてmidiOutCloseで後始末完了です。"MIDI プログラミング"とかで検索してもDTM関連ばかりで使える情報にはなかなか辿り着けないけど、上記の関数名で検索すると詳しい情報がHITします。

    結果出来たのがこれ
    http://www.k4.dion.ne.jp/~anis7742/anogakki.zip
    実験と称して汚いコード書いてるので実行ファイルのみの公開です。
    操作方法は以下の通り
    ・左手のキーで対応した音階が鳴る。
    ・↑↓キーで音色が切り替わる。
    ・マウスホイールでオクターブが切り替わる。
    ・左ボタンを押しながらマウスを縦方向に動かすとピッチが変わる。
    ・右ボタンを押しながらマウスと横方向に動かすとビブラートがかかる。

    僕の下手クソな演奏。こんな感じの表現が出来ます。
    http://www.k4.dion.ne.jp/~anis7742/wavemode20090828224419.wav
    ※規定のMIDIデバイスがMicrosoft GS Wavetable SW Synth以外になってると音が鳴らない不具合があって原因を調査中です。←直った

    ここまでやってみて、次の問題があることがわかった。
    1.デバイスから得られるフィードバックが、その操作の結果として出る音しかない。通常の楽器は身体と楽器の接点の感触から色々な情報を得て演奏している。画面を工夫することで若干の改善は可能だが、限界がある。

    2.キーボードのストロークの深さが正確なタイミングでの操作を妨げる。ストロークが浅いキーボードや、Realforceの様な奥まで押し切らなくても反応するキーボードで改善されるかも。

    3.操作してから音が鳴るまでのタイムラグがある。音源や出力デバイス、その他ソフトウェア等でどこまで改善出来るか。

    マウスとキーボードを本来の目的以外に使うわけだから色々無理があるわけですね。
     
    Posted by あにす at 08:16  |Comments(0)TrackBack(0) | プログラミングメモ , 日記

    2009年08月22日

    メーラーを電信八号からSylpheedに乗り換えた

    メーラーを電信八号からSylpheedに乗り換えた。何故かって?飽きたから。電八で何か不便な点や不満があったわけじゃない。

    メーラーの選択の条件
    ・細かいメールの選別を全てPOPFileに任せているので、ヘッダの内容でメールを振り分けられること。
    ・1メール1テキストファイルで保存されること。
    ・軽いこと
    ・今後の乗り換えに備えてメールのインポート、エクスポート機能があること

    で、Sylpheedに決めた。

    電八からSylpheedへのメールの変換はちょちょいと以下のようなC#のコードをこさえてサクッと完了。厳密に正しいコードかはわからないけど、上手くいったからよし。
    using System.IO;
    using System.Text;

    class Program {
    static void Main(string[] args) {
    int fileNumber = 0;
    Encoding enc = Encoding.GetEncoding("Shift-Jis");
    foreach(string mailFile in Directory.GetFiles(
    "電八のメールフォルダ",
    "*.txt",
    SearchOption.AllDirectories)) {

    string mailTxt = File.ReadAllText(mailFile, enc);
    mailTxt = mailTxt.Replace("--------\r\n", "\r\n");
    File.WriteAllText(
    Path.Combine("Sylpheedのメールフォルダ", fileNumber.ToString()),
    mailTxt,
    enc);
    fileNumber++;
    }
    }
    }

    Linux由来のメーラーだからなのか、キーバインド等の細かい挙動がWindowsアプリと違って戸惑うけど、動作が軽快でいいと思う。
     
    Posted by あにす at 03:57  |Comments(0)TrackBack(0) | プログラミングメモ , 日記

    2009年08月21日

    非常駐型付箋紙アプリSPD配布

    常駐せずに壁紙に付箋を埋め込む非常駐型付箋紙アプリSPDの配布エントリーです。

    Vectorの新着ソフトレビューでSPDが紹介されました。


    SPD_SS_0

    【特徴】
    ・常駐せずに付箋を壁紙に保持する
    ・壁紙の配置は拡大だけでなく並べて表示、中央に表示にも対応。
     拡大表示での画質の低下が気になる方にもご利用頂けます。
    ・付箋の半透明での描画に対応。デスクトップの圧迫感を和らげます。
    ・付箋ごとにフォントと文字色、背景色を指定可能。
    ・付箋の重ね貼りが可能。
    ・付箋の位置を決める為の面倒な操作がありません。付箋を編集しているウィンドウを張りたい位置にD&Dするだけ。見たままの状態で壁紙に張り付きます。

    【想定環境】
    Windows2000以降,Microsoft .NET Framework 2.0

    【動作確認環境】
    WindowsXP SP3,Microsoft .NET Framework 2.0

    【最新版DL】
    SPD_Ver_1_82.zip(35.6KB)
    Ver 1.81 → Ver 1.82
    ・ポータブルモード時に印刷範囲指定フォームで範囲が正しく選択されない問題を修正。
    ・印刷範囲指定フォームの位置を保存するように変更。
    Ver 1.80 → Ver 1.81
    ・マウスホイールで不透明度を変更する際に、正しく10%刻みにならない不具合を修正。
    ・印刷時に印刷範囲を指定するフォームを表示するように変更。

    SPD_Ver_1_80.zip(33.7KB)
    Ver 1.76 → Ver 1.80
    ・画面を切り取って付箋にする機能を実装。
    ・付箋の透明度を個別に指定出来るように変更。
    ・タスクトレイのアイコンの右クリックメニューから壁紙を変更出来る機能を実装。
    ・前回終了時から壁紙が変更されている時にそれを反映する機能を実装。
    ・ポータブルモードを実装。
    ・利用規約を改定。

    ソースコード(Visual C# 2008 Express Edition)
    SPD_SRC_Ver_1_82.zip(56.5KB)

    【タスク】
    ・印刷機能こんなんでいいのかなぁとか考え中。
     

    2009年08月07日

    どう書く?org|Twitterへの投稿

    GoogleReaderでRSSのチェックをしていたらどう書く?のフィードが更新されたので、以前書いたコードを引っ張り出してちょちょいと手を加えてカカッと投稿。

    真の一番乗りゲットォ!

    http://ja.doukaku.org/comment/9448/
     
    Posted by あにす at 00:35  |Comments(0)TrackBack(0) | プログラミングメモ

    2009年07月07日

    ちょっとC#でパスワードを覗いてみた。

    パスワード入力用のテキストボックスで、入力しても"******"としか表示されないものがありますよね。あれ、不便ですよね。自分で入力したのに見えないなんて理不尽ですよね。
    そこで、無理やり入力内容が見える様にするプログラムを書いてみました。Vector辺りを探せばあるような気がするけど気にしたら負けでしょう。

    実はアレ、ただのテキストボックスなのでメッセージ投げれば好きなように弄れちゃうんですよね。で、今回投げたのはEM_SETPASSWORDCHAR。EnumChildWindows関数でウィンドウを列挙して手当たり次第にPostMessageしてます。

    はい、コードです。
    using System;
    using System.Runtime.InteropServices;

    namespace ConsoleApplication1 {
        class Program {
            const int EM_SETPASSWORDCHAR = 0xCC;

            delegate int WNDENUMPROC(IntPtr hwnd, int lParam);

            [DllImport("user32.dll")]
            private static extern int EnumChildWindows(
                IntPtr hWndParent, 
                WNDENUMPROC lpEnumFunc, 
                int lParam);

            [DllImport("user32.dll")]
            private static extern int PostMessage(
                IntPtr hwnd, 
                int wMsg, 
                int wParam, 
                int lParam);

            static void Main(string[] args) {
                EnumChildWindows(IntPtr.Zero, EnumWindowsProc, 0);
            }

            static int EnumWindowsProc(IntPtr hWndParent, int lParam) {
                PostMessage(hWndParent, EM_SETPASSWORDCHAR, 0, 0);
                EnumChildWindows(hWndParent, EnumWindowsProc, 0);
                return 1;
            }
        }
    }

    これでパスワード丸見えなんですってば。
     
    Posted by あにす at 20:30  |Comments(0)TrackBack(0) | プログラミングメモ

    2009年07月03日

    逆P/Invokeを駆使してTTBaseのプラグインを作れるか?

    TTBase http://ttbase.sourceforge.jp/
    逆P/Invoke http://www.artonx.org/diary/20081124.html#p01

    これでC#でTTBaseのプラグイン作れるんじゃね?と思った。

    TTBase.cs TTBaseで使用する構造体とか定数とか

    using System;
    using System.Runtime.InteropServices;

    namespace TTBasePlugInTest {
        //プラグインのロードタイプ
        public enum LoadType {
            ptAlwayLoad = 0,
            ptLoadAtUse = 1,
            ptSpecViolation = 0xFFFF
        }

        //メニューに関する定数
        public enum Menu {
            dmNone = 0,
            dmSystemMenu = 1,
            dmToolMenu = 2,
            dmHotKeyMenu = 4,
            dmMenuChecked = 8
        }

        public enum LogOut {
            elNever = 0,
            elError = 1,
            elWarning = 2,
            elInfo = 3,
            elDebug = 4
        }

        [StructLayout(LayoutKind.Sequential,CharSet = CharSet.Ansi)]
        public struct PLUGIN_COMMAND_INFO {
            [MarshalAs(UnmanagedType.LPStr)]
            public string Name;
            [MarshalAs(UnmanagedType.LPStr)]
            public string Caption;
            public int CommandID;
            public int Attr;
            public int ResID;
            public int DispMenu;
            public uint TimerInterval;
            public uint TimerCounter;
        }

        [StructLayout(LayoutKind.Sequential,CharSet= CharSet.Ansi)]
        public struct PLUGIN_INFO {
            public ushort NeedVersion;
            [MarshalAs(UnmanagedType.LPStr)]
            public string Name;
            [MarshalAs(UnmanagedType.LPStr)]
            public string FileName;
            public ushort PluginType;
            public uint VersionMS;
            public uint VersionLS;
            public uint CommandCount;
            [MarshalAs(UnmanagedType.LPStruct)]
            public IntPtr Commands;
            //public PLUGIN_COMMAND_INFO Commands;
            public uint LoadTime;
        }
    }

    Class1.cs TTBaseに公開するメソッドを実装

    using System;
    using System.Runtime.InteropServices;

    namespace TTBasePlugInTest {
        public class Class1 {
            public static IntPtr TTBEVent_InitPluginInfo(string PluginFilename) {
                PLUGIN_INFO r = new PLUGIN_INFO();
                r.FileName = PluginFilename;
                r.NeedVersion = 0;
                r.Name = "テスト";
                r.PluginType = (int)LoadType.ptAlwayLoad;
                r.CommandCount = 1;

                PLUGIN_COMMAND_INFO pci = new PLUGIN_COMMAND_INFO();
                pci.Name = "test";
                pci.Caption = "てすてす、テストのテストです";
                pci.CommandID = 0;
                pci.DispMenu = (int)(Menu.dmToolMenu | Menu.dmHotKeyMenu);

                IntPtr pciPtr = Marshal.AllocHGlobal(Marshal.SizeOf(pci));
                Marshal.StructureToPtr(pci, pciPtr, false);
                r.Commands = pciPtr;
                //r.Commands = pci;

                IntPtr iPtr = Marshal.AllocHGlobal(Marshal.SizeOf(r));
                Marshal.StructureToPtr(r, iPtr, false);

                return iPtr;
            }

            public static void TTBEvent_FreePluginInfo(IntPtr PluginInfo) {
                Marshal.FreeHGlobal(PluginInfo);
            }

            public static int TTBEvent_Init(string PluginFilename, int hPlugin) {
                return 1;
            }

            public static void TTBEvent_Unload() {

            }

            static int i;

            public static int TTBEvent_Execute(int CommandID, IntPtr hWnd) {
                i++;
                System.Windows.Forms.MessageBox.Show(i.ToString());
                return 1;
            }

            public static void TTBEvent_WindowsHook(int Msg, int wParam, int lParam) {

            }
        }
    }

    上のコードをビルドしたら

    D:\My Documents\Visual Studio 2008\Projects\TTBasePlugInTest\TTBasePlugInTest\bi
    n\Release>ildasm /OUT=TTBasePlugInTest.il TTBasePlugInTest.dll

    でILにする。
    逆P/Invokeを参考にILを編集

    D:\My Documents\Visual Studio 2008\Projects\TTBasePlugInTest\TTBasePlugInTest\bi
    n\Release>c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\ilasm.exe /DLL TTBasePlu
    gInTest.il

    Microsoft (R) .NET Framework IL Assembler. Version 2.0.50727.3053
    Copyright (c) Microsoft Corporation. All rights reserved.
    Assembling 'TTBasePlugInTest.il' to DLL --> 'TTBasePlugInTest.dll'
    Source file is ANSI

    Assembled method TTBasePlugInTest.Class1::TTBEVent_InitPluginInfo
    Assembled method TTBasePlugInTest.Class1::TTBEvent_FreePluginInfo
    Assembled method TTBasePlugInTest.Class1::TTBEvent_Init
    Assembled method TTBasePlugInTest.Class1::TTBEvent_Unload
    Assembled method TTBasePlugInTest.Class1::TTBEvent_Execute
    Assembled method TTBasePlugInTest.Class1::TTBEvent_WindowsHook
    Assembled method TTBasePlugInTest.Class1::.ctor
    Creating PE file

    Emitting classes:
    Class 1: TTBasePlugInTest.Class1
    Class 2: TTBasePlugInTest.PLUGIN_INFO
    Class 3: TTBasePlugInTest.PLUGIN_COMMAND_INFO
    Class 4: TTBasePlugInTest.LoadType
    Class 5: TTBasePlugInTest.Menu
    Class 6: TTBasePlugInTest.LogOut

    Emitting fields and methods:
    Global
    Class 1 Fields: 1; Methods: 7;
    Class 2 Fields: 9;
    Class 3 Fields: 8;
    Class 4 Fields: 4;
    Class 5 Fields: 6;
    Class 6 Fields: 6;
    Resolving local member refs: 13 -> 13 defs, 0 refs, 0 unresolved

    Emitting events and properties:
    Global
    Class 1
    Class 2
    Class 3
    Class 4
    Class 5
    Class 6
    Resolving local member refs: 0 -> 0 defs, 0 refs, 0 unresolved
    Writing PE file
    Operation completed successfully

    とILからDLLを作成。メッセージの意味はよくわからないけど、多分成功してる。

    これで出来たDLLをTTBaseのディレクトリにコピーしてTTBaseを起動するも…プラグインとして認識されない。
    どこが上手く行ってないのかもわからない状態。

    http://twitter.com/takeshik/statuses/2419234928

    とか言われたけどC++/CLIとかキモいし。

    http://twitter.com/takeshik/statuses/2419254967

    とか突っ込まれたけどその通りだと思う。

    C#でやることに意味があるのですよ。C#可愛いよ、C#可愛い。
    でもC#で出来て無いから何の意味も無いという…。
     
    Posted by あにす at 15:26  |Comments(0)TrackBack(0) | プログラミングメモ

    2009年06月10日

    TwitterにRSSのフィードをポストして皆に評価してもらうBOTを作ってみた

    http://twitter.com/anis774_feed

    フォローするとTLにRSSのエントリが投稿されます。
    ★の数が評価の状態を表します。

      @anis774_feed + タイトル

    でエントリの評価が上がります。

      @anis774_feed - タイトル

    でエントリの評価が下がります。

      @anis774_feed add フィードのURL

    でフィードを登録することが出来ます。

    エントリの評価はベイズ理論で求められるので、新しいエントリの評価が次第に最適化されていく仕組みです。

    URLが短縮されてaddコマンドが失敗するんだけど?
    @anis774_feedはh抜きを理解します。URLをh抜きにしてURLの短縮を回避して下さい。
     
    Posted by あにす at 00:08  |Comments(0)TrackBack(0) | TwitterFeedBot

    2009年06月01日

    メガネが無いと…

    ドイツで放送禁止になった、メガネ着用の必要性を訴えるCMらしいです。



    これは酷い。
     
    Posted by あにす at 07:56  |Comments(0)TrackBack(0)

    2009年05月19日

    最寄の自転車屋さんに行ってみた

    英式バルブ用のポンプを持っていないため、空気を入れに近所の自転車屋さんに行ってみた。ショーウィンドウにはSORAとかTIAGRAとかが載ったロードが飾られている店。
    店頭の空気入れだけ使っていくのはあれなので、店内を見ていくことにした。
    「ごゆっくりどうぞー」
    あぁ、良い店だ。この距離感は好きだな。プロショップって感じではないな。その店のお兄さんと少し話したんだけど、なぜかライトウェイのカタログを渡されて帰ってきた。
    どうせならシマノのカタログ貰って来れば良かった。今は全部10速らしいし、互換性とかよくわかんないし。
    ライトウェイのカタログ、見るところが無い……。
     
    Posted by あにす at 10:51  |Comments(0)TrackBack(0) | 日記 , 自転車

    2009年04月04日

    C#|PropertyGridコントロールに表示されるプロパティ名を変更するには?

    PropertyGridコントロールに表示されるプロパティ名を変更するには?
    SPDVer 1.74に取り込み完了。

    とメモしておけば万一上記リンクが切れてもSPDVer 1.74のコードからサルベージ可能。あにすのコード保管庫に転載するわけにもいかないしね。

    あにすのコード保管庫はあにすが書いたコードの保管庫だけど、あにすが書いてないコードも保管したいな。何か良い方法は無いだろうか。公開ブックマーク的なものがあればいいのかな。
     

    2009年03月30日

    C#|ユーザーコントロールをD&Dで移動&サイズ変更

    [C#]ユーザーコントロールをドラッグアンドドロップで移動で面白そうなことやってるので書いてみました。結構泥臭くなりますね。ちょっと嵌ってしまって時間が掛かったのはここだけの秘密。

    using System.Drawing;
    using System.Windows.Forms;

    public partial class UserControl1 :UserControl {
        Size lastMouseDownSize;
        Point lastMouseDownPoint;
        DAndDStatus status;
        int sizeChangeArea = 8;

        public UserControl1() {
            InitializeComponent();
        }

        protected override void OnMouseDown(MouseEventArgs e) {
            lastMouseDownPoint = e.Location;
            lastMouseDownSize = this.Size;

            //動作を決定
            status = DAndDStatus.None;
            if(getTop().Contains(e.Location)) {
                status |= DAndDStatus.Top;
            }
            if(getLeft().Contains(e.Location)) {
                status |= DAndDStatus.Left;
            }
            if(getBottom().Contains(e.Location)) {
                status |= DAndDStatus.Bottom;
            }
            if(getRight().Contains(e.Location)) {
                status |= DAndDStatus.Right;
            }

            if(status == DAndDStatus.None) {
                status = DAndDStatus.Move;
            } else {
                this.Capture = true;
            }

            base.OnMouseDown(e);
        }

        protected override void OnMouseMove(MouseEventArgs e) {
            //カーソルを変更
            if((getTop().Contains(e.Location) && 
                getLeft().Contains(e.Location)) ||
                (getBottom().Contains(e.Location) && 
                getRight().Contains(e.Location))) {

                this.Cursor = Cursors.SizeNWSE;
            } else if((getTop().Contains(e.Location) && 
                getRight().Contains(e.Location)) ||
                (getBottom().Contains(e.Location) && 
                getLeft().Contains(e.Location))) {

                this.Cursor = Cursors.SizeNESW;
            } else if(getTop().Contains(e.Location) || 
                getBottom().Contains(e.Location)) {
                
                this.Cursor = Cursors.SizeNS;
            } else if(getLeft().Contains(e.Location) || 
                getRight().Contains(e.Location)) {
                
                this.Cursor = Cursors.SizeWE;
            } else {
                this.Cursor = Cursors.Default;
            }


            if(e.Button == MouseButtons.Left) {
                int diffX = e.X - lastMouseDownPoint.X;
                int diffY = e.Y - lastMouseDownPoint.Y;

                if((status & DAndDStatus.Move) == DAndDStatus.Move) {
                    this.Left += diffX;
                    this.Top += diffY;
                }
                if((status & DAndDStatus.Top) == DAndDStatus.Top) {
                    int h = this.Height;
                    this.Height -= diffY;
                    this.Top += h - this.Height;
                }
                if((status & DAndDStatus.Bottom) == DAndDStatus.Bottom) {
                    this.Height = lastMouseDownSize.Height + diffY;
                }
                if((status & DAndDStatus.Left) == DAndDStatus.Left) {
                    int w = this.Width;
                    this.Width -= diffX;
                    this.Left += w - this.Width;
                }
                if((status & DAndDStatus.Right) == DAndDStatus.Right) {
                    this.Width = lastMouseDownSize.Width + diffX;
                }
            }

            this.Refresh();

            base.OnMouseMove(e);
        }

        protected override void OnMouseUp(MouseEventArgs e) {
            this.Capture = false;

            base.OnMouseUp(e);
        }

        protected override void OnPaint(PaintEventArgs e) {
            e.Graphics.DrawRectangle(
                Pens.Black,
                0, 0,
                this.Width - 1,
                this.Height - 1);

            base.OnPaint(e);
        }

        private Rectangle getTop() {
            return new Rectangle(0, 0, this.Width, sizeChangeArea);
        }

        private Rectangle getBottom() {
            return new Rectangle(0, this.Height - sizeChangeArea,
                this.Width, sizeChangeArea);
        }

        private Rectangle getLeft() {
            return new Rectangle(0,0,
                sizeChangeArea, this.Height);
        }

        private Rectangle getRight() {
            return new Rectangle(this.Width - sizeChangeArea, 0,
                sizeChangeArea, this.Height);
        }

        private enum DAndDStatus {
            None = 0,
            Move = 1,
            Top = 2,
            Bottom = 4,
            Left = 8,
            Right = 16,
        }
    }

     
    Posted by あにす at 05:43  |Comments(0)TrackBack(0) | プログラミングメモ

    2009年03月21日

    C#|テキストボックス上でのD&Dでフォームを移動する

    TextBoxコントロール上でのドラッグ&ドロップでフォームを移動させようとした場合、文字列を選択する操作とバッティングしないようにする必要がある。
    以下の実装で良い感じに動いた。

    Point mouseDownPoint = new Point();
    bool dAndDMoveFlag = false;

    private void textBox1_MouseDown(object sender, MouseEventArgs e) {
        if(e.Button != MouseButtons.Left) return;
        mouseDownPoint = e.Location;
        bool flag = true;
        int charHeight = 
            TextRenderer.MeasureText("A", this.textBox1.Font).Height;
        
        for(int i = 0; i < this.textBox1.Lines.Length; i++) {
            Point point = new Point(0, i * charHeight);
            Size size = 
                TextRenderer.MeasureText(
                    this.textBox1.Lines[i], 
                    this.textBox1.Font);
            Rectangle rect = new Rectangle(point, size);
        
            if(rect.Contains(e.Location)) {
                flag = false;
                break;
            }
        }
        
        this.dAndDMoveFlag = flag;
    }

    private void textBox1_MouseMove(object sender, MouseEventArgs e) {
        if(dAndDMoveFlag) {
            Point p = this.Location;
            p.Offset(
                new Point(e.X - mouseDownPoint.X, e.Y - mouseDownPoint.Y));
            this.Location = p;
        }
    }

    private void textBox1_MouseUp(object sender, MouseEventArgs e) {
        dAndDMoveFlag = false;
    }


    素早くドラッグ&ドロップしたときに文字が選択されてしまいフォームの移動がキャンセルされるのを回避する為にdAndDMoveFlag(センスねー名前w)でフラグを立てている。
    あとはそんなに難しいことしてないと思う。

    ※textBox1のTextが空の場合にLinesプロパティの境界外を参照して例外が発生していたので修正した。
    ※気が変わったので違うロジックでゴッソリと書き換えた。
     
    Posted by あにす at 07:30  |Comments(0)TrackBack(0) | プログラミングメモ

    2009年03月13日

    C#|System.Windows.Forms.ColorDialogのバグ?

    ColorDialogクラスのCustomColorsプロパティが期待通りに動作しない。

    フォームの背景色をColor.FromArgb(50, 100, 150)に設定している。ちなみにこんな色。

    ColorDialogBug1

    ボタンがクリックされるとColorDialogのCustomColorsプロパティにColor.FromArgb(50, 100, 150)を設定して表示する。

    ColorDialogBug2

    おかしい、追加した色が黒く表示されている。色の作成ボタンを押してRGB値を見てみると

    ColorDialogBug3

    RとBが反転している。これは困った。

    using System;
    using System.Drawing;
    using System.Windows.Forms;

    public partial class Form1 :Form {
        Color color = Color.FromArgb(50, 100, 150);

        public Form1() {
            InitializeComponent();
            this.BackColor = color;
        }

        private void button1_Click(object sender, EventArgs e) {
            this.colorDialog1.CustomColors = new int[] { color.ToArgb() };
            this.colorDialog1.ShowDialog();
        }
    }



    どうやらバグではないらしい。
     
    Posted by あにす at 17:06  |Comments(0)TrackBack(0) | プログラミングメモ