Jak utworzyć okno WPF bez obramowania, którego rozmiar można zmienić tylko za pomocą uchwytu?

95

Jeśli ustawisz ResizeMode="CanResizeWithGrip"na WPF, Windowuchwyt zmiany rozmiaru jest wyświetlany w prawym dolnym rogu, jak poniżej:

Jeśli również ustawisz, WindowStyle="None"pasek tytułu zniknie, ale szara ścięta krawędź pozostanie do momentu ustawienia ResizeMode="NoResize". Niestety, przy tej kombinacji właściwości, uchwyt zmiany rozmiaru również znika.

Mam zastąpione przez Window„s ControlTemplatepoprzez zwyczaju Style. Chcę samodzielnie określić obramowanie okna i nie potrzebuję, aby użytkownicy mogli zmieniać rozmiar okna ze wszystkich czterech stron, ale potrzebuję uchwytu zmiany rozmiaru.

Czy ktoś może wyszczególnić prosty sposób spełnienia wszystkich tych kryteriów?

  1. Nie miej obramowania Windowpoza tą, którą określam w a ControlTemplate.
  2. Miej działający uchwyt zmiany rozmiaru w prawym dolnym rogu.
  3. Nie mają paska tytułu.
Drew Noakes
źródło
3
Należy pamiętać, że Allowtransperency powoduje wyciek pamięci. Więc unikaj go. Proszę odnieść się do social.msdn.microsoft.com/Forums/en/wpf/thread/…
Dipesh Bhatt,
1
@DipeshBhatt Nie mogłem znaleźć żadnego wyjaśnienia tego twierdzenia w podanym przez Ciebie łączu. może chciałeś zamieścić link social.msdn.microsoft.com/Forums/vstudio/en-US/…
itsho
Stałem twarzą do szarej krawędzi u góry, chociaż ustawiłem styl okna na Brak. ResizeMode = "NoResize" rozwiązało mój problem.
Surendra Shrestha

Odpowiedzi:

182

Jeśli ustawisz AllowsTransparencywłaściwość na Window(nawet bez ustawiania wartości przezroczystości), obramowanie znika i możesz zmienić rozmiar tylko za pomocą uchwytu.

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="640" Height="480" 
    WindowStyle="None"
    AllowsTransparency="True"
    ResizeMode="CanResizeWithGrip">

    <!-- Content -->

</Window>

Wynik wygląda następująco:

ZombieSheep
źródło
Czysty fuks Wiedziałem o tym - dziś po południu grałem z tym samym zestawem kontrolnym. :)
ZombieSheep
2
Wow, nie spodziewałbym się tego, ale jest to całkowicie przydatne do tworzenia własnych-notatek-post-it-w-5-minut, dzięki :)
Tomáš Kafka
4
AllowTransparency ma jednak kilka wad, system Windows nie może już obsługiwać kontrolek okien podrzędnych, takich jak WebBrowser, zwykle wymusza renderowanie oprogramowania, zgłosił wycieki pamięci. Zobacz moje obejście poniżej.
Wobbles
Potrzebujesz tylko WindowStyle = "None", aby pozbyć się granic; AllowsTransparency tylko zdarza się, że tego wymaga, ale nie wpływa na granice.
Grault
2
@Grault, który usuwa nagłówek okna, ale wokół formularza nadal jest solidna ramka. Pozwala Przejrzystość pozbywa się granic.
Wobbles
81

Próbowałem stworzyć okno bez obramowania, WindowStyle="None"ale kiedy je testowałem, wydaje się, że pojawia się biały pasek na górze, po kilku badaniach wydaje się, że jest to "Ramka zmiany rozmiaru", oto obraz (zauważyłem na żółto):

Wyzwanie

Po kilku poszukiwaniach w Internecie i wielu trudnych rozwiązaniach innych niż XAML, wszystkie rozwiązania, które znalazłem, zawierały kod w C # i wiele linii kodu, znalazłem pośrednio rozwiązanie tutaj: Maksymalne niestandardowe okno traci efekt cienia

<WindowChrome.WindowChrome>
    <WindowChrome 
        CaptionHeight="0"
        ResizeBorderThickness="5" />
</WindowChrome.WindowChrome>

Uwaga : Musisz użyć platformy .NET 4.5 lub, jeśli używasz starszej wersji, użyj WPFShell, po prostu odwołaj się do powłoki i użyj Shell:WindowChrome.WindowChromezamiast niej.

Użyłem WindowChromewłaściwości Window, jeśli używasz tego, białe "obramowanie zmiany rozmiaru" znika, ale musisz zdefiniować niektóre właściwości, aby działały poprawnie.

CaptionHeight: Jest to wysokość obszaru podpisu (paska nagłówka), który umożliwia przyciąganie Aero, zachowanie dwukrotnego kliknięcia, jak w przypadku zwykłego paska tytułu. Ustaw to na 0 (zero), aby przyciski działały.

ResizeBorderThickness: Jest to grubość na krawędzi okna, w której możesz zmienić rozmiar okna. Ustawiłem na 5, ponieważ podoba mi się ta liczba i ponieważ jeśli ustawisz zero, trudno jest zmienić rozmiar okna.

Po użyciu tego krótkiego kodu wynik jest taki:

Rozwiązanie

A teraz biała ramka zniknęła bez użycia, ResizeMode="NoResize"a AllowsTransparency="True"także pokazuje cień w oknie.

Później wyjaśnię jak sprawić, aby przyciski działały (nie użyłem obrazków do przycisków) w prosty i krótki kod, jestem nowy i myślę, że mogę pisać do codeproject, bo tutaj nie znalazłem miejsca aby opublikować samouczek.

Może jest inne rozwiązanie (wiem, że są trudne i trudne rozwiązania dla noobów takich jak ja), ale to działa w przypadku moich osobistych projektów.

Oto pełny kod

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:Concursos"
    mc:Ignorable="d"
    Title="Concuros" Height="350" Width="525"
    WindowStyle="None"
    WindowState="Normal" 
    ResizeMode="CanResize"
    >
<WindowChrome.WindowChrome>
    <WindowChrome 
        CaptionHeight="0"
        ResizeBorderThickness="5" />
</WindowChrome.WindowChrome>

    <Grid>

    <Rectangle Fill="#D53736" HorizontalAlignment="Stretch" Height="35" VerticalAlignment="Top" PreviewMouseDown="Rectangle_PreviewMouseDown" />
    <Button x:Name="Btnclose" Content="r" HorizontalAlignment="Right" VerticalAlignment="Top" Width="35" Height="35" Style="{StaticResource TempBTNclose}"/>
    <Button x:Name="Btnmax" Content="2" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,0,35,0" Width="35" Height="35" Style="{StaticResource TempBTNclose}"/>
    <Button x:Name="Btnmin" Content="0" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,0,70,0" Width="35" Height="35" Style="{StaticResource TempBTNclose}"/>

</Grid>

Dziękuję Ci!

Fernando Aguirre
źródło
3
Cóż, brawa dla tego! To jest zdecydowanie najprostsza odpowiedź w tym wątku / brak transakcji! Powinien uzyskać znacznie więcej głosów „pozytywnych”. Muszę przyznać, że byłem trochę sceptyczny, zwłaszcza co do tego, co działo się pod maską. Sprawdziłem nawet drzewo WFP i wygląda na to, że przezroczystość nie została ponownie dodana. Nawet skomplikowane elementy sterujące, takie jak wyświetlanie „WebBrowser”, są w porządku. Miałem problem z zawieszaniem się renderowania przy użyciu przezroczystości, gdy aplikacja była bardzo obciążona ... Cóż, tak się nie dzieje w przypadku tego rozwiązania. Myślę, że nadszedł czas, aby wycofać rozwiązanie @Wobbles!
VeV
1
Wygląda na to, że może nadal wymagać współdziałania podczas przeciągania okna, jeśli Rectangle_PreviewMouseDownpoprawnie zinterpretuję użycie zdarzenia.
Wobbles
To może nie działać w <= Win 8.1, zauważyłem dziwne wyniki na mojej maszynie wirtualnej. Windows 7 i 8 są głównymi problemami, ponieważ robią głupią granicę Aero.
Wobbles
Dziękuję za odpowiedź
Fernando Aguirre
Cześć @FernandoAguirre, opublikowałem to powiązane pytanie i byłbym wdzięczny, gdybyś miał odpowiedź.
sampathsris
40

Chociaż przyjęta odpowiedź jest bardzo prawdziwa, chcę tylko zwrócić uwagę, że AllowTransparency ma pewne wady. Nie pozwala na wyświetlanie elementów sterujących okna potomnego, np. WebBrowser, i zazwyczaj wymusza renderowanie oprogramowania, które może mieć negatywny wpływ na wydajność.

Jest jednak lepsza obejście.

Jeśli chcesz utworzyć okno bez obramowania, którego rozmiar można zmienić i jest w stanie hostować formant WebBrowser lub formant Frame wskazujący na adres URL, którego po prostu nie mogłeś, zawartość wspomnianej kontrolki byłaby pusta.

Znalazłem jednak obejście; w oknie, jeśli ustawisz WindowStyle na None, ResizeMode na NoResize (proszę ze mną, nadal będziesz mógł zmienić rozmiar po zakończeniu), a następnie upewnij się, że masz UNCHECKED AllowsTransparency, będziesz mieć statyczne okno bez obramowania i pokaże kontrolka przeglądarki.

Teraz prawdopodobnie nadal chcesz mieć możliwość zmiany rozmiaru, prawda? Cóż, możemy to zrobić za pomocą połączenia międzyoperacyjnego:

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

    [DllImportAttribute("user32.dll")]
    public static extern bool ReleaseCapture();

    //Attach this to the MouseDown event of your drag control to move the window in place of the title bar
    private void WindowDrag(object sender, MouseButtonEventArgs e) // MouseDown
    {
        ReleaseCapture();
        SendMessage(new WindowInteropHelper(this).Handle,
            0xA1, (IntPtr)0x2, (IntPtr)0);
    }

    //Attach this to the PreviewMousLeftButtonDown event of the grip control in the lower right corner of the form to resize the window
    private void WindowResize(object sender, MouseButtonEventArgs e) //PreviewMousLeftButtonDown
    {
        HwndSource hwndSource = PresentationSource.FromVisual((Visual)sender) as HwndSource;
        SendMessage(hwndSource.Handle, 0x112, (IntPtr)61448, IntPtr.Zero);
    }

I voila, okno WPF bez obramowania, nadal ruchome i skalowalne bez utraty zgodności z kontrolkami takimi jak WebBrowser

Chwieje się
źródło
Jak powinniśmy się zachować, jeśli chcemy zmienić rozmiar ze wszystkich stron, nie tylko u dołu po prawej, ale nadal nie chcemy, aby obramowanie było widoczne?
Daniel
6
Oto inne wartości wParam, po prostu przypisz nowe zdarzenia do nowych obiektów UI, używając ich w razie potrzebyprivate enum ResizeDirection { Left = 61441, Right = 61442, Top = 61443, TopLeft = 61444, TopRight = 61445, Bottom = 61446, BottomLeft = 61447, BottomRight = 61448, }
Wobbles
Działa to świetnie dla mnie, z jednym wyjątkiem, ale gdy masz NoResize, nie możesz już przyciągnąć okna, przeciągając go na górę.
CJK
2
@CJK Prawda, ale bez wątpienia możesz również podłączyć wiadomości systemu Windows i sobie z tym poradzić.
Wobbles
Nie mogę przeciągnąć okna. jakiś pomysł, dlaczego? (zmiana rozmiaru działa jak urok)
Li3ro
5

Próbka tutaj:

<Style TargetType="Window" x:Key="DialogWindow">
        <Setter Property="AllowsTransparency" Value="True"/>
        <Setter Property="WindowStyle" Value="None"/>
        <Setter Property="ResizeMode" Value="CanResizeWithGrip"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Window}">
                    <Border BorderBrush="Black" BorderThickness="3" CornerRadius="10" Height="{TemplateBinding Height}"
                            Width="{TemplateBinding Width}" Background="Gray">
                        <DockPanel>
                            <Grid DockPanel.Dock="Top">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition></ColumnDefinition>
                                    <ColumnDefinition Width="50"/>
                                </Grid.ColumnDefinitions>
                                <Label Height="35" Grid.ColumnSpan="2"
                                       x:Name="PART_WindowHeader"                                            
                                       HorizontalAlignment="Stretch" 
                                       VerticalAlignment="Stretch"/>
                                <Button Width="15" Height="15" Content="x" Grid.Column="1" x:Name="PART_CloseButton"/>
                            </Grid>
                            <Border HorizontalAlignment="Stretch" VerticalAlignment="Stretch" 
                                        Background="LightBlue" CornerRadius="0,0,10,10" 
                                        Grid.ColumnSpan="2"
                                        Grid.RowSpan="2">
                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition/>
                                        <ColumnDefinition Width="20"/>
                                    </Grid.ColumnDefinitions>
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="*"/>
                                        <RowDefinition Height="20"></RowDefinition>
                                    </Grid.RowDefinitions>
                                    <ResizeGrip Width="10" Height="10" Grid.Column="1" VerticalAlignment="Bottom" Grid.Row="1"/>
                                </Grid>
                            </Border>
                        </DockPanel>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
Kebes
źródło
24
Powinieneś dołączyć krótkie wyjaśnienie, jak działa kod.
M456
0

Miałem trudności z uzyskaniem odpowiedzi przy użyciu @ fernando-aguirre WindowChromedo pracy. To nie działa w moim przypadku bo nadrzędne OnSourceInitializedw MainWindowa nie wywołanie metody klasy bazowej.

protected override void OnSourceInitialized(EventArgs e)
{
    ViewModel.Initialize(this);
    base.OnSourceInitialized(e); // <== Need to call this!
}

Dręczyło mnie to bardzo długo.

Mike Ward
źródło