佐々木屋

技術的なことから趣味まで色々書きます

iPhoneのSafari対応 Javascriptからクリップボードを設定

会社の携帯が変更になるようで、それに伴い色々設定作業が入ります。
設定作業自体は部下にやらせるとして、やはり不用意にパスワードなどは見せたくないわけで。

そうした時にパスワードをクリップボード経由で扱えばいいのですが、iPhoneクリップボードはちょっと特殊のようで。


仕組みとして直接クリップボードへ値を入れることは出来ません。但し、テキストボックスの値を疑似的に選択状態にすればクリップボードへの代入が可能になります。

手順としては、ダミーのテキストボックス(txtPassword )を1個用意してそこに値を設定し、その値を選択状態にしてクリップボードへコピーします。

var txtPassword = document.getElementById("txtPassword ");
var range = document.createRange();
range.selectNode(txtPassword);
window.getSelection().addRange(range);
document.execCommand('copy');
alert("コピーしました。");

後は空文字をテキストボックスに代入し、それを同様の処理でクリップボードへコピーすればクリップボード内を削除出来ます。ダミーのテキストボックスの値は文字色を白にしたり、コピー直後にリダイレクトして値を削除したり、色々工夫すれば担当者レベルであれば目くらましになるでしょう。

MIMETypeの取得

ファイルのMIMETypeを取得する方法です。
.NET Frameworkによって違いますので、お好みの方をどうぞ。

.NET Framework4.5以上

System.Web.MimeMappingクラスのGetMimeMappingメソッドへファイル名を渡すことで取得可能です。

System.Web.MimeMapping.GetMimeMapping("ファイル名");
System.Web.MimeMapping.GetMimeMapping("ファイル名")

GetMimeMappingメソッドはフルパスを渡しても結果的には一緒です。恐らく内部的にファイル拡張子から判断しているものと思われます。

ですので、拡張子が認識できない場合はnullを返さずoctet-streamを返します。ファイル有無も関係ありません。

.NET Framework4.0まで

System.Web.MimeMappingクラスでは取得できませんので、ClassRootレジストリからContent Typeを取得する方法となります。

使い方によりけりですが、GetMimeMappingメソッドと合わせるならデフォルトでoctet-streamを返す仕様が好ましいです。あえてnullを返すという手もありますが、臨機応変にどうぞ。

private string GetMimeTypeByExtension(string ext) {
    string res = "application/octet-stream";

    try {
        var key = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(ext);
        if (key != null) {
            var mimetype = key.GetValue("Content Type");
            if (mimetype != null) res = (string)mimetype;
        }
    }
    catch { }

    return res;
}
Private Function GetMimeTypeByExtension(ByVal ext As String) As String
    Dim res As String = "application/octet-stream"

    Try
        Dim key = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(ext)
        If key IsNot Nothing Then
            Dim mimetype = key.GetValue("Content Type")
            If mimetype IsNot Nothing Then res = CType(mimetype, String)
        End If
    Catch ex As Exception
    End Try

    Return res
End Function



if文のところはnull条件演算子を使えばもっと簡潔に書けますが、.NET Framework4.0では使用できないので少し読みにくいですが仕方ありません。

IIS管理外の画像ファイルを表示させる

ASP.NETIIS管理内のディレクトリの画像ファイルをImageコントロールへ動的に表示させるためには以下で簡単にできますが、

img.ImageUrl = ”相対パス”

これを別のディレクトリに置いた画像を表示させたい場合は、System.Drawing名前空間のBitmapクラスへインスタンス展開して、それをResponse.OutputStreamに向けて保存すれば表示可能です。

protected void Page_Load(object sender, EventArgs e) {
    Bitmap bmp = new Bitmap(@"pngファイル絶対パス");
    Response.ContentType = "image/png";
    Response.Flush();
    bmp.Save(Response.OutputStream, ImageFormat.Png);
    Response.End();
}
Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
    Dim bmp As New Bitmap("pngファイル絶対パス")
    Response.ContentType = "image/png"
    Response.Flush()
    bmp.Save(Response.OutputStream, ImageFormat.Png)
    Response.End()
End Sub



で、ここまでは簡単な話ですが、ここで終わってはツマランわけで。
折角なので、MIMEや保存時のImageFormatを表示させるファイルの拡張子によって動的に設定できれば汎用性はぐっとあがります。

最初の1行目のビットマップクラスへの展開によって、ContentTypeとImageFormatを動的に取得するようなクラスを考えればよいことになります。

オブジェクト指向の観点を考慮しつつ構築する演習としてみましょう。
ということで、このまま演習へ引き継ぎます。

Strategyパターン

デザインパターンの中でもよく見るものですが、オブジェクト指向が分からないとチンプンカンプンになります。

「戦略」という意味で、メソッドなどにちりばめられた静的なアルゴリズムを、ifやswitch(VB.NETはSelect Case)などの条件分岐で動的にアルゴリズムごと変更してしまう手法です。

このパターンによってアルゴリズムに変更が生じても、条件分岐で意図しない影響が出ないようになります。

まずインターフェースを定義します。

Public interface IStrategy {
    void Work();
}
Public Interface IStrategy
    Sub Walk()
End Interface

次にそれらを実装したクラスを定義します。

public class Class1 : IStrategy {
    public void Walk() {
        Console.WriteLine("テクテク歩く");
    }
}

public class Class2 : IStrategy {
    public void Walk() {
        Console.WriteLine("ノソノソ歩く");
    }
}

public class Class3 : IStrategy {
    public void Walk() {
        Console.WriteLine("サクサク歩く");
    }
}
Public Class Class1
    Implements IStrategy

    Public Sub Walk() Implements IStrategy.Work
        Console.WriteLine("テクテク歩く")
    End Sub
End Class

Public Class Class2
    Implements IStrategy

    Public Sub Walk() Implements IStrategy.Work
        Console.WriteLine("ノソノソ歩く")
    End Sub
End Class

Public Class Class3
    Implements IStrategy

    Public Sub Walk() Implements IStrategy.Work
        Console.WriteLine("サクサク歩く")
    End Sub
End Class

最後に、インターフェースを通じて必要な処理を返す(委譲させるための)クラスを定義します。

public class Context {
    public IStrategy Strategy { set; get; }

    public void Execute() {
        Strategy.Walk();
    }
}
Public Class Context
    Public Property Strategy As IStrategy

    Public Sub Execute()
        Strategy.Walk()
    End Sub
End Class

こうすることで、メインコードからアルゴリズムを切り離してカプセル化することにより、不用意な操作やアルゴリズムの変更に対応できるようになります。

var cls = new Context();

cls.Strategy = new Class1();
cls.Execute();

cls.Strategy = new Class2();
cls.Execute();

cls.Strategy = new Class3();
cls.Execute();
Dim cls As New Context()

cls.Strategy = New Class1()
cls.Execute()

cls.Strategy = New Class2()
cls.Execute()

cls.Strategy = New Class3()
cls.Execute()

オブジェクト指向の導入

課題

手続き型でよく見る配列に情報を持つ場合(カンマ区切りなども含めて)があります。一番の問題は何番目に何のデータを保持しているのかが分かりにくいということです。これは非常に危険なデータの持ち方でもあります。

string[] person = new string[2];
person[0] = "山田太郎";
person[1] = "21";
person[2] = "090********";
Dim person(2) As String
person(0) = "山田太郎"
person(1) = "21"
person(2) = "090********"


リファクタリング

オブジェクト指向を導入することで一気に解決します。Personクラスをオブジェクトとして構築し、そこで管理させればよいのです。

public class Person {
    public string KanjiName { get; set; }
    public int Age { get; set; }
    public string Tell { get; set; }
}

Person p = new Person();
p.KanjiName = "山田太郎";
p.Age = 21;
p.Tell = "090********";
Public Class Person
    Public Property KanjiName As String
    Public Property Age As Integer
    Public Property Tell As String
End Class

Dim p As New Person()
p.KanjiName = "山田太郎"
p.Age = 21
p.Tell = "090********"

クラス化することでプロパティで桁数チェックや体裁チェックなども可能となります。その他にも、複数のインスタンスを作成してジェネリックコレクションにすれば、配列によるインデックス管理も行わなくてよくなります。

Sys.Extended.UI.ModalPopupBehaviorがnullになる件

ASP.NET Ajaxネタですが、ネットで同じように困った方が見つからず、海外サイトでようやく回避策を見つけたので備忘として載せておきます。


というか、これだけ日本語のサイトが出てこないと、正直私が何か間違っているのか???と不安になります・・・。

もし誰か、何かお気づきあれば、教えて下さい・・・。


マスターページにToolkitScriptManagerを配置したコンテンツページ上でModalPopupExtenderを配置したサイトです。

ModalPopupExtenderのプロパティは以下のように設定しています。

asp:ModalPopupExtender

プロパティ
ID MordalCalendar
Drag True
Enabled True
TargetControlID btnCalendar
PopupControlID pnlCalendar1
PopupDragHandleControlID pnlCalendar2
BehaviorID MordalCalendar
パネルは単純に重ねて、ボタンクリックでカレンダーが表示されるようなHTMLです。

<asp:Panel ID="pnlCalendar1" runat="server">
    <asp:Panel ID="pnlCalendar2" runat="server">
        <iframe id="iCalendar" runat="server" class="iCalendar"></iframe>
    </asp:Panel>
</asp:Panel>



で、実行すると、以下のエラーが表示されて正しく画面が表示されません(ajaxがおかしくなる)。

AjaxControlToolkit.ModalPopupExtender missing required PopupControlID property value
sys.extended.ui.modalpopupbehavior undefined

つまり、Sys.Extended.UI.ModalPopupBehaviorがnullになっているわけです。
なぜそのような状況になるのかは今をもって不明ですが、以下で回避することができます。

ToolkitScriptManagerコントロールのCombineScriptsプロパティを「True」から「False」へ変更する

CombineScriptsプロパティはAjaxに関わる重複するJavaScriptファイルをダウンロードしない機能ですが、それが有効になっているとJavaScriptをまとめることが出来るので、通信の効率化になるようです。ただ、作成しているWEBアプリケーションではほとんど影響ないようでしたので、今回はこれを無効化することで回避しました。

Facadeパターン

Facadeパターンは現在でもしばしば使われるくらい、結構メジャーなパターンです。

Facadeは「玄関」や「窓口」と言われ、その名の通りプログラムの各所から利用される一つのクラスを仲介するパターンです。利用されるクラスが将来変更される可能性がある場合や、メソッドの順番などに制約があり、各所に面倒な「縛り」があった状態で乱用されることを防ぐことができます。

例えば、OtherLibraryという将来変更の可能性があるクラスがあり、メソッドの使用順番に制約があるとします。そのままクラスを利用されると後からの修正が面倒なので、Facadeクラスを用意します。

//変更される可能性のあるクラス
public class OtherLibrary {
    public void Do1() { }
    public void Do2() { }
    public void Do3() { }
}

//Facadeクラス
public class Facade {
    public void DoLibrary() {
        OtherLibrary olib = new OtherLibrary();
        olib.Do1();
        olib.Do2();
        olib.Do3();
    }
}
'変更される可能性のあるクラス
Public Class OtherLibrary
    Public Sub Do1()
    End Sub
    Public Sub Do2()
    End Sub
    Public Sub Do3()
    End Sub
End Class

'Facadeクラス
Public Class Facade
    Public Sub DoLibrary()
        Dim olib As New OtherLibrary
        olib.Do1()
        olib.Do2()
        olib.Do3()
    End Sub
End Class

あとは使用する側は、Facadeクラスを介してOtherLibraryクラスを利用します。OtherLibraryクラスに変更が生じても修正はFacadeクラスのみとなる為、全体の修正量を大幅に抑えることとなります。

Facade doLib = new Facade();
doLib.DoLibrary();
Dim doLib As New Facade
doLib.DoLibrary()