반응형
BindableProperty 를 Override 해보자
Xamarin 으로 View 를 개발하다보면 재활용성을 높이기 위해 기존의 View 를 상속받아서 개발하게 됩니다. 특히 특정 스타일의 View 를 반복적으로 사용할 때 아래와 같이 작성하게 됩니다.
public class MyFrame : Frame
{
private readonly Label _title;
public string Title { set => _title.Text = value; }
public MyFrame()
{
BackgroundColor = Color.LightPink;
_title = new Label
{
TextColor = Color.Black,
FontSize = 16,
};
Content = _title;
}
}
위에서 만든 MyFrame
을 아래와 같이 사용하게 됩니다.
<ContentPage>
<ContentPage.Resources>
<Style x:Key="Style" TargetType="local:MyFrame">
<Setter Property="HorizontalOptions" Value="FillAndExpand" />
<Setter Property="BackgroundColor" Value="LimeGreen" />
</Style>
</ContentPage.Resources>
<ContentPage.Content>
<local:MyFrame Style="{StaticResource Style}"
Title="Hello, World!" />
</ContentPage.Content>
</ContentPage>
이렇게 개발한 프로젝트를 실행하면 개발자는 당연히 MyFrame
의 BackgroundColor 가 LimeGreen 색으로 나타나길 기대하게 됩니다. 하지만 실제 결과물은 아래와 같은 LightPink 색으로 나오게 됩니다.
이처럼 Frame
를 상속받아서 개발한 MyFrame
에 Style 이 적용되지 않는 황당함을 경험하게 됩니다. 이러한 예상 외의 결과가 나타나는 이유는 Style 의 Setter 보다 MyFrame
의 생성자 메서드에서 설정한 BackgroundColor = Color.LightPink;
의 우선순위가 더 높기 때문에 발생하는 문제입니다. 이를 XAML 로 표현하면 아래와 같이 작성한 것과 동일하기 때문입니다.
<ContentPage>
<ContentPage.Resources>
<Style x:Key="Style" TargetType="local:MyFrame">
<Setter Property="HorizontalOptions" Value="FillAndExpand" />
<Setter Property="BackgroundColor" Value="LimeGreen" />
</Style>
</ContentPage.Resources>
<ContentPage.Content>
<local:MyFrame Style="{StaticResource Style}"
BackgroundColor="LightPink"
Title="Hello, World!" />
</ContentPage.Content>
</ContentPage>
이러한 현상을 해결하려면 MyFrame
에 BackgroundColor
라는 동일한 이름으로 기존의 BindableProperty 를 덮는 방법이 제일 무난합니다. 그래서 저는 Xamarin 소스 코드를 참고해서 다음과 같은 Extensions 메서드 코드를 추가해서 활용했습니다.
public static class BindablePropertyExtensions
{
// BindableProperty 의 메서드를 확장하여 기존 BindableProperty 속성을 덮어쓸 수 있는 Override 메서드.
public static BindableProperty Override(
this BindableProperty bindableProperty,
string propertyName = null,
Type returnType = null,
Type declaringType = null,
object defaultValue = null,
BindingMode defaultBindingMode = BindingMode.OneWay,
BindableProperty.ValidateValueDelegate validateValue = null,
BindableProperty.BindingPropertyChangedDelegate propertyChanged = null,
BindableProperty.BindingPropertyChangingDelegate propertyChanging = null,
BindableProperty.CoerceValueDelegate coerceValue = null,
BindableProperty.CreateDefaultValueDelegate defaultValueCreator = null)
{
if (string.IsNullOrEmpty(propertyName))
propertyName = bindableProperty.PropertyName;
if (returnType == null)
returnType = bindableProperty.ReturnType;
if (declaringType == null)
declaringType = bindableProperty.DeclaringType;
if (defaultValue == null)
defaultValue = bindableProperty.DefaultValue;
if (propertyChanged == null)
propertyChanged = (bindable, oldValue, newValue) =>
{
// 기존 BindableProperty 속성과 연동하기 위해 새로운 속성이 바뀌면 기존 속성도 변경되도록 했다.
var property = bindableProperty.DeclaringType.GetProperty(bindableProperty.PropertyName);
property.SetValue(bindable, newValue);
};
return BindableProperty.Create(propertyName, returnType, declaringType, defaultValue, defaultBindingMode,
validateValue, propertyChanged, propertyChanging, coerceValue, defaultValueCreator);
}
}
위 코드를 사용해서 MyFrame
을 수정하면 MainPage 에서 설정한 Style 이 정상적으로 동작하는 걸 확인하실 수 있습니다.
public class MyFrame : Frame
{
// VisualElement 에 정의되어 있던 BackgroundColorProperty 를 덮어쓴다.
public static new readonly BindableProperty BackgroundColorProperty = VisualElement.BackgroundColorProperty.Override();
private readonly Label _title;
public string Title { set => _title.Text = value; }
public MyFrame()
{
BackgroundColor = Color.LightPink;
_title = new Label
{
TextColor = Color.Black,
FontSize = 16,
};
Content = _title;
}
}
반응형
'Develop > MAUI 가이드' 카테고리의 다른 글
[Xamarin] Renderer 를 만들었더니 BackgroundColor 가 적용되지 않을 때 해결하는 방법 (0) | 2021.03.25 |
---|---|
[Xamarin] View 안에 View 를 주입할 수 있는 View 를 작성해보자 (0) | 2020.12.24 |
[Xamarin] Item 이 Layout 에 따라 자동으로 배치되도록 해보자 (0) | 2020.12.24 |
[Xamarin] 화면 크기에 따라 변화하는 ResponsiveLayout 을 만들어 보자 (0) | 2020.12.23 |
[Xamarin] BindableProperty 구현하고 관리하는 방법 (0) | 2020.12.17 |
꾸준히 노력하는 개발자 "김예건" 입니다.