반응형

BindableProperty 구현하고 관리하는 방법
마이크로소프트 공식 문서 에서 BindableProperty 를 구현하는 방법을 소개하고 있습니다. 다만 아쉽게도 공식문서엔 BindalbeProperty 코드를 재사용하거나 관리하는 측면에 대한 디테일한 설명이 없어서 글을 작성하게 되었습니다.
먼저 공식 문서대로 하면 BindableProperty 는 기본적으로 아래와 같이 구현하게 됩니다.
public class MyContentView : ContentView
{
public static readonly BindableProperty NameProperty =
BindableProperty.Create ("Name", typeof(string), typeof(MyContentView), null);
public string Name
{
get { return (string)GetValue (NameProperty); }
set { SetValue (NameProperty, value); }
}
}
공식 문서와 달리 개인적으로 아래와 같이 구현하는 게 저에게는 더 가독성이 좋습니다.
public class MyContentView : ContentView
{
public static readonly BindableProperty NameProperty = BindableProperty.Create(
propertyName: nameof(Name),
returnType: typeof(string),
declaringType: typeof(MyContentView),
defaultValue: null,
defaultBindingMode: BindingMode.OneWay);
public string Name
{
get => (string)GetValue(NameProperty);
set => SetValue(NameProperty, value);
}
}
BindableProperty.Create메서드에 입력해야 할 속성이 많다보니 각 속성의 의미가 무엇인지 순서를 외우지 못했다면 위와 같이 작성해야 다른 개발자가 봐도 이해할 수 있습니다.propertyName에 단순히"Name"이라 적기보다는nameof를 활용하면 속성 이름을 변경하면 연동되서 편리합니다.defaultBindingMode를 지정하면 Xaml 파일에서 Mode 를 지정하는 코드를 줄일 수 있고,Command와 같이 한번 바인딩하면 변경하지 않는 속성의 경우BindingMode.OneTime을 적용해서 최적화할 수도 있습니다. 다만 다른 개발자에게 소스코드가 공개되지 않는 View 라이브러리일 경우, 기본 바인딩을BindingMode.OneWay이라고 가정하기 때문에 변경하지 않는걸 추천합니다.
공식문서를 따라하면 BindableProperty 가 변경되었을 때 대응하는 코드는 아래와 같이 구현하게 됩니다.
public class MyContentView : ContentView
{
public static readonly BindableProperty NameProperty =
BindableProperty.Create (
"Name", typeof(string), typeof(MyContentView), null, propertyChanged: OnNameChanged);
// ...
public string Name
{
get => (string)GetValue(NameProperty);
set => SetValue(NameProperty, value);
}
// ...
static void OnNameChanged (BindableObject bindable, object oldValue, object newValue)
{
// Property changed implementation goes here
}
}
저는 아래와 같이 구현하는게 가독성이 좋고 관리하기 편합니다.
public class MyContentView : ContentView
{
#region Name
public static readonly BindableProperty NameProperty = BindableProperty.Create(
propertyName: nameof(Name),
returnType: typeof(string),
declaringType: typeof(MyContentView),
defaultValue: null,
defaultBindingMode: BindingMode.OneWay,
propertyChanged: (bindable, oldValue, newValue) =>
{
// 1번) 기본적으로 잘못 호출할 일이 없도록 여기서 전부 해결한다.
if(bindable is MyContentView view && newValue is string newName)
{
// Name 속성 변경에 대응하는 코드 ...
}
// 2번) 하지만 재사용이 가능한 코드로 해결하고 싶다면 아래와 같이 구현한다.
(view as MyContentView).OnNameChanged((string)newValue);
});
// 2번) 재사용할 메서드
private void OnNameChanged(string newName)
{
// Name 속성 변경에 대응하는 코드 ...
}
public string Name
{
get => (string)GetValue(NameProperty);
set => SetValue(NameProperty, value);
}
#endregion
}
- 굳이 재사용하지 않을
static메서드를 만들 필요는 없다고 생각하기도 하고, 만약 다른 개발자가static메서드를 호출하면 버그가 될 수도 있어서, 저는BindableProperty내에 메서드를 정의합니다. 하지만 재사용해야 한다면 2번과 같이 구현하기도 합니다. - 공식 문서나 샘플 코드 또는 Xamarin 소스 코드에서는
BindableProperty와 연관된 코드들의 위치를 클래스에서 흩어 놓았는데, 저는 한 위치에 모아서#region을 지정해 관리하는 걸 선호하는 편입니다. 속성과 관련하여 수정할 일이 있을 때 다같이 수정해야 하는데 위치가 흩어져 있으면 찾아서 수정해야 하는 번거로움을 해결할 수 있어서 모아 놓습니다.
BindableProperty 재사용 패턴 (IElement 패턴)
신기하게도 Xamarin 소스코드에서는 열심히 사용하는 패턴인데 공식문서에서는 언급조차 하지 않는 패턴입니다. 이유는 알 수 없지만 코드를 특정한 방식으로 사용하라고 공식 문서에 남기지 않기 위해 설명을 하지 않은 걸까 추측하고 있습니다.
BindableProperty는 특이한 방법으로 구현하는 속성이다 보니 재사용은 아래와 같이 구현해야 합니다. 예제로는 MVVM 패턴에서 빠질 수 없는 Command 를 재사용 가능한 BindableProperty 로 구현하는 방법을 보여 드리겠습니다.
1. ICommandElement 인터페이스 정의
[EditorBrowsable(EditorBrowsableState.Never)]
public interface ICommandElement
{
ICommand Command { get; set; }
void OnCommandChanged(Command newValue);
object CommandParameter { get; set; }
}
2. CommandElement static 클래스 정의
static class CommandElement
{
public static readonly BindableProperty CommandProperty
= BindableProperty.Create(
propertyName: nameof(ICommandElement.Command),
returnType: typeof(ICommand),
declaringType: typeof(ICommandElement),
defaultValue: null,
defaultBindingMode: BindingMode.OneTime,
propertyChanged: );
private static void OnCommandChanged(BindableObject bindable, object oldValue, object newValue)
=> (bindable as ICommandElement).OnCommandChanged((ICommand)newValue);
public static readonly BindableProperty CommandParameterProperty
= BindableProperty.Create(
propertyName: nameof(ICommandElement.CommandParameter),
returnType: typeof(object),
declaringType: typeof(ICommandElement),
defaultValue: null,
defaultBindingMode: BindingMode.OneTime);
}
3. ICommandElement 와 CommandElement 를 활용해 BindableProperty 재사용
public class MyContentView : ContentView, ICommandElement
{
public static readonly BindableProperty CommandProperty = CommandElement.CommandProperty;
public ICommand Command
{
get => (ICommand)GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}
public static readonly BindableProperty CommandParameterProperty = CommandElement.CommandParameterProperty;
public object CommandParameter
{
get => GetValue(CommandParameterProperty);
set => SetValue(CommandParameterProperty, value);
}
}반응형
'Develop > MAUI 가이드' 카테고리의 다른 글
| [Xamarin] Item 이 Layout 에 따라 자동으로 배치되도록 해보자 (0) | 2020.12.24 |
|---|---|
| [Xamarin] 화면 크기에 따라 변화하는 ResponsiveLayout 을 만들어 보자 (0) | 2020.12.23 |
| [Xamarin] Effects 로 Platform-Specific 하게 구현하기 (0) | 2020.11.17 |
| [Xamarin] Custom Renderer 로 Platform-Specific 하게 구현하기 (0) | 2020.09.29 |
| [Xamarin] 픽셀 및 기기 독립 Unit (Device-Independent Units) (0) | 2020.08.21 |
꾸준히 노력하는 개발자 "김예건" 입니다.