반응형
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 |
꾸준히 노력하는 개발자 "김예건" 입니다.