하아찡

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

WPF/XAML조각모음

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

하아찡 2023. 8. 3. 17:01

아래 코드는 Xaml Style 전체 입니다.

<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>

 
현재 리소스사전 타겟 타입을 1편에서 소개했던 CustomTextbox.cs 로 등록해줍니다.

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

 
수정할 속성을 지정해줍니다.

<Setter Property="Template">

 
이와같이 Template을 속성으로 지정해주고 ControlTemplate을 설정해줍니다.

<Setter.Value>
	<ControlTemplate TargetType="{x:Type local:CustomTextbox}">

 
텍스트를 입력받을 텍스트 박스를 추가해줍니다.

<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>

x:Name 이부분은 해당 컨트롤 이름을 정해줘서 Xaml안에서 해당 이름으로 컨트롤을 찾아낼 수 있습니다.
MVC패턴에선 해당 이름을가지고 코드에서도 사용 할 수 있습니다.
 
Text="{TemplateBinding TextValue}" 이부분은 1편에서 CustomTextbox.cs에서 지정해줬던 변수를 바인딩시켜줘서 Xaml과 CustomTextbox를 서로 연결시켜줍니다.
 
<TextBox.Style> 이부분은 TextBox 스타일값을 설정하기 위해서 열어줍니다.
 
<Style TargetType="TextBox"> 이부분은 기본적으로 텍스트박스에 존재하는 테두리값을 없애주기위해 TextBox를 타겟을 잡고 해당 타겟 안에있는 데이터값을 <Setter>로 수정해줍니다.
제가 수정해줄 내용은 테두리값인데 TextBox 테두리값은 BorderThickness로 존재하기때문에 
<Setter Property="BorderThickness" Value="0"/> 이와같이 추가해주면 텍스트박스 테두리가 사라집니다.
 
 
아래 코드는 PlaceHolder를 사용하기위해 TextBlock를 셋팅해준 부분입니다.

<TextBlock  Padding="4,2,2,2" Text="{TemplateBinding PlaceHolder}" x:Name="TPlaceHolder" 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>

Text="{TemplateBinding PlaceHolder}" 이부분은 1편에서 CustomTextbox.cs에서 지정해줬던 변수를 바인딩시켜줘서 Xaml과 CustomTextbox를 서로 연결시켜줍니다.
 
 
애니메이션을 사용할때 Margin값 말고 위차값으로 변경해서 해당 코드를 추가해줬습니다.

<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를 사용하겠습니다.
DataTrigger는 값을 기준으로 작동시키고 싶을때 사용합니다. 저는 TextBox에 텍스트가 없을경우에는 TextBlock을 회색처리하고 TextBox위에 있다가 TextBox에 데이터가 들어올경우 TextBlock색상이 검정색으로 변경되며 상단으로 움직이게 처리를 하기위해 값을 받아와야해서 DataTrigger를 사용했습니다. 그밖에 EventTrigger, MultiTrigger 등등 있으니 한번 찾아서 공부해보시는것도 좋습니다.
 
아래 코드는 DataTrigger를 사용한 코드 일부분 입니다.

<DataTrigger Binding="{Binding Text, ElementName=textbox,Converter={StaticResource ResourceKey=isNullConverter}}" Value="False">

Binding 은 Value값이랑 비교할 값을 넣어주는 부분입니다.
바인딩 내부에 보시면 {Binding Text, ElementName=textbox, Converter={staticResource ResourceKey=isNullConverter}}가 있는데 하나씩 풀어서 보겠습니다.
Binding Text 텍스트값을 바인딩합니다.
ElementName=textbox 누구의 텍스트 값인지를 ElementName으로 지정해줍니다. textbox는 x:key로 지정된 Textbox 이름입니다.
Converter={StaticResource ResourceKey=isNullConverter} 해당 부분은 들어온 데이터값을 변환해서 Value랑 비교해주는 작업을 해줍니다.(내용을 다 적어서 그렇지 ResourceKey는 생략이 가능합니다.
Converter={StaticResource isNullConverter} 이런식으로 말이죠 )
이전에 봤던 아래 코드를 여기서 사용했습니다.

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

여기서 x:key값으로 이름을 지정해준다음 해당 방식으로 불러다 사용하면 됩니다.
 
 
이제는 Converter에서 사용했던 IsNullConverter를 보겠습니다.
코드 구성은 아래와 같이 구성됐습니다.

internal class IsNullConverter : IValueConverter
{
	public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
	{
		return ((string)value == "");
	}

	public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
	{
		throw new InvalidOperationException("IsNullConverter can only be used OneWay.");
	}
}

IValueConverter 라는 인터페이스를 사용하는데 UI요소의 값을 데이터로 변환하는데 사용됩니다.
해당 인터페이스에서는 Convert와 ConvertBack 두가지 메서드를 정의하고 있습니다.
 
Convert는 데이터 바인딩에서 UI요소로 데이터 값을 변환하는 메서드 입니다.(솔직히 뭔말인지 모르겠지만 값이 정상적으로 변경됐을때 작동돼서 값을 반환해줘서 이걸 Xaml코드에 트리거를 사용한 곳에서  Value에서 비교해서 트리거를 처리해주는걸로 확인하고있습니다. 해당부분은 잘모르겠지만 느낌적으로 하고있어요..)
 
ConvertBackConvert 와 반대로 UI요소를 데이터 소스로 되돌릴때 사용한다는데 아직 잘 사용해보지 않아서 모르겠습니다.(아시는분이 설명해주시면 정말 좋겠당~)
 
그래서 Converter를 사용해서 트리거를 내가 원하는 값으로 비교를시켜서 정상적으로 작동시켜 TextBox에 값이 있을경우에는 False를 전달해주고, 없을경우에는 True를 전달시켜줍니다.
 
이제 값을 전달 받는 부분까지 확인을 했으니 해당 트리거를 작동시켜 애니메이션 처리하겠습니다.
아래는 DataTrigger 부분입니다.

<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>

Converter로 데이터를 처리하여 Value값이 False때는 TextBox에 값이 있으닌깐 TextBlock을 상단으로 옮기면서 동시에 색상을 변경해줍니다.
 
그전에 봐야할 내용들이 있습니다.

<DataTrigger.EnterActions>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
</DataTrigger.ExitActions>

EnterActions과 ExitActions 두가지가 존재하는데 기본적으로 컴퓨터 언어를 아시는분은
If else로 보시면 됩니다.

bool Check = False; //TextBox값이 빈칸일경우
if(Check == False){
	//여기 부분이 EnterActions
}
else{
	//여기 부분이 ExitActions
}

위와 같이 처리를 할 수 있기때문에 텍스트가 들어왔을때 애니메이션 하나를 처리해주고
텍스트가 없을때 애니메이션 처리를 해주는 부분을 각각 만들어서 넣어주면 애니메이션 처리가 됩니다.
 
 
아래 코드는 애니메이션을 처리하는 코드입니다.

<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>

<BeginStoryboard> 는 특정 상태에 따라 애니메이션을 시작하거나 중지하고싶을때 사용할때 유용하답니다!
 
그거에 대한 애니메이션은 
<Storyboard> 에 작성을 하기 시작하는데
애니메이션 값을처리하는데 있어서
OOOOAnimation으로 여러가지 태그값들이 있습니다
현재 사용한건 DoubleAnimation과 ColorAnimation을 사용했으니 추측을 해본다면
DoubleAnimation은 double값을 사용하겠고 ColorAnimation은 색상을 변경하는 애니메이션으로 사용하겠죠?
 
Storyboard.TargetProperty="(TextBlock.RenderTransform).(TranslateTransform.X)"  여기를 보시면 이상하게 길고 낯설게 치기싫은 문자들이 있는데 쳐줘야하더라고요...
근데 해당값은 우리가 전에 봤던애들입니다!

<TextBlock.RenderTransform>
	<TranslateTransform X="0" Y="0"/>
</TextBlock.RenderTransform>

애니메이션을 처리하겠다고 넣어준 RenderTransform값을 사용해서 X, Y값을 변경해줬던겁니다.
이러한 방식으로 내가 바꿔줄 속성을 지정해주고
To="-4" -4까지 줄여라
Duration="0:0:0.3" 시간은 0시 0분 0.3초 시간동안 처리해라
라고 코드를 설명 할 수 있습니다.
물론 To(도착지) 말고도 From(시작지)도 지정해 줄 수 있습니다.
 
마지막으로 해당 커스텀 컨트롤을 등록시켜야 합니다
App.xaml이라는 파일을 열어보시면 아래와 같이 코드가 존재하는데

<Application x:Class="WpfApp1.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfApp1"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
    	<!-- 이렇게 리소스사전을 등록하실수 있습니다. -->
        <!-- 여기서 각종 값을들 등록하셔서 StaticResouce를 사용하셔서 가져오시면 됩니다 -->
        <ResourceDictionary Source="Themas/CustomControls/CustomTextBox.xaml"/>
    </Application.Resources>
</Application>

위와같이 App.xaml에 내가 리소스사전을 등록해주셔야 사용이 가능합니다.
 

반응형