본문 바로가기

MAUI CommandParameter 전달 실패 문제 해결, 구조적 원인 분석과 실전 대응 전략

@veedeeo 2025. 12. 10. 22:36
MAUI MVVM, Command는 실행되는데 CommandParameter만 null로 전달되는 이유

MAUI MVVM, Command는 실행되는데 CommandParameter만 null로 전달되는 이유

MAUI MVVM 구조에서 가장 많이 보고되는 문제 중 하나가 Command는 실행되지만 CommandParameter가 null로 전달되는 현상이다. 버튼을 눌렀을 때 커맨드는 호출되지만, 정작 ViewModel에서 필요한 값이 넘어오지 않아 이벤트 처리가 불완전해지거나, CollectionView의 항목 삭제, 편집 기능이 정상 동작하지 않는 오류로 이어진다.

이 문제는 단순한 구문 실수가 아니라, BindingContext 전파 규칙, DataTemplate 특성, x:Reference 타이밍, 셀 재활용 구조, 뷰 계층 구조의 실제 동작 방식이 얽힌 결과다. 따라서 CommandParameter 문제는 초보자뿐만 아니라 실무 개발자에게도 빈번하게 발생하는 구조적 오류다.

이 글에서는 CommandParameter가 전달되지 않는 이유를 4가지 구조적 원인으로 분석하고, 실전에서 안전하게 작동하도록 만드는 설계 전략과 코드 패턴을 제시한다.

Digital vector diagram of CollectionView conflict


CollectionView ItemTemplate에서 Command는 호출되는데 CommandParameter만 null로 들어오는 경우, 대부분은 DataTemplate 내부 BindingContext가 ViewModel이 아니라 Item이라는 사실을 놓치거나, x:Reference가 참조 시점과 이름에서 실패하거나, Template 내부 Binding이 특정 뷰 계층에서 다르게 평가되거나, 셀 재활용 타이밍으로 Parameter가 바뀌는 문제로 귀결된다.

따라서 안전한 기본 해법은 Command는 페이지 전체 BindingContext에서 가져오고, CommandParameter는 모델 전체를 전달하며, 페이지에는 x:Name을 명확히 지정하는 구조를 기본값으로 삼는 것이다.


CommandParameter 전달 실패가 발생하는 핵심 원인 분석

원인 1, DataTemplate 내부에서는 BindingContext가 ViewModel이 아니다

다음 코드를 보자.

<Button
    Text="삭제"
    Command="{Binding DeleteCommand}"
    CommandParameter="{Binding ID}" />

이 코드를 CollectionView ItemTemplate 내부에 넣으면 많은 개발자가 다음과 같이 생각한다.

  • DeleteCommand는 ViewModel의 명령을 바인딩한 것이다
  • CommandParameter는 해당 항목의 ID를 전달한다

하지만 실제로는 완전히 다르다.

ItemTemplate 내부의 BindingContext는 ViewModel이 아니라 각 항목 모델, Item이다.

따라서 DeleteCommand는 Item 클래스에서 찾게 되며, 물론 그런 속성은 존재하지 않는다.

이때 발생하는 로그는 다음과 같다.

Binding: 'DeleteCommand' property not found on MyItem

하지만 UI는 조용히 실패하므로, 개발자는 아무 것도 알지 못한다.


원인 2, x:Reference 이름이 렌더링 타이밍과 맞지 않을 때

많은 개발자가 이 문제를 해결하려고 다음 코드를 사용한다.

<Button
    Command="{Binding BindingContext.DeleteCommand, Source={x:Reference MainPage}}"
    CommandParameter="{Binding .}" />

그러나 x:Reference의 참조 시점이 렌더링보다 늦거나, 페이지 이름이 실제 인스턴스와 일치하지 않으면 Source가 null이 되어 바인딩이 실패한다.

MAUI 로그에는 다음과 같은 메시지가 출력된다.

Binding: Cannot find reference 'MainPage'

특히 Shell이나 NavigationPage 환경에서는 페이지 이름이 개발자가 예상하는 이름과 다를 수 있으므로 문제가 더 자주 발생한다.


원인 3, CommandParameter가 단순한 문자열이 아닌 경우 바인딩 실패

다음과 같은 코드는 정상적으로 작동한다.

CommandParameter="123"

그러나 다음 코드는 실패할 수 있다.

CommandParameter="{Binding}"

또는

CommandParameter="{Binding Source={x:Reference Root}, Path=SelectedItem}"

이유는 두 가지이다.

  1. BindingContext가 없는 요소에서 Binding을 사용했다
  2. DataTemplate 내부의 Binding 점이 현재 셀 모델을 가리키지 못했다

특히 CollectionView에서 Template 내 점 바인딩은 정상적이지만, ContentView, ControlTemplate, Popup 내부에서는 다르게 동작한다.


원인 4, CommandParameter 전달 시 셀 재활용이 끼어드는 문제

마지막 주요 원인은 셀 재활용 과정에서 Parameter가 예상하지 못한 시점에 바뀌는 현상이다.

  • 셀 3개가 화면에 표시됨
  • 스크롤하여 셀이 재활용됨
  • CommandParameter로 바인딩된 값이 이전 모델의 값을 유지하거나, 아직 업데이트되지 않은 상태를 전달

이 때문에 가끔 null이 전달된다 형태의 난해한 버그로 나타난다.

MAUI Button CommandParameter Flowchart


실전에서 발생하는 대표 코드 패턴 3가지

문제 코드 1, ItemTemplate 내부에서 직접 Command 바인딩

<Button Command="{Binding DeleteCommand}" />

ItemTemplate 내부 BindingContext는 Item이므로 실패한다.


문제 코드 2, x:Reference 이름 불일치

<ContentPage x:Name="RootPage">

<Button
    Command="{Binding BindingContext.DeleteCommand, Source={x:Reference MainPage}}" />

RootPage인데 MainPage로 작성하는 실수가 자주 발생한다.


문제 코드 3, CommandParameter 바인딩 불안정

CommandParameter="{Binding ID}"

셀 재활용 타이밍에 따라 바인딩이 늦게 반영될 수 있어, 커맨드 호출 시 null이 전달될 위험이 있다.


CommandParameter를 안정적으로 전달하는 정석 구조

정석 1, Command는 ViewModel에서 가져오고

<Button
    Text="삭제"
    Command="{Binding BindingContext.DeleteCommand, Source={x:Reference Root}}"
    CommandParameter="{Binding .}" />

여기서

  • Source는 페이지 전체의 BindingContext, ViewModel
  • 점 바인딩은 현재 모델, Item 객체를 통째로 전달

이 조합이 가장 흔하게 쓰이는 안정적인 구조다.


정석 2, 부모 페이지에서 x:Name을 반드시 지정해야 한다

<ContentPage x:Name="Root">

Shell 환경에서도 정상적으로 동작한다.


정석 3, CommandParameter에는 모델 전체를 넘기는 것이 안정적

개별 속성, ID만 넘기는 것보다 모델 전체를 넘기는 것이 셀 재활용 문제를 해결하는 데 더 도움이 된다.

ViewModel에서는 다음처럼 처리한다.

public ICommand DeleteCommand => new Command<MyItem>(Delete);

private void Delete(MyItem item)
{
    Items.Remove(item);
}

ID만 넘기면 셀 재활용 타이밍 문제로 null이나 이전 값이 전달될 수 있다.


정석 4, CollectionView에서 SelectionMode를 None으로 두면 Parameter 문제도 줄어든다

SelectionMode가 켜져 있으면 선택 상태 갱신이 UI 렌더링 순서를 흔들어 CommandParameter 바인딩에도 영향을 준다.

따라서 다음 설정이 가장 안정적이다.

SelectionMode="None"

마무리, Command 구조를 이해하면 MAUI UI 이벤트 문제의 80퍼센트가 해결된다

CommandParameter 전달 실패는 단순한 바인딩 문법 오류가 아니라 MAUI UI 구조의 복잡한 동작 원리와 밀접하게 연결된 문제다.

이 글에서 다룬 원리들을 정리하면 다음과 같다.

  • ItemTemplate 내부에서는 BindingContext가 ViewModel이 아님
  • Command는 ViewModel에서 가져오고
  • Parameter는 모델 전체, 점 바인딩을 넘기는 것이 가장 안정적
  • x:Reference는 페이지 이름과 생성 타이밍이 중요
  • 셀 재활용은 항상 Parameter 불일치 문제를 일으킬 수 있음

앞선 글에서 다룬 CheckBox 문제, ObservableCollection 문제, SelectionMode 충돌 문제, BindingContext 꼬임 문제와 모두 연결되는 큰 흐름 속에서, CommandParameter 문제는 마지막 퍼즐 조각과 같다.

다음 글에서는 MAUI에서 Shell Navigation 파라미터가 전달되지 않는 문제를 다룰 수 있다.

veedeeo
@veedeeo :: .net MAUI·Netlify·SEO·웹 최적화 기술 블로그

.net MAUI·Netlify·SEO·웹 최적화 기술 블로그

공감하셨다면 ❤️ 구독도 환영합니다! 🤗

목차