2

私が現在取り組んでいるソフトウェア製品には、いくつかの 3D ビュー コントロールがあります。これらの 3D ビューの上にオーバーレイ情報が必要であることがわかりました。要点ではないため、背景の詳細​​についてはあまり触れませんが、直面している制約は次のとおりです。

  • 2 つの異なる 3D ビュー コントロールを使用する必要があります
  • それらのソースコードはありません
  • これらは Windows フォーム コントロールに埋め込まれており、これらのコントロールに関する独自の GUI はすべて Windows フォームにあります。
  • .NET Framework 3.5SP1 と Windows 7 を使用しています

これらの 3D ビューの上にさまざまなオーバーレイ情報とコントロールを表示できるようにしたいと考えています。これは、通常、必要な情報とコントロールを備えた GUI を表示せずに、大画面で全画面の 3D ビューを表示して製品のデモを行うためです。

以前は、1 種類の 3D ビューしか使用していませんでしたが、リフレクションを含むさまざまなハックを介して、DirectX で記述された独自のオーバーレイ ウィンドウ システムをフックすることができました (WorldWind .NET オーバーレイ ウィジェットに基づいており、3D ビューは実際に WorldWind に基づいていました)。時間)。3D View 製品の次のバージョンでは、レンダリング コア コードに大きな変更が加えられ、もちろんこれらのハックは互換性がなくなりました (ええ、私はそれが来ることを知っていました :-))。さらに、他の製品でのニーズが異なるため、別の種類の 3D ビュー、クローズド ソースも使用しています。

レンダリング ループにアクセスできないため、CEGUI などの 3D エンジン用に作成されたウィンドウ システムをフックできないため、それらのソース コードがないことを強調しておきます (自分で検索してください。まだ多くのハイパーリンクを投稿することは許可されていません、申し訳ありません)。

その結果、次のような考えが浮かびました: 3D ビューは winforms コントロールに埋め込まれているので、オーバーレイ コントロールを単純な winforms でコーディングして、3D ビューの上に重ねてみませんか? このソリューションの利点は非常に大きいです:

  • これは両方の 3D ビューと互換性があり、必要に応じてエンジン全体でオーバーレイを再利用できます。
  • 残りの GUI 用に既に開発したカスタム コントロールまたはフォームを再利用できます。実際、これはかなり大きなプロジェクトであり、そのようなコントロールのかなりのライブラリを用意し始めています。

唯一の小さな (!) 問題は、DirectX の以前のシステムで行ったように、オーバーレイの透過性を管理できるようにしたいということです。ビューが乱雑になるため、完全に不透明なオーバーレイを使用する余裕はありません。かろうじて見えるオーバーレイのようなものを想像してみてください。たとえば、マウスがその上に置かれると、より不透明になります。

Windows は、他のウィンドウまたはコントロール内に子ウィンドウを持つ可能性を提供します (Win32 API はウィンドウとコントロールの間に実際の違いはありません。私が理解しているように、これはほとんど MFC/WinForms の抽象化です)。レベル ウィンドウの場合、これらの透過性を調整することはできないため、これは使用できるものではありません。ここで見ましたが、これは Windows 8 でも可能ですが、7 を実行しているかなりの数のマシンにソフトウェアが展開されているため、すぐに Windows 8 に切り替えることはできません。

そこで、このような問題を回避するにはどうすればよいかについて、激しいグーグルセッションを開始しました。トップ レベル ウィンドウを 3D ビュー コントロールに「奴隷化」する必要があるようです。私はすでにwinformsで直接そのようなことを試し、コントロールによってフォームを所有し(親ではなく、明確な区別があり、以前にリンクされたMSページでそれについて読んでください)、画面上のその動きを「追跡」しました。予想通り、うまくいきましたが、問題を克服するのは困難です。最も重要なのはクリッピングの問題です。所有者コントロールの親フォームのサイズが変更された場合でも、オーバーレイ フォームは完全に表示されます。私のサンプルでは、​​メニュー付きのシンプルなフォームと、カレンダーを含む黒いパネルがあります (子コントロールと所有コントロール間のクリッピングの違いを示すため)。私は「奴隷にされた」黒いパネルにプロパティ グリッドを含む縁なしフォーム。フォームの動きにうまく追従しますが、メインフォームを縮小すると、次のようになります。

クリッピングの問題のスクリーンショット

カレンダーが正しく切り取られていること (子ウィンドウ) と、オーバーレイが正しく切り取られていないこと (所有ウィンドウ) に注意してください。メインフォームを最小化/復元するときにも奇妙な効果が得られます。確かに、最小化するとオーバーレイが消えますが、復元すると、メインフォームの復元アニメーションが発生している間に「生成」されます。しかし、これはそれほど問題ではなく、適切なイベントを処理することで回避できると思います (しかし、どのイベントですか?)。

私が理解したことから、win32 API 呼び出しとフックを使用して、少なくともクリッピングの一部を自分で処理する必要があります。私はすでに自分自身を文書化し始めていますが、それはかなり複雑なものです. Win32 API は非常に混乱しており、私は元 Unix 開発者であり、優れた .NET フレームワークを介して Windows プログラミングを紹介されました。Win32 の実際の経験がないため、どこから始めればよいか、どのようにすればよいかわかりません。このジャングルで自分の道を作るために...

したがって、winapi の第一人者が通りかかった場合、または上記の制約を考慮して私の目標を達成するための他のアイデアを誰かが持っている場合は、それについて読んで喜んでいます :-)

前もって感謝し、質問をするためだけに購読することで、そのようなスタックオーバーフローの「リーチャー」であることをお詫びしますが、私はワークステーションに直接インターネットにアクセスすることはできません(ええ、実際には、これ)、この素晴らしいコミュニティに参加することは、私にとってそれほど簡単ではありません。

最後に、これが私のサンプルコードです(質問すればデザイナーコードを入手できます):

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

    Point _myLoc;

    private void formToolStripMenuItem_Click(object sender, EventArgs e)
    {
        var ctrl = new PropertyGrid();
        var obj = this.panel1;
        ctrl.SelectedObject = obj;
        var form = new Form();
        ctrl.Dock = DockStyle.Fill;
        form.Controls.Add(ctrl);
        form.Opacity = 0.7;
        var rect = obj.RectangleToScreen(obj.DisplayRectangle);
        form.StartPosition = FormStartPosition.Manual;
        form.Location = new Point(rect.Left + 10, rect.Top + 10);

        var parentForm = this;
        _myLoc = parentForm.Location;
        form.FormBorderStyle = FormBorderStyle.None;

        parentForm.LocationChanged += (s, ee) => {
            int deltaX = parentForm.Location.X - _myLoc.X;
            int deltaY = parentForm.Location.Y - _myLoc.Y;
            var loc = form.Location;
            form.Location = new Point(loc.X + deltaX, loc.Y + deltaY);
            _myLoc = parentForm.Location;
        };
        form.Show(this.panel1);
    }
}
4

1 に答える 1

1

クリッピングは、プロパティを使用して簡単に実装できRegionます。各ウィンドウには、Regionウィンドウ レンダリングの制約を定義する関連オブジェクトを含めることができます。

static void ManualClipping(Control clipRegionSource, Form formToClip)
{
    var rect = clipRegionSource.DisplayRectangle;
    rect = clipRegionSource.RectangleToScreen(rect);
    rect = formToClip.RectangleToClient(rect);
    rect = Rectangle.Intersect(rect, formToClip.ClientRectangle);
    if(rect == formToClip.ClientRectangle)
    {
        formToClip.Region = null;
    }
    else
    {
        formToClip.Region = new Region(rect);
    }
}

利用方法:

/* ... */
parentForm.SizeChanged += (s, ee) => ManualClipping(panel1, form);
form.Show(this.panel1);
于 2013-01-03T13:20:13.390 に答える