WPF アプリケーションで Windows 7 の自動ウィンドウ ドッキング機能を無効にすることはできますか?
11 に答える
ResizeMode = CanResizeWithGrip
最近、ウィンドウ装飾のない (タイトル バーとボタンのない)カスタムのサイズ変更可能な WPF ウィンドウに対してこれを行う必要がありました。以前DragMove()
はウィンドウを移動していましたが、AeroSnap で最大化すると、ウィンドウが移動できなくなり、所定の位置にロックされます。
Barn Monkey のソリューションを試してみましたが、部分的には機能しましたが、それでも AeroSnap グラフィックが表示され、アプリのサイズがフルスクリーン サイズに変更されました。以下で変更したところ、期待どおりに動作するようになりました。サイズ変更は可能ですが、AeroSnap はまったくありません。
void Window1_MouseDown(object sender, MouseButtonEventArgs e)
{
if( e.LeftButton == MouseButtonState.Pressed )
{
// this prevents win7 aerosnap
if( this.ResizeMode != System.Windows.ResizeMode.NoResize )
{
this.ResizeMode = System.Windows.ResizeMode.NoResize;
this.UpdateLayout();
}
DragMove();
}
}
void Window1_MouseUp( object sender, MouseButtonEventArgs e )
{
if( this.ResizeMode == System.Windows.ResizeMode.NoResize )
{
// restore resize grips
this.ResizeMode = System.Windows.ResizeMode.CanResizeWithGrip;
this.UpdateLayout();
}
}
編集:
これを書いてからしばらく経ちましたが、人々はまだこれを見ているので、私が今使っているもので更新します. エッジのスナップを防ぎ、ウィンドウを移動するために基本的に同じ方法を引き続き使用しますが、またはBehavior<>
にアタッチできるカスタムクラスにパックしました。これにより、MVVM での使用が非常に簡単になります (私は Caliburn Micro を使用しています)。Window
UserControl
動作クラスは次のとおりです。
/// <summary>
/// behavior that makes a window/dialog draggable by clicking anywhere
/// on it that is not a control (ie, button)
/// </summary>
public class DragMoveBehavior<T> : Behavior<T> where T : FrameworkElement
{
protected override void OnAttached()
{
AssociatedObject.MouseLeftButtonDown += MouseDown;
base.OnAttached();
}
protected override void OnDetaching()
{
AssociatedObject.MouseLeftButtonDown -= MouseDown;
base.OnDetaching();
}
void MouseDown( object sender, EventArgs ea ) => Window.GetWindow( sender as T )?.DragMove();
}
public class WinDragMoveBehavior : DragMoveBehavior<Window> { }
public class UCDragMoveBehavior : DragMoveBehavior<UserControl> { }
/// <summary>
/// behavior that makes a window/dialog not resizable while clicked. this prevents
/// the window from being snapped to the edge of the screen (AeroSnap). if DragMoveBehavior
/// is also used, this must be attached first.
/// </summary>
/// <typeparam name="T"></typeparam>
public class NoSnapBehavior<T> : Behavior<T> where T : FrameworkElement
{
ResizeMode lastMode = ResizeMode.NoResize;
protected override void OnAttached()
{
AssociatedObject.MouseLeftButtonDown += MouseDown;
AssociatedObject.MouseLeftButtonUp += MouseUp;
base.OnAttached();
}
protected override void OnDetaching()
{
AssociatedObject.MouseLeftButtonDown -= MouseDown;
AssociatedObject.MouseLeftButtonUp -= MouseUp;
base.OnDetaching();
}
/// <summary>
/// make it so the window can be moved by dragging
/// </summary>
void MouseDown( object sender, EventArgs ea )
{
var win = Window.GetWindow( sender as T );
if( win != null && win.ResizeMode != ResizeMode.NoResize )
{
lastMode = win.ResizeMode;
win.ResizeMode = ResizeMode.NoResize;
win.UpdateLayout();
}
}
void MouseUp( object sender, EventArgs ea )
{
var win = Window.GetWindow( sender as T );
if( win != null && win.ResizeMode != lastMode )
{
win.ResizeMode = lastMode;
win.UpdateLayout();
}
}
}
public class WinNoSnapBehavior : NoSnapBehavior<Window> { }
public class UCNoSnapBehavior : NoSnapBehavior<UserControl> { }
次に、それらをダイアログ ボックスのビューに次のようにアタッチします。
<UserControl ...
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:util:="...">
<i:Interaction.Behaviors>
<util:UCNoSnapBehavior/>
<util:UCDragMoveBehavior/>
</i:Interaction.Behaviors>
...
</UserControl>
そして、それはうまくいきます!
Win7 の「Sticky Notes」を例に挙げると、標準のウィンドウ枠がないことに気付いたかもしれません。ResizeMode="NoResize"
それに基づいて、サイズ変更動作を手動で設定して処理する以外に、これを行う直接的な方法はないとしか言えません。以下は、私がすぐに作成した非常に基本的な非専門的なソリューションですが、必要に応じてさらに関数を追加できます:)
<Window
x:Class="WpfApplication1.Window1"
x:Name="window"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1"
Width="300"
Height="300"
ResizeMode="NoResize"
WindowStyle="None"
AllowsTransparency="True"
Background="Transparent"
WindowState="Maximized">
<Window.Resources>
<x:Array
x:Key="TextBlockList"
Type="{x:Type TextBlock}">
<TextBlock
Text="○ Resize Horizontally by dragging right grip" />
<TextBlock
Text="○ Resize Vertically by dragging bottom grip" />
<TextBlock
Text="○ Move Horizontally by dragging left grip" />
<TextBlock
Text="○ Move Verticallyby dragging top grip" />
</x:Array>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition
Height="Auto" />
<RowDefinition
Height="{Binding Height, Mode=OneWay, ElementName=window}" />
<RowDefinition
Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition
Width="Auto" />
<ColumnDefinition
Width="{Binding Width, Mode=OneWay, ElementName=window}" />
<ColumnDefinition
Width="Auto" />
</Grid.ColumnDefinitions>
<GridSplitter
Grid.Column="1"
Grid.Row="1"
HorizontalAlignment="Left"
MinWidth="5" />
<GridSplitter
Grid.Column="1"
Grid.Row="1"
HorizontalAlignment="Right"
MinWidth="5" />
<GridSplitter
Grid.Column="1"
Grid.Row="1"
VerticalAlignment="Top"
MinHeight="5"
ResizeDirection="Rows"
HorizontalAlignment="Stretch" />
<GridSplitter
Grid.Column="1"
Grid.Row="1"
VerticalAlignment="Bottom"
MinHeight="5"
ResizeDirection="Rows"
HorizontalAlignment="Stretch" />
<Border
Grid.Column="1"
Grid.Row="1"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Margin="5">
<Grid x:Name="root">
<ItemsControl
ItemsSource="{StaticResource TextBlockList}" />
</Grid>
</Border>
</Grid>
</Window>
親キャンバス内でサイズ変更および移動できるコントロール (基本的にはパネル) を作成することもできます。これで、このコントロールを透明な最大化ウィンドウに入力できます。これにより、コントロールが「ウィンドウ スナップ」に応答せず、ドッキングしないウィンドウであるという錯覚が得られます。
お役に立てれば。
よろしく、
ミヒル・ゴーカニ
私はしばらくアンソニーのソリューションを使用していましたが、ResizeMode を切り替えると、ウィンドウのサイズ変更境界が一時的に削除され、少し面倒です。ここに別の解決策があります。WS_OVERLAPPEDWINDOW フラグを設定して WS_THICKFRAME フラグを削除すると、ウィンドウの Aero スナップ機能が無効になりますが、サイズ変更の境界線は一時的に削除されません。スタイルをいじって、必要な正確なスタイルを取得できますが、重要なのは WS_THICKFRAME フラグを削除することです。
public enum WindowStyles: int
{
WS_BORDER = 0x00800000,
WS_CAPTION = 0x00C00000,
WS_CHILD = 0x40000000,
WS_CHILDWINDOW = 0x40000000,
WS_CLIPCHILDREN = 0x02000000,
WS_CLIPSIBLINGS = 0x04000000,
WS_DISABLED = 0x08000000,
WS_DLGFRAME = 0x00400000,
WS_GROUP = 0x00020000,
WS_HSCROLL = 0x00100000,
WS_ICONIC = 0x20000000,
WS_MAXIMIZE = 0x01000000,
WS_MAXIMIZEBOX = 0x00010000,
WS_MINIMIZE = 0x20000000,
WS_MINIMIZEBOX = 0x00020000,
WS_OVERLAPPED = 0x00000000,
WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
WS_POPUP = unchecked((int)0x80000000),
WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU,
WS_SIZEBOX = 0x00040000,
WS_SYSMENU = 0x00080000,
WS_TABSTOP = 0x00010000,
WS_THICKFRAME = 0x00040000,
WS_TILED = 0x00000000,
WS_TILEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
WS_VISIBLE = 0x10000000,
WS_VSCROLL = 0x00200000,
}
int newWinLongStyle = 0;
newWinLongStyle |= (int)WindowStyles.WS_OVERLAPPEDWINDOW;
newWinLongStyle ^= (int)WindowStyles.WS_THICKFRAME;
WindowInteropHelper helper = new WindowInteropHelper(this);
NativeMethods.SetWindowLong(helper.Handle, (int)WindowStyles.GWL_STYLE, newWinLongStyle);
これが私の解決策です。ResizeMode が ResizeMode.NoResize に設定されている場合、Windows はスナップしません。したがって、ドラッグ/移動の開始時と終了時を確実に判断することが重要です。
編集: alexandrud は、これが「ボーダーレス」ウィンドウ (WPF 用語では WindowStyle = None) でのみ機能することを正しく指摘しました。
この情報を私たちにもたらすために、多くのボサンが亡くなりました。
class NoSnapWindow : System.Windows.Window
{
public NoSnapWindow()
{
SourceInitialized += delegate {
var source = HwndSource.FromVisual(this) as HwndSource;
source.AddHook(SourceHook);
};
}
private IntPtr SourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
case 0x112: // WM_SYSCOMMAND
switch (wParam.ToIn32() & ~0x0F)
{
case 0xF010: // SC_MOVE
ResizeMode = ResizeMode.NoResize;
break;
}
break;
case 0x2A2: // WM_MOUSELEAVE
ResizeMode = ResizeMode.CanResize;
break;
}
return IntPtr.Zero;
}
}
WPF アプリケーションでのウィンドウ サイズの変更を防ぐために、Windows 7 の Aero スナップ/ドックを検出する必要がありました。検索中にこの投稿に出くわし、アンソニーからの回答が非常に役立つことがわかりました。
以下は私のために働いたものです。
private void DisplayWindow_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Released)
{
this.ResizeMode = System.Windows.ResizeMode.CanResizeWithGrip;
}
}
private void DisplayWindow_LocationChanged(object sender, EventArgs e)
{
this.ResizeMode = System.Windows.ResizeMode.NoResize;
}
ウィンドウの XAML にはResizeMode="CanResizeWithGrip"
設定がありました。
編集:
私の応答は、Windows 7 Aeroスナップを適切に処理しませんでした。bjo の応答は、問題をエレガントに解決してくれました。
それはあなたにとって完璧な解決策ではないかもしれませんが、私にとってはフォームをサイズ変更不可に設定することでうまくいきました。
DragMove は UI スレッドを一時停止します。そのコードも機能します。
void Window1_MouseDown(object sender, MouseButtonEventArgs e)
{
if( e.LeftButton == MouseButtonState.Pressed )
{
// this prevents win7 aerosnap
this.ResizeMode = System.Windows.ResizeMode.NoResize;
this.UpdateLayout();
DragMove();
// restore resize grips
this.ResizeMode = System.Windows.ResizeMode.CanResizeWithGrip;
this.UpdateLayout();
}
}
ここにはWindows7ボックスがないので、これをテストすることはできませんが、これを試してみます。
1-テストフォームを作成し、WndProcをオーバーライドします
。2-サイズ、位置、およびWindowStateの変更に関連する特定のメッセージをテストしてログに記録します。
3-ドッキング時にウィンドウに送信されるメッセージがSize/Position / WindowStateの組み合わせであるかどうか
、または別の新しいWindows 7メッセージがあるかどうかを判断します(5分間の検索では何も
わかりませんでした)。メッセージについては、発生している「固有の」ケースがあるかどうかを確認してください。
5-その固有のケースに対応するようにコードを変更します。
誰も思いつかないのなら、今週末、家でこれを回転させるかもしれません。
ボーダレスウィンドウでも機能する、私が見つけたかなり単純な解決策:最大化ボタンを非表示にするだけです(キャプションバーがないためにまだ表示されていない場合のイベント):
[DllImport("user32.dll")]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
private const int GWL_STYLE = -16;
private const int WS_MAXIMIZEBOX = 0x10000;
private void Window_OnSourceInitialized(object sender, EventArgs e)
{
var hwnd = new WindowInteropHelper((Window)sender).Handle;
var value = GetWindowLong(hwnd, GWL_STYLE);
SetWindowLong(hwnd, GWL_STYLE, (int)(value & ~WS_MAXIMIZEBOX));
}
コントロールパネルの[アクセスのしやすさ]で、[
タスクに集中しやすくする
とダニ
画面の端に移動したときにウィンドウが自動的に配置されないようにします