2009-09-21T06:58:51の更新内容

programming/tips/setdeskwallpaper_multidisplay/index.wiki.txt

current previous
1,151 0,0
+
${smdncms:keywords,壁紙,マルチディスプレイ,C#}
+
${smdncms:tags,lang/c#,api/.net,api/win32}
+
*壁紙を変更する
+
#googleadunit
+

          
+
[[programming/tips/setdeskwallpaper]]の方法ではマルチディスプレイ環境ですべてのディスプレイに対して個別に壁紙を設定することは出来ない。 また、個別に壁紙を設定するためのレジストリエントリやAPIもないようなので、それらを使わずに壁紙を設定する方法を考える。
+

          
+
やり方としては、
+
+すべてのディスプレイに対応した大きさのビットマップを作成し
+
+そこにディスプレイ毎の壁紙を描画し
+
+その画像を一枚の壁紙として設定する
+

          
+
ことでディスプレイ毎に壁紙を設定したように見せることができる。
+

          
+
例えば、サイズが800x600のディスプレイが2つ横に並んでいる環境の場合、サイズが1600x600のビットマップを用意し、(0,0)-(800,600)に1つ目のディスプレイ用の壁紙、(800,0)-(1600,600)に2つ目のディスプレイ用の壁紙を描画することで、2つのディスプレイそれぞれに壁紙を設定したように見せることができる。
+

          
+
このやり方で必要となるディスプレイ毎のサイズと位置は、Screen.AllScreensプロパティに含まれる各インスタンスのScreen.Boundsプロパティを参照することで得られるが、プライマリディスプレイとその他のディスプレイの位置関係によっては、Screen.BoundsプロパティのXおよびYの値が負になる場合がある点に注意する必要がある(プライマリディスプレイの原点は常に(0, 0)となる模様)。 また、HKEY_CURRENT_USER\Control Panel\DesktopのTileWallpaperの値は1、Wallpaperstyleの値は0(つまり「並べて表示」)に設定しないと意図したとおりに表示されない。
+

          
+
**サンプル
+
このコードでは、実行環境に2台のディスプレイが接続されていることを前提としている。 また、画像のアスペクト比は考慮せず、常に全体に拡大して表示する。
+
このコードを使ったサンプルプログラムは[[works/tools/junk/MultiDisplayWallpaperChanger]]で公開している。
+

          
+
#code(cs){{
+
using System;
+
using System.ComponentModel;
+
using System.Drawing;
+
using System.Drawing.Imaging;
+
using System.Drawing.Drawing2D;
+
using System.Runtime.InteropServices;
+

          
+
private void DrawWallpaper()
+
{
+
  // ディスプレイ毎の壁紙を描画したビットマップを保存するためのファイル名
+
  const string combinedWallpaperFile = @"wallpaper.bmp";
+

          
+
  // ディスプレイと設定する壁紙のファイル名
+
  var wallpaperMap = new[] {
+
    new {Screen = Screen.AllScreens[0], File = @"C:\Users\Public\Pictures\Sample Pictures\Dock.jpg"},
+
    new {Screen = Screen.AllScreens[1], File = @"C:\Users\Public\Pictures\Sample Pictures\Waterfall.jpg"},
+
  };
+

          
+
  // すべてのディスプレイを含む矩形を計算する
+
  var allScreenBounds = new Rectangle(0, 0, 0, 0);
+

          
+
  foreach {
+
    allScreenBounds = Rectangle.Union(allScreenBounds, screen.Bounds);
+
  }
+

          
+
  Console.WriteLine("AllScreenBounds: {0}", allScreenBounds);
+

          
+
  // 計算したサイズでビットマップを作成する
+
  using {
+
    using {
+
      g.Clear(Color.Black);
+

          
+
      // allScreenBoundsの原点が(0, 0)以外になった場合でもビットマップの左上端から描画されるように
+
      // Graphicsの座標系の原点を変換する
+
      g.TranslateTransform((float)-allScreenBounds.X,;
+

          
+
      // ディスプレイ毎の壁紙を読み込み、作成したビットマップに描画する
+
      foreach {
+
        using {
+
          g.DrawImage(bitmap, pair.Screen.Bounds);
+

          
+
          Console.WriteLine("{0}: {1}", pair.Screen.DeviceName, pair.Screen.Bounds);
+
        }
+
      }
+
    }
+

          
+
    // 描画したビットマップを保存する
+
    allScreenBitmap.Save(combinedWallpaperFile, ImageFormat.Bmp);
+
  }
+

          
+
  // 保存したビットマップを壁紙として設定する
+
  // WallpaperOriginXおよびWallpaperOriginYが(0, 0)のままだと表示される位置が合わなくなるため
+
  // allScreenBoundsの原点を表示する位置にする
+
  SetWallpaper(combinedWallpaperFile, allScreenBounds.X, allScreenBounds.Y);
+
}
+

          
+
[DllImport("user32.dll", SetLastError = true)] public static extern bool SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIini);
+

          
+
private void SetWallpaper(string file, int x, int y)
+
{
+
  const int SPI_SETDESKWALLPAPER  = 0x0014;
+
  const int SPIF_UPDATEINIFILE    = 0x0001;
+
  const int SPIF_SENDWININICHANGE = 0x0002;
+

          
+
  using {
+
    if
+
      throw new Win32Exception(Marshal.GetLastWin32Error());
+

          
+
    regkeyDesktop.SetValue("TileWallpaper", "1");
+
    regkeyDesktop.SetValue("Wallpaperstyle", "0");
+
    regkeyDesktop.SetValue("WallpaperOriginX", x.ToString());
+
    regkeyDesktop.SetValue("WallpaperOriginY", y.ToString());
+
    regkeyDesktop.SetValue("Wallpaper", file);
+
  }
+

          
+
  var flags =
+
    ? SPIF_SENDWININICHANGE
+
    : SPIF_SENDWININICHANGE | SPIF_UPDATEINIFILE;
+

          
+
  if
+
    throw new Win32Exception(Marshal.GetLastWin32Error());
+
}
+
}}
+

          
+
SetWallpaperの詳細は[[programming/tips/setdeskwallpaper]]を参考のこと。
+

          
+
以下いくつかのディスプレイ配置設定で実行した際の結果とスクリーンショット。
+

          
+
***パターン1
+
プライマリディスプレイを左、2台めのディスプレイを右に配置した場合。
+
#prompt{{
+
AllScreenBounds: {X=0,Y=0,Width=2720,Height=1024}
+
\\.\DISPLAY1: {X=0,Y=0,Width=1280,Height=1024}
+
\\.\DISPLAY2: {X=1280,Y=0,Width=1440,Height=900}
+
}}
+

          
+
#ref(./sample1.jpg,プライマリディスプレイを左、2台めのディスプレイを右に配置した場合のスクリーンショット)
+

          
+
***パターン2
+
プライマリディスプレイを右、2台めのディスプレイを左に配置した場合。
+
#prompt{{
+
AllScreenBounds: {X=-1440,Y=0,Width=2720,Height=1024}
+
\\.\DISPLAY1: {X=0,Y=0,Width=1280,Height=1024}
+
\\.\DISPLAY2: {X=-1440,Y=120,Width=1440,Height=900}
+
}}
+

          
+
#ref(./sample2.jpg,プライマリディスプレイを右、2台めのディスプレイを左に配置した場合のスクリーンショット)
+

          
+
***パターン3
+
プライマリディスプレイを左上、2台めのディスプレイを右下に配置した場合。
+
#prompt{{
+
AllScreenBounds: {X=0,Y=0,Width=1952,Height=1924}
+
\\.\DISPLAY1: {X=0,Y=0,Width=1280,Height=1024}
+
\\.\DISPLAY2: {X=512,Y=1024,Width=1440,Height=900}
+
}}
+

          
+
#ref(./sample3.jpg,プライマリディスプレイを左上、2台めのディスプレイを右下に配置した場合のスクリーンショット)
+

          
+
***パターン4
+
プライマリディスプレイを左下、2台めのディスプレイを右上に配置した場合。
+
#prompt{{
+
AllScreenBounds: {X=0,Y=-900,Width=1952,Height=1924}
+
\\.\DISPLAY1: {X=0,Y=0,Width=1280,Height=1024}
+
\\.\DISPLAY2: {X=512,Y=-900,Width=1440,Height=900}
+
}}
+

          
+
#ref(./sample4.jpg,プライマリディスプレイを左下、2台めのディスプレイを右上に配置した場合のスクリーンショット)
+

          

works/tools/junk/MultiDisplayWallpaperChanger/index.wiki.txt

current previous
1,22 0,0
+
*MultiDisplayWallpaperChanger
+
**概要
+
マルチディスプレイに対応した壁紙チェンジャです。 [[programming/tips/setdeskwallpaper_multidisplay]]を使ったサンプルプログラムです。
+

          
+
**機能
+
-ディスプレイ毎に壁紙を設定する機能
+
--並べて配置、全体に拡大して配置の二種類の描画が可能
+
-起動中にディスプレイの解像度・配置が変わった場合に壁紙を再描画する機能
+

          
+
**ダウンロード
+
#googleadunit
+
-${smdncms:distfilelink,MultiDisplayWallpaperChanger.tar.bz2}
+

          
+
Windows Vistaにて意図したとおりに動作することを確認済みです。 それ以外のマルチディスプレイに対応したOSでの動作確認はしていません。
+

          
+
**スクリーンショット
+
#ref(multidisplaywallpaperchanger.jpg)
+
#ref(multidisplaywallpaperchanger_desktop.jpg)
+

          
+
**使い方
+
ウィンドウ内に表示される各ディスプレイをクリックして壁紙として設定するファイルを選んでください。
+