하아찡

CustomTitleBar작업 -1 본문

WPF/XAML조각모음

CustomTitleBar작업 -1

하아찡 2023. 8. 18. 17:17

기존 타이틀바 말고 새롭게 커스텀 하고싶어서 만들어 봤습니다.

 

 

결과물

 

현재까지 구현한 기능은 아이콘 추가, 타이틀명, 최소화, 최대화, 폼 종료까지 구현했습니다.

 

Window Xaml 코드입니다.

<Window x:Class="TitleBar.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:TitleBar"
        mc:Ignorable="d"
        x:Name="MyForm"
        xmlns:rclass="clr-namespace:TitleBar.Themas.ResourceClass"
        WindowStyle="None"
        BorderThickness="0"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="50"/>
            <RowDefinition Height="50"/>
            <RowDefinition Height="1*"/>
        </Grid.RowDefinitions>
        <rclass:CustomTitleBar Icon="/Resources/teddy-bear.ico" Grid.Row="0" WindowForm="{x:Reference MyForm}" HoverBackColor="Beige" TitleName="타이틀바 테스트1" />
        <rclass:CustomTitleBar  Grid.Row="1" ContentColor="Red" WindowForm="{x:Reference MyForm}" BackColor="AliceBlue" HoverBackColor="#FFCFE7FD" TitleName="타이틀바 테스트2" />
    </Grid>
</Window>

기존에 있는 Titlebar를 지우기위해 WindowStyle="None" 을 추가하여 기본으로 제공하는 Titlebar를 제거 했습니다.

CustomControl 이름은 CustomTitleBar라고 작업을 진행 했습니다.

 

CustomTitleBar.Xaml 코드 입니다.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:rclass="clr-namespace:TitleBar.Themas.ResourceClass"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Style TargetType="Button" x:Key="Btns">
        <Setter Property="Template">
            <Setter.Value>
                
                <ControlTemplate TargetType="{x:Type Button}">
                    
                    <Grid Background="{Binding BackColor , RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}" x:Name="TargetBack">
                        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center">
                            <ContentPresenter.Resources>
                                <Style TargetType="TextBlock">
                                    <Setter Property="Foreground" Value="{Binding ContentColor , RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}"/>
                                </Style>
                            </ContentPresenter.Resources>
                        </ContentPresenter>
                    </Grid>
                    
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="TargetBack" Property="Background" Value="{Binding HoverBackColor , RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}"/>

                        </Trigger>

                    </ControlTemplate.Triggers>
                </ControlTemplate>

            </Setter.Value>
        </Setter>

    </Style>

    <Style TargetType="{x:Type rclass:CustomTitleBar}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type rclass:CustomTitleBar}">
                    <Grid Background="{Binding BackColor , RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}">
                        <Grid.InputBindings>
                            <MouseBinding Gesture="LeftClick" Command="{Binding DragFormCommand, RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}" />
                        </Grid.InputBindings>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="{Binding IconWidth , RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}"/>
                            <ColumnDefinition Width="1*" />
                            <ColumnDefinition Width="{Binding MinimizeButton , RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}"/>
                            <ColumnDefinition Width="{Binding MinMaxButton , RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}"/>
                            <ColumnDefinition Width="{Binding CloseButton , RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}"/>
                        </Grid.ColumnDefinitions>

                        <StackPanel  Orientation="Horizontal" Margin="5,3,5,3" Width="auto">
                            <Image Grid.Column="0" Width="20" Height="20" Stretch="Fill"
                                VerticalAlignment="Center" HorizontalAlignment="Center"
                                Source="{Binding Icon , RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}"
                                   />
                        </StackPanel>

                        <TextBlock Grid.Column="1" Padding="6,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="14" Foreground="{TemplateBinding ContentColor}"
                            Text="{Binding TitleName, RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}"/>

                        <Button Grid.Column="2" Content="ㅡ" Style="{StaticResource Btns}"
                            Command="{Binding MinimizeFormCommand, RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}" />

                        <Button Grid.Column="3" Content="ㅁ" Style="{StaticResource Btns}"
                            Command="{Binding MinMaxFormCommand, RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}"/>

                        <Button Grid.Column="4" Content="X" Style="{StaticResource Btns}"
                            Command="{Binding CloseFormCommand, RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

첫번째 x:key="Btns"는 CustomTitleBar에서 최소화, 최대화, 닫기를 모두 버튼을 사용하여 만들어서 스타일 한 개를 만들어 추가하는 방식으로 사용했습니다.

해당 스타일에는 기본적인 배경색, 폰트색상, 호버됐을때 배경색변경하는 이벤트들을 추가 하였습니다.

해당 코드를보면 기본적으로 그냥 Trigger를 사용했을때 호버됐을때 배경색이 변경되지 않아 ControlTemplate으로 새로 만들어 추가해줬습니다.

 

배경색 변경 및 폰트색 변경쪽 데이터를 받아오기위해 CustomTitleBar 리소스 클래스 코드에 존재하는 값을 바인딩 하기위해 아래와같이 추가해줬습니다.

Background="{Binding BackColor , RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}"

해당 부분을 보면 하위 요소에서 특정 데이터를 가져오기위해 AncestorType={x:Type 특정데이터를 가지고있는요소} 을 사용하여 특정 데이터를 가져올 수 있습니다.

작업하다 보면 해당 데이터 안에 갇혀 상위 데이터를 가져오지 못할때 사용하면 좋을 듯 합니다.

 

 

CustomTitleBar에 대한 내용입니다.

<Style TargetType="{x:Type rclass:CustomTitleBar}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type rclass:CustomTitleBar}">
                    <Grid Background="{Binding BackColor , RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}">
                        <Grid.InputBindings>
                            <MouseBinding Gesture="LeftClick" Command="{Binding DragFormCommand, RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}" />
                        </Grid.InputBindings>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="{Binding IconWidth , RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}"/>
                            <ColumnDefinition Width="1*" />
                            <ColumnDefinition Width="{Binding MinimizeButton , RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}"/>
                            <ColumnDefinition Width="{Binding MinMaxButton , RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}"/>
                            <ColumnDefinition Width="{Binding CloseButton , RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}"/>
                        </Grid.ColumnDefinitions>

                        <StackPanel  Orientation="Horizontal" Margin="5,3,5,3" Width="auto">
                            <Image Grid.Column="0" Width="20" Height="20" Stretch="Fill"
                                VerticalAlignment="Center" HorizontalAlignment="Center"
                                Source="{Binding Icon , RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}"
                                   />
                        </StackPanel>

                        <TextBlock Grid.Column="1" Padding="6,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="14" Foreground="{TemplateBinding ContentColor}"
                            Text="{Binding TitleName, RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}"/>

                        <Button Grid.Column="2" Content="ㅡ" Style="{StaticResource Btns}"
                            Command="{Binding MinimizeFormCommand, RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}" />

                        <Button Grid.Column="3" Content="ㅁ" Style="{StaticResource Btns}"
                            Command="{Binding MinMaxFormCommand, RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}"/>

                        <Button Grid.Column="4" Content="X" Style="{StaticResource Btns}"
                            Command="{Binding CloseFormCommand, RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}" />
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

그리드는 최대 5개로 나눠져있으며, 

첫번째 컬럼은 아이콘을 넣어줄 Image를 저장.

두번째 컬럼은 타이틀명을 저장해줄 TextBlock을 저장.

세번째 컬럼은 최소화를 해줄 버튼을 저장.

네번쨰 컬럼은 축소 및 확대를 해줄 버튼을 저장.

다섯번째 컬럼은 해당 폼을 종료 해줄 버튼을 저장.

 

드래그 이벤트를 작동시킬 타이틀바를 클릭을해야하는데 모든데이터를 묶어주고 있는 Grid에 클릭 이벤트를 추가해줬습니다.

<Grid.InputBindings>
	<MouseBinding Gesture="LeftClick" Command="{Binding DragFormCommand, RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}" />
</Grid.InputBindings>

Grid에 직접적으로 Commad를 사용하지못해 InputBindings라는 속성을 사용해서 MouseBinding을 사용했습니다.

MouseBinding은 아래를 참고하세요.

https://learn.microsoft.com/ko-kr/dotnet/api/system.windows.input.mousebinding?view=windowsdesktop-6.0 

 

MouseBinding 클래스 (System.Windows.Input)

MouseGesture를 RoutedCommand 또는 다른 ICommand 구현에 바인딩합니다.

learn.microsoft.com

 

첫번째 컬럼

<StackPanel  Orientation="Horizontal" Margin="5,3,5,3" Width="auto">
		<Image Grid.Column="0" Width="20" Height="20" Stretch="Fill"
		VerticalAlignment="Center" HorizontalAlignment="Center"
		Source="{Binding Icon , RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}"/>
</StackPanel>

아이콘 경로를 바인딩을 통해 받아와 아이콘 수정을 편리하게 추가했음.

 

 

두번째 컬럼

<TextBlock Grid.Column="1" Padding="6,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="14" Foreground="{TemplateBinding ContentColor}"
                            Text="{Binding TitleName, RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}"/>

TextBlock에 Text에 데이터를 바인딩하여 추가했음. 

 

 

3~5 컬럼

<Button Grid.Column="2" Content="ㅡ" Style="{StaticResource Btns}"
	Command="{Binding MinimizeFormCommand, RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}" CommandParameter="{TemplateBinding WindowForm}"/>

<Button Grid.Column="3" Content="ㅁ" Style="{StaticResource Btns}"
	Command="{Binding MinMaxFormCommand, RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}" CommandParameter="{TemplateBinding WindowForm}"/>

<Button Grid.Column="4" Content="X" Style="{StaticResource Btns}"
	Command="{Binding CloseFormCommand, RelativeSource={RelativeSource AncestorType={x:Type rclass:CustomTitleBar}}}" CommandParameter="{TemplateBinding WindowForm}"/>

최소화, 축소 및 최대화, 종료버튼을 추가해줌.

스타일은 맨위에서 작성한 스타일을 추가해서 사용했습니다.

 

여기까지 CustomTitleBar.Xaml 코드였습니다.

다음편에는 Cs코드를 살펴보겠습니다.

반응형