Javascript External Library

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

2023-03-26

Microsoft.Web.WebView2.NavigateToString の Referer

結論から言うと「about:blank」で Microsoft.Web.WebView2.Source に収納されている。

この情報がなぜ必要になったかというと Microsoft.Web.WebView2 で NavigateToString を使うときに制限としてつけるためです。
Google Maps JavaScript API はAPIキーによる制限とキー単位での課金形態が取られています。キーが中露韓に不正利用される可能性があるのでキー以外にも制限を掛ける必要があります。
Googleが提示する制限は「Webサイト用のReferer制限」と「接続元IP制限」と「Androidアプリのキーによる制限」と「iOSアプリのキーによる制限」となっています。では、「Windowsアプリ」はどうするかというと「Webサイト用のReferer制限」を利用します。

何も指定しない場合で NavigateToString メソッドで NavigateToString を利用した場合は Source プロパティの初期状態である「about:blank」が送信されます。
ただ、「about:blank」では正直なところセキュリティは弱そうなので、他に使えるのは https://developers.google.com/maps/documentation/javascript/get-api-key にある about://, app://, applewebdata://, asset://, chrome://, content://, file://, ftp://, ionic://, local://, ms-appx://, ms-appx-web://, ms-local-stream://, prism://, qrc://, res://, saphtmlp:// を使いたいですね。

2023-03-04

Microsoft.Web.WebView2 で動的文字列を表示させる

public void NavigateToString(string html)
{
    if (WebViewer.CoreWebView2 == null)
    {
        WebViewer.CoreWebView2InitializationCompleted += (sender, _) =>
        {
            if (sender == null) return;
            ((WebView2)sender).NavigateToString(html);
        };
        WebViewer.EnsureCoreWebView2Async();
    }
    else
    {
        WebViewer.NavigateToString(html);
    }
}
Chrome使えばいいのにわざわざアプリ内でChromium使う理由・・・
それはアプリ内のリソースをHTML表示させるためですよね。
Microsoft.Web.WebView2 で文字列を表示させるには NavigateToString(String) メソッドを使えばいいです。ただ、WebView2 自体が Initialize されていても内部の CoreWebView2 が起動しているとは限らない。これが詰まるポイントでした。
WPF の WebView2 には Initialized プロパティがあるのですが、これはあくまでもWPFの初期化状態を示すだけなので CoreWebView2 の存在性を確認します。起動してない場合は EnsureCoreWebView2Async メソッドを呼び明示的に起動させます。EnsureCoreWebView2Async はやや高コストの処理で WebView2 が表示状態でないと処理が完了しないようなので Wait しないでスレッドを進行させます。代わりに CoreWebView2InitializationCompleted イベントを拾って改めて NavigateToString を試みるようにします。

Microsoft は Microsoft.UI.Xaml.Controls.WebView2 になってた。いい加減MAUIしろとMS様がおっしゃっておられる・・・

2022-11-23

同じAssembly内のUserControlを参照するとMC3074エラーが生じる

WPFのXAMLはUIデザインを組むうえで非常に便利だが、複雑なデザインになると階層が深くなって可読性が落ちるのが難点です。そのため、特に使い回さないブロックも User Control として同一アセンブリ内に切り出すのだが、マークアップ時のインテリジェンスは有効なのだがコンパイル時にMC3047エラーが生じるようになった。

調べてみるとGitのIssueに同じ問題を提示している人がいたので覚え書き代わりにメモを残しておくことにする。
要約すると.NET 5から.NET 6になったときに仕様が変わったことが原因らしい。プロジェクトファイルで明示的に指示すればコンパイルできるようになる。
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <IncludePackageReferencesDuringMarkupCompilation>false</IncludePackageReferencesDuringMarkupCompilation>
  </PropertyGroup>
</Project>
解決はするが、この設定はGUIでは表示されない。バグっぽい気もするし、User Control は別プロジェクトにすべきという方針なのかもしれない。

2022-10-05

Microsoft.Web.WebView2 で Web スクレイピング

はじめに

Microsoft Internet Explorer もなくなり、Microsoft Edge も Chromium になって世の中が Chromium 一色となりました。Wpf を使ってWeb Browser を作るときは System.Windows.Controls.WebBrowser でしたが、これは内部がIE7というとんでもない骨董品でした。Wpf で Chromium は扱えなくもないですが、微妙に扱いにくいのでどうしたものかと思っていたら Microsoft が Microsoft.Web.WebView2 を出してくれたのでこれをちょっと触ってみることにします。

XAMLに埋め込む

XAMLに埋め込むのはこんな感じです。
<UserControl x:Class="UserControls.WebBrowser"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
  <Grid>
    <wv2:WebView2
        x:Name="WebViewer" />
  </Grid>
</UserControl>
Microsoft.Web.WebView2 は Nuget から拾ってきます。


URLで画面遷移する

アドレスバーの入力など任意のWebページに遷移するのは Source Property を指定します。
using Microsoft.Web.WebView2.Wpf;
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
namespace UserControls.WebBrowser;
public partial class WebBrowser : UserControl
{
    public WebBrowser()
    {
        InitializeComponent();
        
        WebViewer..Source = new Uri(@"https://www.lutheta.jp/");
    }
}

Responseを拾う

Response を拾うには WebResourceResponseReceived を拾えばいいです。ただ、これは Microsoft.Web.WebView2.Wpf.WebView2 の CoreWebView2 Property の Microsoft.Web.WebView2.Core.CoreWebView2 にあるので注意が必要です。
using Microsoft.Web.WebView2.Wpf;
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
namespace UserControls.WebBrowser;
public partial class WebBrowser : UserControl
{
    public WebBrowser()
    {
        InitializeComponent();

        WebViewer.CoreWebView2InitializationCompleted += (sender, e) =>
        {
            if (sender != null)
                ((WebView2)sender).CoreWebView2.WebResourceResponseReceived += async (sender, e) =>
                {
                }
        };
    }
}
CoreWebView2 は Component の Initialize 時には Null なので CoreWebView2InitializationCompleted Event で WebResourceResponseReceived Event をセットするようにします。

これを利用して

とりあえず MarineTraffic から航跡を抽出するアプリを作ってみました。

MarinTraffic

なかなか応用幅が広がりそうな感じでです。

2021-03-21

【XAML】TextboxでEnter入力をBindする

<TextBox Text="{Binding InputText, UpdateSourceTrigger=PropertyChanged}">
  <TextBox.InputBindings>
    <KeyBinding Gesture="Enter" Command="{Binding EnterText}" />
  </TextBox.InputBindings>
</TextBox>

TextBox.InputBindings で Input 操作を取得する
KeyBinding で Enter を Bind して ICommand を実行する

注意点は
UpdateSourceTrigger = PropertyChanged
で、Text プロパティのバインドを PropertyChanged としておくこと、既定は LostFocus なので Enter 操作時に Text に Bind した要素が更新されない。




2021-03-13

WPF で透過アプリケーションを作る

Windows 7以降、アプリケーションごと透過できるようになりました。普段は特に必要ない機能ではありますが、常駐の情報表示アプリケーションなどでは便利な場面もあります。

xmalだけでやろうとするとだいたいこんな感じ
<Window x:Class="Lutheta.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
        xmlns:prism="http://prismlibrary.com/"
        prism:ViewModelLocator.AutoWireViewModel="True"
        ShowInTaskbar="False"
        WindowStyle="None"
        AllowsTransparency="True"
        Left="200" Top="200" Height="480" Width="640">
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="MouseLeftButtonDown">
      <i:CallMethodAction TargetObject="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" MethodName="DragMove" />
    </i:EventTrigger>
  </i:Interaction.Triggers>
  <Window.Style>
    <Style TargetType="Window">
      <Setter Property="ResizeMode" Value="NoResize" />
      <Style.Triggers>
        <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsActive}" Value="True">
          <Setter Property="ResizeMode" Value="CanResizeWithGrip" />
        </DataTrigger>
      </Style.Triggers>
    </Style>
  </Window.Style>
  <Grip />
</Window>

要点は次のような感じです。
  • ShowInTaskbar="False"
    タスクバーに表示しない
  • WindowStyle="None"
    ウィンドウ枠を表示しない。最小化なども表示されなくなります。
  • AllowsTransparency="True"
    透過を許可する
  • Window.MouseLeftButtonDown event で Window.DragMove method する
    マウス左ボタン押下時にウィンドウを移動できるようにする
     xmlns:i="http://schemas.microsoft.com/xaml/behaviors" が必要
  • Window.IsActive の値によって Window.ResizeMode を変更する
    アクティブ状態になったときにサイズ変更用のGripマーカーを表示する
    非アクティブ状態のときは表示しないようにするほうが見栄えがいい
以上です。

MVVM だと xaml.cs にはなるべく手を入れたくないので、 xaml で完結させました。

2021-03-07

.NET 5 への移行

.NET 5 SDK をインストールする

ダウンロードしてインストールする。
まあ、これ忘れることはないと思う。

.NET Core 3 から .NET 5 へ

ターゲットフレームワークを変更する。

ちなみに、名前順なのか「.NET Core 1.0」よりも上にある。

プロジェクトファイルは次のような感じになる。
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net5.0-windows</TargetFramework>
  </PropertyGroup>
</Project>

TargetFrameworkは自動で変換してくれるが、ProjectタグのSdk属性は手動で変えないといけない。

コンパイル場所の既定は「bin/Debug/net5.0-windows」となる。もう深すぎてめんどくさい。いい加減発行手順の手抜きはするなということかなあ。

Xamarin.Forms 5.0 にする

これは必須ではないんだけど、Xamarin.Forms 5.0も使えるようになった。
これも.NET 5と同様に使っていきたい。
注意が必要なのはAndroidXしか使えないこと。Android.Supportからの移行が必要になります。
また、最小APIが29となる。OSだとAndroid 10となるので、旧いスマホは要注意となる。というか、旧いスマホのOSのバージョンアップができないのは端末を買ってるのにおかしいと思う。所有権じゃなくて使用権でも売ってるつもりかよ。

移行方法の詳細はMicrosoftがDocsで発表している。

Azureをつかう

Azure Web Apps は現段階で .NET 5 はEarly Accessとなっている。
利用に大きなトラブルはないようだが、利用は自己責任の範囲が大きくなるので責任あるアプリケーションのバージョンアップは避けておいたほうがいいと思う。

Azure Functions は.NET 5は未対応。
全く動かない。少しも動かない。エミュレータすらも動かない。
しかも、コンパイルできるのに動かないと来たもんだ。
githubのissueでも論争が繰り広げられているのですが、「対応未定」とか「対応中」とかそういう次元ではなく「対応しない」予定らしい。
Azure Functions はバージョンアップしてはならない。私はやってしまったので後で戻さないと・・・

まとめ

特に大きな作業の必要なくバージョンアップ自体はできると言えます。
ただし、Azureを利用するものはしばらく様子見をすることを推奨します。

2021-01-30

NETSDK1137 の解決

.NET Core のデスクトップアプリを .NET 5 に変更すると NETSDK1137 の警告が出る。
使用するSDKが .NET SDK Windows Desktop (Microsoft.NET.Sdk.WindowsDesktop) から .NET SDK (Microsoft.NET.Sdk) に変更されたことが原因

プロジェクトファイルのSDKを手動で変更すればいい。
TargetFramework は net5.0 でなく、 net5.0-windows な点も注意が必要だけど、こちらはプロジェクトプロパティのGUIで設定できる。

参考:Micosoft Docs

やっと.NET 5に触ることができた・・・

2020-07-14

.NET Core で Shift_JIS を使う

次の2つの作業が必要
  • System.Text.Encoding.CodePages を Nuget からインストールする
  • Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); を実行する
Encoding.RegisterProvider メソッドは GetEncording など利用する前ならいつでも宣言できる。また、同じものを何度実行しても問題ない。

Shift_JISは日本語を取り扱う上で古くからある規格でかつては一般的だったのですが、今はUTF-8に置き換わって・・・欲しかったなあ。
徐々に置き換わって入るのですが、まだまだShift_JISを読み込むことは多いので仕方ないです。国内で完結するならまだしもインターネットは国際的なものですから国際的な規格で統一したいですね。

余談ですが、支那はUTF-8を時刻用に魔改造したものを利用している。このせいで稀に問題が生じる。この問題にハマると気がつくのに時間がかかるので覚えていて損はない。この魔改造した規格を国際規格でゴリ押ししてくるんだから支那の中華思想はやばい。

※中華思想:「俺たちは世界の中心で他は蛮族だから侵略しようが虐殺しようが問題ないぜ」という思想

2019-11-25

Windows 10 の標準ブラウザの取得 (c#)

.NET Standard 2.1が使えるようになったので、ライブラリを更新しました。
そうすると、System.Diagnostics.Process クラス の Start メソッドの挙動が変わってしましました。
具体的には string 型でURLを渡すと標準ブラウザが起動してくれいたのが、Win32Exception が発生するようになってしまってました。
標準ブラウザが起動しないとOAuth2認証が困るのですが・・・

パラメータ付きで起動することはできるので、実行ファイルのパスさえわかればなんとかなりそうです。ちなみに、explorer.exe にURLをパラメータとして私もOKですが、OAuth2認証のように長いクエリストリング付きのものはうまく行かない。

Windows10 の標準ブラウザはレジストリの「HKEY_CURRENT_USER\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice」にProgIdが保存されています。
更に、ProgIdは「HKEY_CLASSES_ROOT\{ProgId}\shell\open\command」の規定に実行ファイルのパスが記載されています。

以上を踏まえまして
string progId = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(
 @"Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice",
 false)
 .GetValue("ProgId")
 .ToString();
string command = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(
 string.Format(@"{0}\shell\open\command", progId),
 false)
 .GetValue(null)
 .ToString()
 .Replace("%1", "{0}");
Process.Start(string.Format(command, "https://www.lutheta.jp/"));
こんな感じでしょうか。面倒になったなあ。