none
WPF - кнопки окна Minimize, Close RRS feed

  • Общие обсуждения

  • Здравствуйте!

    Осваиваю XAML и нужна помощь :)

    Как вы поняли из заголовка, я пытаюсь создать свои кнопки свертывания и закрытия окна.

    Код кнопки закрытия:

    <Button Margin="100,100,0,0" Style="{DynamicResource ButtonStyle_Close}"/>

    Стиль кнопки:

    <Style x:Key="ButtonStyle_Close" TargetType="{x:Type Button}">
    	<Setter Property="Width" Value="30"/>
    	<Setter Property="Height" Value="30"/>
    	<Setter Property="BorderThickness" Value="0"/>
    	<Setter Property="BorderBrush" Value="White"/>
    	<Setter Property="ToolTip" Value="Закрыть"/>
    	<Setter Property="Template">
    		<Setter.Value>
    			<ControlTemplate TargetType="{x:Type Button}">
    				<Path Name="Path" Data="M5,5 L25,25 M25,5 L5,25" Width="30" Height="30" Stroke="White" StrokeThickness="4"/>
    				<ControlTemplate.Triggers>
    					<Trigger Property="Button.IsMouseOver" Value="True">
    						<Setter Property="Button.BorderThickness" Value="1"/>
    						<Setter TargetName="Path" Property="Path.StrokeThickness" Value="6" />
    					</Trigger>
    					<Trigger Property="Button.IsMouseOver" Value="False">
    						<Setter Property="Button.BorderThickness" Value="0"/>
    						<Setter TargetName="Path" Property="Path.StrokeThickness" Value="4" />
    					</Trigger>
    				</ControlTemplate.Triggers>
    			</ControlTemplate>
    		</Setter.Value>
    	</Setter>
    </Style>

    На данном этапе не получается вот что:

    1) желаемый результат - при наведении указателя мышки на любую область кнопки, толщина крестика увеличивается, при отводе курсора - возвращается в исходное состояние. На настоящий момент - толщина крестика увеличивается только при наведении на сам крестик (фигуру Path). 

    2) желаемый результат - при наведении указателя мышки на область кнопки, появляется белая квадратная рамка кнопки. На настоящий момент - ничего не появляется...

    3) Вопрос про универсальность. По мимо кнопки закрытия у меня еще должна быть кнопка свернуть окно. По сути она получается копиркой кнопки Close (по поведению - при наведении указателя, по стилю отображения - только path и рамка). Гипотетически могут еще понадобиться такие кнопки со схожим поведением и отображением. Получается нецелесообразно писать отдельный стиль для каждой такой кнопки.. много кода получается.. нужно что-то универсальное.. Можно как-нибудь настроить стиль кнопки, чтобы она была без фона, в нем было задано поведение path, но сам path мы задавали бы так:

    <Button>
      <Path Width="100" Height="100" Data="{Binding Geometry}" />
    </Button>

    Нашел примерный код, который пытался адаптировать по себя:

    <Button Name="Test_Button"  Margin="355.5,125.5,67.5,141" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="{x:Null}" >
            <Path Data="M5,5 L25,25 M25,5 L5,25" Width="30" Height="30" Stroke="White" StrokeThickness="4"/>
        <Button.Style>
            <Style TargetType="Button">
                <Style.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Foreground" Value="Red" />
                    </Trigger>
                    <Trigger Property="IsMouseOver" Value="False">
                        <Setter Property="Foreground" Value="Gray" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Button.Style>
    </Button>

    Но опять же код не рабочий. Не работают триггеры, не могу убрать рамку вокруг крестика, при наведении появляется старый фон кнопки.. как вообще убить у кнопки все старое отображение, чтобы она была чистая как слезинка? Blend в стилях задает 

    <Setter Property="FocusVisualStyle" Value="{StaticResource ButtonFocusVisual}"/>
    			<Setter Property="Background" Value="{StaticResource ButtonNormalBackground}"/>
    			<Setter Property="BorderBrush" Value="{StaticResource ButtonNormalBorder}"/>
    Но если строки удалить, то ничего не меняется.. прежний фон кнопки не восстанавливается. А если у кнопки все вручную убирать, то рамка остается, хоть убейся. И при наведении старый фон тоже.

    Как лучше сделать? Помогите доработать код 1 и 2 вариант стилей :) Заранее спасибо! Простите за сумбурность, накипело %)



    17 июня 2014 г. 16:04

Все ответы

  • Ну для начала использовать для этих целей класс Button не совсем лучший вариант.

    Button "тяжелый" класс переполненный многими полезными штуками, которые вы не используете, так зачем перегружать приложение. Я для той же задачи использую Border.

    Теперь по вашим вопросам. С пунктом 1 все просто. Что бы контейнер отлавливал события у него должна быть заливка, причем заливка Transparent тоже считается. Поэтому ели вы не задумываете видимой заливки ставьте в Background не Null, а Transparent.

    По второму пункту не очень понял, так как не увидел ваших попыток в XAML (или не заметил).

    И пункт 3. Само собой универсальность правильное стремление. Она реализуется при использовании моего совета номер 1.

    Собственно все описанное я реализовал в этой статье. Ниже есть ссылка на исходник на VB. Если вы используете C#, то не проблема, вам достаточно будет только XAML


    VB.Net - WPF, WinRT, WP

    18 июня 2014 г. 9:08
    Отвечающий
  • Извините что немного обманул. В той статье я не уделял внимания кнопкам. Это повод написать еще одну статью серии, но что бы вам не ждать выложу XAML сюда.

    Исходник из статьи нужно дорастить и видоизменить так:

    XAML стиля кнопок:

     <!--Стиль кнопок управления окном-->
        <Style x:Key="CloseMiniBorderStyle" TargetType="{x:Type Border}">
            <Setter Property="Background" Value="White"/>
            <Setter Property="Padding" Value="1"/>
            <Setter Property="Opacity" Value="0.7"/>
            <Setter Property="Cursor" Value="Hand"/>
            <Setter Property="CornerRadius" Value="2"/>
            <Style.Triggers>
                <EventTrigger RoutedEvent="UIElement.MouseLeftButtonDown">
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="{x:Null}">
                                <EasingDoubleKeyFrame KeyTime="0" Value="0.7"/>
                            </DoubleAnimationUsingKeyFrames>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
                <EventTrigger RoutedEvent="UIElement.MouseLeftButtonUp">
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="{x:Null}">
                                <EasingDoubleKeyFrame KeyTime="0" Value="1"/>
                            </DoubleAnimationUsingKeyFrames>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="BorderBrush" Value="{StaticResource BorderForegroundBrush}"/>
                    <Setter Property="BorderThickness" Value="1"/>
                    <Setter Property="Opacity" Value="1"/>
                </Trigger>
            </Style.Triggers>
        </Style>

    И участок описывающий эти кнопки в шаблоне окна:

    <Border Name="MinimazeWindow" Height="17" Width="17" Margin="0,0,5,0" Style="{StaticResource CloseMiniBorderStyle}" Visibility="Collapsed">
      <Viewbox VerticalAlignment="Bottom" Margin="2,0,2,2">
       <Line Stroke="Blue" X1="0" X2="6"/>
      </Viewbox>
    </Border>
    <Border  Name="MaximumWindow" Height="17" Width="17" Margin="0,0,5,0" Style="{StaticResource CloseMiniBorderStyle}" Visibility="Collapsed">
      <Border Margin="2" BorderBrush="Green" BorderThickness="1,2,1,1"></Border>
    </Border>
    <Border  Name="CloseWindow" Height="17" Width="17" Style="{StaticResource CloseMiniBorderStyle}">
      <Viewbox VerticalAlignment="Bottom" Margin="2">
       <Grid>
        <Line Stroke="Red" StrokeThickness="1.5" X1="0" X2="6" Y2="7"/>
        <Line Stroke="Red" StrokeThickness="1.5" X1="0" X2="6" Y1="7"/>
       </Grid>
      </Viewbox>
    </Border>
    Будут вопросы пишите.

    VB.Net - WPF, WinRT, WP

    18 июня 2014 г. 9:22
    Отвечающий
  • Спасибо прежде всего за помощь. В том числе и за представленный код. 

    Я: 2) желаемый результат - при наведении указателя мышки на область кнопки, появляется белая квадратная рамка кнопки. На настоящий момент - ничего не появляется...

    Вы: По второму пункту не очень понял, так как не увидел ваших попыток в XAML (или не заметил).

    <ControlTemplate.Triggers>
    					<Trigger Property="Button.IsMouseOver" Value="True">
    						<Setter Property="Button.BorderThickness" Value="1"/>
    						<Setter TargetName="Path" Property="Path.StrokeThickness" Value="6" />
    					</Trigger>
    					<Trigger Property="Button.IsMouseOver" Value="False">
    						<Setter Property="Button.BorderThickness" Value="0"/>
    						<Setter TargetName="Path" Property="Path.StrokeThickness" Value="4" />
    					</Trigger>
    				</ControlTemplate.Triggers>

    Я игрался в стиле свойством Button.BorderThickness, устанавливая его то в 0, то в 1. По моей задумке рамка должна была появляться...

    Я пошел по вашему пути и использую border. Сейчас мой код выглядит так:

    Кнопка:

    <!-- Кнопка "Закрыть окно" -->
    					<Border x:Name="border_close" Style="{DynamicResource CloseWindow}" Margin="969.125,0,0,0" Height="Auto" Width="Auto">
    						<Path x:Name="path_cross" Data="M1,1 L2,2 M2,1 L1,2" Stroke="White" StrokeThickness="3" HorizontalAlignment="Center" VerticalAlignment="Center" Width="14" Height="14" ToolTip="Закрыть" Stretch="Fill"/>
    					</Border>

    Стиль кнопки:

    <!-- Стиль кнопки "Закрыть окно" -->
    		<Style x:Key="CloseWindow" TargetType="{x:Type Border}">
    			<Setter Property="Width" Value="25"/>
    			<Setter Property="Height" Value="25"/>
    			<Setter Property="Background" Value="Transparent"/>
    			<Setter Property="BorderBrush" Value="Transparent"/>
    			<Setter Property="BorderThickness" Value="0"/>
    			<Style.Triggers>
    				<EventTrigger RoutedEvent="UIElement.MouseEnter">
    					<BeginStoryboard>
    						<Storyboard>
    							<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Width)" Storyboard.TargetName="{x:Null}">
    								<EasingDoubleKeyFrame KeyTime="0" Value="14"/>
    							<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="16"/>
    							</DoubleAnimationUsingKeyFrames>
    							<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="{x:Null}">
    								<EasingDoubleKeyFrame KeyTime="0" Value="14"/>
    								<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="16"/>
    							</DoubleAnimationUsingKeyFrames>
    							<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Shape.StrokeThickness)" Storyboard.TargetName="{x:Null}">
    								<EasingDoubleKeyFrame KeyTime="0" Value="3"/>
    								<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="4"/>
    							</DoubleAnimationUsingKeyFrames>
    						</Storyboard>
    					</BeginStoryboard>
    				</EventTrigger>
    				<EventTrigger RoutedEvent="UIElement.MouseLeave">
    				<BeginStoryboard>
    					<Storyboard>
    						<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Width)" Storyboard.TargetName="{x:Null}">
    							<EasingDoubleKeyFrame KeyTime="0" Value="16"/>
    							<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="14"/>
    						</DoubleAnimationUsingKeyFrames>
    						<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="{x:Null}">
    							<EasingDoubleKeyFrame KeyTime="0" Value="16"/>
    							<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="14"/>
    						</DoubleAnimationUsingKeyFrames>
    						<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Shape.StrokeThickness)" Storyboard.TargetName="{x:Null}">
    							<EasingDoubleKeyFrame KeyTime="0" Value="4"/>
    							<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="3"/>
    						</DoubleAnimationUsingKeyFrames>
    					</Storyboard>
    				</BeginStoryboard>
    			</EventTrigger>
            </Style.Triggers>
    		</Style>

    Код не рабочий. Опять я встречаю проблему, когда не могу "достучаться" до path в кнопке, чтобы изменить его свойства (например, ширину и высоту, как указано в стиле). Не могу получить доступ к свойствам path даже через TargetName (x:Name), хотя на сколько мне известно оно специально создавалось как раз, чтобы к нему обращаться из любого места кода XAML и C#, VB и пр.

    При указании TargetName = path_cross, получаю ошибку "свойство targetname не может быть задано для style setter". Как быть? :)

    <Window.Resources>
    		<!-- Стиль кнопки "Закрыть окно" -->
    		<Style x:Key="CloseWindow" TargetType="{x:Type Border}">
    			<Setter Property="Width" Value="25"/>
    			<Setter Property="Height" Value="25"/>
    			<Setter Property="Background" Value="Transparent"/>
    			<Setter Property="BorderBrush" Value="Transparent"/>
    			<Setter Property="BorderThickness" Value="0"/>
    		</Style>
    		
    		<Storyboard x:Key="OnMouseEnter1">
    			<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Width)" Storyboard.TargetName="path_cross">
    				<EasingDoubleKeyFrame KeyTime="0" Value="14"/>
    				<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="16"/>
    			</DoubleAnimationUsingKeyFrames>
    			<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="path_cross">
    				<EasingDoubleKeyFrame KeyTime="0" Value="14"/>
    				<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="16"/>
    			</DoubleAnimationUsingKeyFrames>
    			<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Shape.StrokeThickness)" Storyboard.TargetName="path_cross">
    				<EasingDoubleKeyFrame KeyTime="0" Value="3"/>
    				<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="4"/>
    			</DoubleAnimationUsingKeyFrames>
    		</Storyboard>
    		<Storyboard x:Key="OnMouseLeave1">
    			<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Width)" Storyboard.TargetName="path_cross">
    				<EasingDoubleKeyFrame KeyTime="0" Value="16"/>
    				<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="14"/>
    			</DoubleAnimationUsingKeyFrames>
    			<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="path_cross">
    				<EasingDoubleKeyFrame KeyTime="0" Value="16"/>
    				<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="14"/>
    			</DoubleAnimationUsingKeyFrames>
    			<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Shape.StrokeThickness)" Storyboard.TargetName="path_cross">
    				<EasingDoubleKeyFrame KeyTime="0" Value="4"/>
    				<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="3"/>
    			</DoubleAnimationUsingKeyFrames>
    		</Storyboard>
    </Window.Resources>
    <Window.Triggers>
    		<EventTrigger RoutedEvent="Mouse.MouseEnter" SourceName="border_close">
    			<BeginStoryboard Storyboard="{StaticResource OnMouseEnter1}"/>
    		</EventTrigger>
    		<EventTrigger RoutedEvent="Mouse.MouseLeave" SourceName="border_close">
    			<BeginStoryboard x:Name="OnMouseLeave1_BeginStoryboard" Storyboard="{StaticResource OnMouseLeave1}"/>
    		</EventTrigger>
    </Window.Triggers>

    Это код из Blend, здесь все вынесено за стиль :( Код рабочий.

    19 июня 2014 г. 13:05
  • хотя на сколько мне известно оно специально создавалось как раз, чтобы к нему обращаться из любого места кода XAML и C#, VB и пр.

    Не в коем случае. У всего есть область видимости. Нельзя так назвать элемент, что бы он потом был везде доступен.

    Вы пытаетесь скрестить универсальность с удобством. Это не всегда возможно. Так как у кнопки закрытия и сворачивания будет разный path засунуть в стиль его не получится, а значит обращаться к нему из стиля тоже не выйдет.

    Но всегда есть обходные пути. Пример -  вы можете нарисовать path вне стиля, а его BorderThickness привязать к свойству BorderThickness головного Border. При таком раскладе толщина линии у Border будет менятся в стиле, а path отреагирует по привязке.


    VB.Net - WPF, WinRT, WP

    19 июня 2014 г. 13:15
    Отвечающий