하아찡

XAML TextBox에 TextBlock을 사용하여 PlaceHolder를 추가함. - 1 본문

WPF/XAML조각모음

XAML TextBox에 TextBlock을 사용하여 PlaceHolder를 추가함. - 1

하아찡 2023. 8. 3. 16:58

일단 기본적인 개념은 아래 사이트에서 공부해보시면 생각보다 좋은내용들이 있습니다.

https://www.tutorialspoint.com/xaml/index.htm

 

XAML Tutorial

XAML Tutorial - Welcome to the XAML tutorial for beginners. This tutorial puts greater emphasis on realtime implementation of the concept rather than discussing just the theory part. The primary objective of this tutorial is to provide you a better underst

www.tutorialspoint.com

 

원하는 구성 TextBox 모습입니다.

 텍스트가 추가됐을때 PlaceHolder내용이 상단으로가서 TextBox Input값이 뭐였는지 확인 할 수 있게 TextBlock을 상단으로 옮기는 작업.

 

일단 파일 구성은 아래와같이 구성했습니다.

Convert쪽에 IsNullConverter라는 클래스를 추가하여 해당 TextBox에 값이 Null값인지 체크해줍니다.

 

Themas -> CustomControls에 원하는 TextBox를 ResourceDictionary(리소스사전)을 사용하여 해당 ControlTemplate을 만들어줬습니다.

 

템플릿은 종류가 여러가지있는데 현재 작업하는건 ControlTemplate입니다.

ControlTemplate은 컨트롤 외관을 꾸밀때 사용합니다.

다른 템플릿인 DataTemplate는 컨트롤 컨텐츠 데이터들을 스타일링할때 사용합니다.

일단 현재는 ControlTemplate만 사용했으니 해당 부분만 보겠습니다.

 

전체적인 CustomTextBox.xaml 코드입니다.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib" 
                    xmlns:conv="clr-namespace:WpfApp1.Converter"
                    xmlns:local="clr-namespace:WpfApp1">
    <conv:IsNullConverter x:Key="isNullConverter"/>
    <Style TargetType="{x:Type local:CustomTextbox}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:CustomTextbox}">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="1*"/>
                            <RowDefinition Height="2"/>
                        </Grid.RowDefinitions>
                        <Border BorderBrush="Gray" BorderThickness="0"  Grid.Row="0">
                            <Grid>

                                <TextBox TextWrapping="Wrap" Margin="0,15,0,0" FontSize="16" Padding="2" x:Name="textbox" Text="{TemplateBinding TextValue}">
                                    <TextBox.Style>
                                        <Style TargetType="TextBox">
                                            <Setter Property="BorderThickness" Value="0"/>
                                        </Style>
                                    </TextBox.Style>
                                </TextBox>
                                <TextBlock  Padding="4,2,2,2" Text="{TemplateBinding PlaceHolder}" x:Name="TPlaceHolder" IsHitTestVisible="False" VerticalAlignment="Center" HorizontalAlignment="Left">
                                    <TextBlock.RenderTransform>
                                        <TranslateTransform X="0" Y="0"/>
                                    </TextBlock.RenderTransform>
                                    <TextBlock.Style>
                                        <Style TargetType="TextBlock">
                                            <Setter Property="Visibility" Value="Visible"/>
                                            <Setter Property="Foreground" Value="Gray"/>
                                            <Setter Property="Margin" Value="0,10,0,0"/>
                                            <Setter Property="Padding" Value="4,2,2,2"/>
                                            <Style.Triggers>
                                                <DataTrigger Binding="{Binding Text, ElementName=textbox,Converter={StaticResource ResourceKey=isNullConverter}}" Value="False">
                                                    <DataTrigger.EnterActions>
                                                        <BeginStoryboard>
                                                            <Storyboard>
                                                                <DoubleAnimation 
                                                                         Storyboard.TargetProperty="(TextBlock.RenderTransform).(TranslateTransform.X)" 
                                                                         To="-4" Duration="0:0:0.3"/>

                                                                <DoubleAnimation 
                                                                         Storyboard.TargetProperty="(TextBlock.RenderTransform).(TranslateTransform.Y)" 
                                                                         To="-18" Duration="0:0:0.3"/>
                                                                <ColorAnimation
                                                                         Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)" 
                                                                         To="Black" Duration="0:0:0.3"/>
                                                            </Storyboard>
                                                        </BeginStoryboard>
                                                    </DataTrigger.EnterActions>
                                                    <DataTrigger.ExitActions>
                                                        <BeginStoryboard>
                                                            <Storyboard>
                                                                <DoubleAnimation 
                                                                         Storyboard.TargetProperty="(TextBlock.RenderTransform).(TranslateTransform.X)" 
                                                                         To="0" Duration="0:0:0.3"/>

                                                                <DoubleAnimation 
                                                                         Storyboard.TargetProperty="(TextBlock.RenderTransform).(TranslateTransform.Y)" 
                                                                         To="0" Duration="0:0:0.3"/>
                                                                <ColorAnimation
                                                                         Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)" 
                                                                         To="Gray" Duration="0:0:0.3"/>
                                                            </Storyboard>
                                                        </BeginStoryboard>
                                                    </DataTrigger.ExitActions>

                                                </DataTrigger>

                                            </Style.Triggers>
                                        </Style>
                                    </TextBlock.Style>
                                </TextBlock>
                            </Grid>
                        </Border>

                        <Rectangle Grid.Row="1" Width="auto" Height="auto" Fill="#333"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

 

하나씩 풀어서 보자면

xmlns:conv="clr-namespace:WpfApp1.Converter"

네임스페이스를 지정해줍니다. -> xmlns(네임스페이스 지정) : conv(해당 지정된 이름을 내가 호출할때 사용할 이름)

IsNullConverter를 사용하기위해 추가해줬습니다.

 

 

다음은 방금 지정해온 IsNullConverter를 해당 xaml에 등록시킴.

<conv:IsNullConverter x:Key="isNullConverter"/>

내가 지정해준 conv에다가 :(콜론)을 붙여주면 해당 안에있는 클래스를 가져올수있게 뜹니다. 그중에 작업해둔 IsNullConverter를 가져와서 x:Key를 지정해줍니다. 해당 키 이름을 지정해준뒤 나중에

Converter={StaticResource ResourceKey=isNullConverter}이런식으로 정해준 이름으로 해당 Converter를 불러다 쓸 수 있습니다.

 

 

다음은

작업하는 컨트롤 타입을 정해줍니다.

<Style TargetType="{x:Type local:CustomTextbox}">

이와같이 타겟의 타입을 정해주는데 해당 타입을 정해주면 TargetType안에있는 클래스 변수값들을 TemplateBinding을 통해 가져와 사용 할 수 있습니다.

 

아래 내용은 Xaml코드가 아닌 C#코드 입니다. 언어 적인 설명은 추가하지 않습니다.

해당 CustomTextbox.cs 코드입니다.

internal class CustomTextbox : TextBox
    {
        #region 공용 설정
        public static readonly DependencyProperty PlaceHolderProperty = DependencyProperty.Register("PlaceHolder", typeof(string), typeof(CustomTextbox), new PropertyMetadata(string.Empty));
        public static readonly DependencyProperty TextValueProperty = DependencyProperty.Register("TextValue",typeof(string), typeof(CustomTextbox), new PropertyMetadata(string.Empty));

        #endregion



        public string PlaceHolder
        {
            get { return (string)GetValue(PlaceHolderProperty); }
            set { SetValue(PlaceHolderProperty, value); }
        }

        public ObservableCollection<string> TextValue
        {
            get {
                return (ObservableCollection<string>)GetValue(TextValueProperty); 
            }
            set {
                if ((ObservableCollection<string>)GetValue(TextValueProperty) != value)
                {
                    SetValue(TextValueProperty, value);
                }
            }
        }



        static CustomTextbox()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomTextbox), new FrameworkPropertyMetadata(typeof(CustomTextbox)));
        }


    }

 

다음은 DependencyProperty(의존성 속성)

public static readonly DependencyProperty PlaceHolderProperty = DependencyProperty.Register("PlaceHolder", typeof(string), typeof(CustomTextbox), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty TextValueProperty = DependencyProperty.Register("TextValue",typeof(string), typeof(CustomTextbox), new PropertyMetadata(string.Empty));

 

DependencyProperty.Register 인수들은

아래와 같이 구성됐습니다.

public static DependencyProperty Register(
	string name,
	Type propertyType,
	Type ownerType,
	PropertyMetadata typeMetadata
)

해당 내용은 자세하게 이해하지 못한상태로 작성됐습니다.(작성일 : 23-08-03 / 수정일 : 수정일이 등록됐을경우 이해하고 내용을 바꿈.)

name 은 의존성 속성의 이름을 나타내고 문자열로 지정해줘야함.

propertyType 은 의존성 속성의 데이터 형식을 지정해줘야합니다. 문자열이면 typeof(string) 이런식으로 지정해줘야 합니다.

ownerType 은 의존성 속성의 클래스 형식을 지정해줍니다. 현재는 클래스는 CustomTextbox이므로 typeof(CustomTextbox)로 사용했습니다.

typeMetadata 는 해당 의존성 속성 데이터를 초기 설정값을 지정해줍니다. 현재 코드로는 new PropertyMetadata(string.Empty) 형태로 문자열값을 아무값도 넣어주지 않은 상태로 속성을 시작합니다. 내가 다른 값을 가지고 시작하고 싶을 경우에는 new PropertyMetadata("초기값설정")  이런식으로 변경해줄경우 초기값이 "초기값설정" 으로 나옵니다.(현재는 데이터 타입이 string이여가지고 가능하고 사용자가 바꾼 데이터 타입으로 변경하시면 됩니다.)

 

 

PlaceHoler(워터마크)

public string PlaceHolder
{
	get { return (string)GetValue(PlaceHolderProperty); }
	set { SetValue(PlaceHolderProperty, value); }
}

 

TextValue(실제 텍스트박스안에 들어가는 값을 TextValue에 저장)

public ObservableCollection<string> TextValue
{
	get {
		return (ObservableCollection<string>)GetValue(TextValueProperty); 
	}
	set {
		if ((ObservableCollection<string>)GetValue(TextValueProperty) != value)
		{
			SetValue(TextValueProperty, value);
		}
	}
}

해당 코드에 값을 가져오고 설정하는 코드입니다. 자세한 내용은 생략합니다.

 

여기까지 CustomTextbox.cs 에 대한 설명입니다. 다음은 Xaml에서 Style설정 및 애니메이션 처리하는 코드를 정리하겠습니다.

 

반응형