Laden...

WPF: Button statt Fenstertitel

Erstellt von cartilla vor 13 Jahren Letzter Beitrag vor 13 Jahren 13.041 Views
C
cartilla Themenstarter:in
12 Beiträge seit 2006
vor 13 Jahren
WPF: Button statt Fenstertitel

Hallo Leute!

Im neuen Firefox 4 wird ja statt des Fenstertitels ein Button angezeigt. Wie mache ich das denn mit WPF?

3.430 Beiträge seit 2007
vor 13 Jahren

Hallo cartilla,

in WPF kannst du dir einfach selbst ein Fenster designen.
Siehe dazu: http://www.kirupa.com/blend_wpf/custom_wpf_windows.htm
Oder hier gibt es schon was vorgefertigtes: http://wpfwindow.codeplex.com/

Gruss
Michael

U
1.578 Beiträge seit 2009
vor 13 Jahren

Hab ich in Dianthus auch.

Ich habe es genau so wie man es bei den wpfwindow sieht, aber absolut richtig ist das nicht, die Buttons sind nicht immer genau am rand und auch nicht die korrekten System Buttons.

Wie man es richtig macht sieht man hier:

http://code.msdn.microsoft.com/WPFShell

Mir persönlich war das bisher zu viel arbeit und noch zuviel Code den ich noch verstehen lernen müsste, darum habe ich mich damit noch nicht genauer beschäftigt.
http://www.my-libraries.de/dianthus/previews/

Eventuell auch lesenswert
http://blogs.msdn.com/b/wpfsdk/archive/2008/09/08/custom-window-chrome-in-wpf.aspx

C
cartilla Themenstarter:in
12 Beiträge seit 2006
vor 13 Jahren

Vielen Dank für die Antworten!

Ich habe mir mal die WPF Shell Integration Library runtergeladen, da ist ein Beispiel für genau das was ich suche dabei. Wenn allerdings nur ein bestimmter Teil (Thickness = 0, 40, 0, 0) des Fensters transparent sein soll, wird es schwarz 🙁 🤔

L
862 Beiträge seit 2006
vor 13 Jahren

Versuche mal AllowOpacity vom Window auf True zu setzen. Dieses Flag ist per Default auf false was dazu führt dass die Transparenzen schwarz werden.

Einige Fragen hätte ich zu den besagten Libraries:

  • Kann ich damit eine komplett eigene FensterLeiste schreiben oder nur Controls darauf platzieren? Ich habe in meinen Programm auch eine eigene Titelleiste die ich jedoch komplett selbst programmiert habe (also eigener Thumb zum Verschieben und Resizen & eigene Buttons für Minimieren/Maximieren/Schließen).

  • Falls ja: Unterstützen diese Fenster dann die neuen Win7-Platzerungsfeatures. Unter Win7 kann man ja ein Fenster maximieren indem man es an die obere Bildschirmgrenze schiebt und über 50% des Bildschirms verteilen indem man es nach ganz links oder rechts schiebt. Ein solches Feature habe ich in meinen eigenen Move-Thumb noch nicht integriert.

1.820 Beiträge seit 2005
vor 13 Jahren

Hallo!

@Lector:
Mit WPF kann mal letztendlich komplett selbstgestylte Fenster erstellen. Dazu deaktiviert man (über die Windows-API) die äußeren Fenster-Elemente (Non-Client-Area), also Rahmen und Titelleiste, und kann anschließend eigene Controls beliebig setzen. Allerdings muss dann auch Funktionen wie Fenster mit Maus verschieben oder Größe über Rahmen ändern entsprechend neu implementieren.

Nobody is perfect. I'm sad, i'm not nobody 🙁

5.742 Beiträge seit 2007
vor 13 Jahren

Wenn allerdings nur ein bestimmter Teil (Thickness = 0, 40, 0, 0) des Fensters transparent sein soll, wird es schwarz

Lege mal ein Border hinter den Rest, dessen Background du auf White oder so festlegst.

D
216 Beiträge seit 2009
vor 13 Jahren
this.DragMove()

@Lector

Wenn du eine eigene Titelleiste oder etwas ähnliches zum verschieben gemacht hast, kannst du einfach im MouseLeftButtonDown-Event this.DragMove() aufrufen, dann wird das Fenster richtig verschoben und wenn man an die Bildschirmränder kommt das Fenster maximiert, bzw. auf 50% der größe geändert.

DarthMaim

U
1.578 Beiträge seit 2009
vor 13 Jahren

DragMove ist gut, nur

  1. Auf jeden Fall Prüfen das der Mouse State auch Pressed ist
  2. Das aus dem Vollbild wieder raus ziehen funktioniert so nicht
L
862 Beiträge seit 2006
vor 13 Jahren

Danke für die Vorschläge!

DragMove() kannte ich noch nicht. Das werde ich mal ausprobieren.

@CSL
Gibt es auch einfache Lösung für das Problem mit dem Herausziehen aus denn maximierten Modus?

U
1.578 Beiträge seit 2009
vor 13 Jahren

Nicht das ich wüsste, man muss das selber ermitteln
Ein state setzen bei MouseDown, und bei MouseMove dann prüfen wie weit die Maus bewegt wurde, ab ein Paar Pixel dann das Fenster wieder Normalisieren und irgendwie an der Maus positionieren sodass es bewegbar ist.

Das es Problematisch ist sieht man auch daran das selbst Office 2007 das nicht kann, erst in 2010 ist es möglich.

Was noch nicht erwähnt wurde, man muss ja um ein vollständiges Fenster zu schreiben AllowTransparency auf True stellen, nur dann hat das Fenster keine Schatten mehr, finde ich persönlich sehr ärgerlich.

Wie man es richtig macht sieht man auch an Google Chrome, nur das ist ja kein WPF 😄

Man müsste sich tief in die WinApi rein wursteln, wie man das an der Shell Integration Library sieht - das ist kein einfacher Code, und auch die Benutzung ist total bescheiden.

U
1.578 Beiträge seit 2009
vor 13 Jahren

Anmerkung:

Die Fenster bei http://wpfwindow.codeplex.com/ haben auch das Problem was ich hatte, Glass funktioniert nicht bei Vollbild.
Ich habe das gelöst indem ich das Glass durch ein Blurr austausche sobald das Fenster maximiert wird.

Das "Extended" Fenster hat das Problem das die oberen Buttons abgeschnitten werden bei Vollbild, und es ist nur unten rechte Resizable.

Uuuuuuuuuuund

Das Fenster verdeckt weiterhin die Startleiste.

  • Schatten
  • Resize
  • Move
  • Beachtung der Startleiste an verschiedenen Positionen
  • Glass (Maximiert ja/nein)
  • XP support (Glass ja/nein)
  • BorderDrag

Viele Sachen die man beachten muss, ich denke bei meinem Dianthus.Window habe ich ein guten Zwischenweg gefunden.

L
862 Beiträge seit 2006
vor 13 Jahren

Ja das Problem mit der Startleiste habe ich auch. Sobald ein Fenster ohne Standard-Rahmen maximiert ist meint WPF es gleich als Fullscreen darstellen zu müssen. Ich habe auch schon mal versucht das über die WinAPI direkt auszuschalten allerdings funktioniert dann MinWidth und MinHeight des Fensters nicht mehr (irgendwo hier im Forum müsste auch schon ein Thread über genau dieses Thema sein).

U
1.578 Beiträge seit 2009
vor 13 Jahren

Ich habe es so gelöst:

protected override void OnSourceInitialized(EventArgs e)
{
	base.OnSourceInitialized(e);

	IntPtr handle = (new WindowInteropHelper(this)).Handle;
	HwndSource.FromHwnd(handle).AddHook(new HwndSourceHook(WindowProc));
}


private static IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
	switch (msg)
	{
		case 0x0024:
			WmGetMinMaxInfo(hwnd, lParam);
			handled = true;
			break;
	}

	return (IntPtr)0;
}

private static void WmGetMinMaxInfo(IntPtr hwnd, IntPtr lParam)
{
	System.Windows.Interop.HwndSource _win = System.Windows.Interop.HwndSource.FromHwnd(hwnd);
	Window _root = (Window)_win.RootVisual;

	MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));

	int MONITOR_DEFAULTTONEAREST = 0x00000002;
	IntPtr monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);

	if (monitor != IntPtr.Zero)
	{
		var monitorInfo = new MONITORINFO();
		GetMonitorInfo(monitor, monitorInfo);
		var rcWorkArea = monitorInfo.rcWork;
		var rcMonitorArea = monitorInfo.rcMonitor;
		mmi.ptMaxPosition.x = Math.Abs(rcWorkArea.left - rcMonitorArea.left);
		mmi.ptMaxPosition.y = Math.Abs(rcWorkArea.top - rcMonitorArea.top);
		mmi.ptMaxSize.x = Math.Abs(rcWorkArea.right - rcWorkArea.left);
		mmi.ptMaxSize.y = Math.Abs(rcWorkArea.bottom - rcWorkArea.top);

		if (!double.IsInfinity(_root.MinWidth) &&
		   !double.IsInfinity(_root.MinHeight))
		{
			mmi.ptMinTrackSize = new POINT(Convert.ToInt16(_root.MinWidth), Convert.ToInt16(_root.MinHeight));
		}

		if (!double.IsInfinity(_root.MaxWidth) &&
		   !double.IsInfinity(_root.MaxHeight))
		{
			mmi.ptMaxTrackSize = new POINT(Convert.ToInt16(_root.MaxWidth), Convert.ToInt16(_root.MaxHeight));
		}
	}

	Marshal.StructureToPtr(mmi, lParam, true);
}

[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
	public int x;
	public int y;
	public POINT(int x, int y)
	{
		this.x = x;
		this.y = y;
	}
}

[StructLayout(LayoutKind.Sequential)]
private struct MINMAXINFO
{
	public POINT ptReserved;
	public POINT ptMaxSize;
	public POINT ptMaxPosition;
	public POINT ptMinTrackSize;
	public POINT ptMaxTrackSize;
};

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private class MONITORINFO
{
	public int cbSize = Marshal.SizeOf(typeof(MONITORINFO));
	public RECT rcMonitor = new RECT();
	public RECT rcWork = new RECT();
	public int dwFlags = 0;
}

[StructLayout(LayoutKind.Sequential, Pack = 0)]
private struct RECT
{
	public int left;
	public int top;
	public int right;
	public int bottom;
	public static readonly RECT Empty = new RECT();
	public int Width
	{
		get { return Math.Abs(right - left); }
	}
	public int Height
	{
		get { return Math.Abs(bottom - top); }
	}

	public RECT(int left, int top, int right, int bottom)
	{
		this.left = left;
		this.top = top;
		this.right = right;
		this.bottom = bottom;
	}

	public RECT(RECT rcSrc)
	{
		this.left = rcSrc.left;
		this.top = rcSrc.top;
		this.right = rcSrc.right;
		this.bottom = rcSrc.bottom;
	}
}

[DllImport("user32")]
private static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);

[DllImport("user32")]
private static extern IntPtr MonitorFromWindow(IntPtr handle, int flags);

Ich hatte damals eine einfachere Lösung, diese hatte aber das Problem das es nicht funktionierte wenn die Startleiste links oder Oben angedockt war, mit dieser funktioniert alles wie es sein soll.
(Selbstverständlich mit mehreren Monitoren).

Genau so habe ich es in Dianthus.Window 😉

L
862 Beiträge seit 2006
vor 13 Jahren

Ich denke ich werde mir dein Window mal ansehen und dieses Tastleisten-Verhalten evtl. in ein Behavior packen.

EDIT: Kann man sich bei deinen Libraries auch den Code ansehen?

L
862 Beiträge seit 2006
vor 13 Jahren

Ich habe mir jetzt aus deinem Code-Schnipsel ein entsprechendes Behavior gebastelt. Der Code scheint zu funktionieren. Allerdings ist mir beim ersten Mal folgendes passiert:

Ich habe ein Fenster geöffnet, dieses dann maximiert.
Anschließend habe ich mit der Taskleiste herumgespielt (vergrößert, verschoben).
Danach habe ich das Fenster wieder klein gemacht und verkleinert.
Dadurch ist erstmal der komplette PC eingefroren (auch die Maus). Nach ca 10 Sekunden war Windows dann auf 300x200 Pixel meines Hauptbildschirms beschränkt und hat gemeldet dass der Grafikkartetreiber abgestürzt ist. Ich musste mein XP dann neu starten. Das Problem konnte ich nicht mehr reproduzieren werde aber noch auf einigen anderen PCs testen.
Ein solches Problem ist bei mir zum ersten Mal aufgetreten. Von einem Hardware-Defekt gehe ich nicht aus.

Falls es jemanden interressiert:
Hier ist das Behavior welches das Fullscreen-Verhalten ohne Vererbung ermöglicht.
Die Methode WindowProc() könnt ihr aus CSLs Code entnehmen.


private static bool GetHasHook(Window obj)
    {
      return (bool)obj.GetValue(HasHookProperty);
    }
    private static void SetHasHook(Window obj, bool value)
    {
      obj.SetValue(HasHookProperty, value);
    }
    private static readonly DependencyProperty HasHookProperty =
        DependencyProperty.RegisterAttached("HasHook", typeof(bool), typeof(WindowBehavior), new UIPropertyMetadata(false));

    /// <summary>
    /// Ruft den Wert des EnableFullscreen-Properties ab
    /// </summary>
    /// <param name="obj">Bezugsobjekt</param>
    /// <returns>Wert des EnableFullscreen-Properties</returns>
    public static bool GetEnableFullscreen(Window obj)
    {
      return (bool)obj.GetValue(EnableFullscreenProperty);
    }
    /// <summary>
    /// Setzt den Wert des EnableFullscreen-Properties
    /// </summary>
    /// <param name="obj">Bezugsobjekt</param>
    /// <param name="value">Neuer Wert</param>
    public static void SetEnableFullscreen(Window obj, bool value)
    {
      obj.SetValue(EnableFullscreenProperty, value);
    }
    /// <summary>
    /// Gibt an ob ein maximiertes, rahmenloses Fenster im Fullscreen-Modus angezeigt und die Taskleiste verdecken soll
    /// </summary>
    public static readonly DependencyProperty EnableFullscreenProperty =
        DependencyProperty.RegisterAttached("EnableFullscreen", typeof(bool), typeof(WindowBehavior),
        new UIPropertyMetadata(true, onEnableFullscreenChanged));
    private static void onEnableFullscreenChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
      Window wnd = (Window)sender;
      
      if ((bool)e.NewValue)
      {
        wnd.SourceInitialized -= wnd_SourceInitialized;
        removeHook(wnd);
      }
      else
      {
        addHook(wnd);
        wnd.SourceInitialized += wnd_SourceInitialized;
      }
    }

    private static void wnd_SourceInitialized(object sender, EventArgs e)
    {
      Window wnd = (Window)sender;
      addHook(wnd);
    }

    private static void addHook(Window wnd)
    {
      if (!GetHasHook(wnd))
      {
        IntPtr handle = new WindowInteropHelper(wnd).Handle;
        //Hier prüfen wir ob unser Window schon initialisiert wurde
        //Falls nicht steht das handle noch auf 0. Dies würde eine Exception verursachen welche wir hier umgehen.
        //Das HasHook-Flag wird in diesem Fall nicht gesetzt
        if (handle.ToInt32() != 0)
        {
          HwndSource hwnd = HwndSource.FromHwnd(handle);
          hwnd.AddHook(windowProc);
          SetHasHook(wnd, true);
        }
      }
    }
    private static void removeHook(Window wnd)
    {
      if (GetHasHook(wnd))
      {
        IntPtr handle = new WindowInteropHelper(wnd).Handle;
        HwndSource hwnd = HwndSource.FromHwnd(handle);
        hwnd.RemoveHook(windowProc);
        SetHasHook(wnd, false);
      }
    }

L
862 Beiträge seit 2006
vor 13 Jahren

Ein Problem habe ich noch festgestellt.
Wenn man unter XP die Taskleiste so einstellt dass sie sich automatisch ausblendet dann sieht man sie im maximierten Fenstermodus trotzdem nicht. Bei normalen Anwendungen wird diese dann eingeblendet wenn man mit der Maus an den entsprechenden Bildschirmrand fährt.

C
cartilla Themenstarter:in
12 Beiträge seit 2006
vor 13 Jahren

Wenn allerdings nur ein bestimmter Teil (Thickness = 0, 40, 0, 0) des Fensters transparent sein soll, wird es schwarz
Lege mal ein Border hinter den Rest, dessen Background du auf White oder so festlegst.

Sorry das ich jetzt erst antworte. Der Fensterrahmen wird auch schwarz, das kann man auf dem Screenshot vielleicht nicht so gut sehen.

T
179 Beiträge seit 2007
vor 13 Jahren

Gibts da jetzt eigentlich ne gut funktionierende Lösung für?
(Also ohne dass die Buttons im Vollbild abgeschnitten werden und das ganze Schwarz wird wenn man auf den Nicht-Aero Modus umstellt)

M
164 Beiträge seit 2009
vor 13 Jahren

Hallo

Evtl. hilft euch die Lösung weiter: [gelöst] Aero Theme lässt Window schwarz werden

Gruss

Martin