티스토리 뷰

WPF .NET

Kiosk 만들기 - Part5

kaki104 2023. 10. 25. 10:00
반응형

메뉴를 선택해서 주문 목록체 추가하는 부분을 작업합니다.

1. SelectMenu.xaml

<ItemsControl.ItemTemplate>
    <DataTemplate>
        <Grid Margin="10">
            <b:Interaction.Triggers>
                <b:EventTrigger EventName="MouseDown">
                    <b:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType=ItemsControl}, Path=DataContext.SelectProductCommand}" CommandParameter="{Binding}" />
                </b:EventTrigger>
            </b:Interaction.Triggers>
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Image Source="{Binding ImageUri}" Stretch="UniformToFill" />
            <StackPanel Grid.Row="1">
                <TextBlock
                    HorizontalAlignment="Stretch"
                    Text="{Binding Name}"
                    TextAlignment="Center" />
                <TextBlock
                    HorizontalAlignment="Stretch"
                    Text="{Binding Price, StringFormat={}{0:N0}}"
                    TextAlignment="Center" />
            </StackPanel>
        </Grid>
    </DataTemplate>
</ItemsControl.ItemTemplate>

상품을 선택하면 뷰모델에 커맨드를 실행해서 주문을 추가해야 합니다.

1-1. EventTrigger를 이용해서 MouseDown 이벤트가 발생하면 InvokeCommandAction을 이용해서 뷰모델에 커맨드를 실행합니다.

<b:Interaction.Triggers>
    <b:EventTrigger EventName="MouseDown">
       <b:InvokeCommandAction />
    </b:EventTrigger>
</b:Interaction.Triggers>

1-2. 위의 코드는 DataTemplate에 내부에 있기 때문에, ViewModel을 찾기 위해서는 RelativeSource를 이용해야 합니다.

<b:InvokeCommandAction 
    Command="{Binding RelativeSource={RelativeSource AncestorType=ItemsControl}, 
                 Path=DataContext.SelectProductCommand}" 
    CommandParameter="{Binding}" />

* RelativeSource AncestorType=ItemsControl : 현재 VisualTree 위치에서부터 위로 올라가면서 처음 만나는 ItemsControl을 찾습니다.

* Path=DataContext.SelectProductCommand : ItemsControl을 찾으면 DataContext.SelectProductCommand를 실행합니다.

* ItemsControl의 DataContext에는 ViewModel이 들어가 있기 때문에 DataContext.SelectProductCommand라고 하면 내가 원하는 커맨드를 찾을 수 있기 때문입니다.

DataContext에 어떻게 ViewModel이 들어가는지 모르시는 경우 여기를 참고하시면 됩니다.

* CommandParameter="{Binding}" : 현재 위치가 DataTemplate이기 때문에 여기서 이야기하는 {Binding} 은 데이터를 가르키게 됩니다.

* 한글로 풀어서 이야기하면, 사용자가 마우스 다운 이벤트를 발생시키면, SelectProductCommand를 실행시키는데, 파라메터는 지금 표시하는 데이터를 가지고 가라~ 정도가 될 것 같습니다.

1-3. DataGrid

<DataGrid
    AutoGenerateColumns="False"
    IsReadOnly="True"
    ItemsSource="{Binding AppContext.CurrentOrder.Items}">
    <DataGrid.Columns>
        <DataGridTextColumn
            Width="200"
            Binding="{Binding ProductName}"
            Header="메뉴명" />
        <DataGridTextColumn
            Width="100"
            Binding="{Binding UnitPrice, StringFormat={}{0:N0}}"
            Header="단가"
            TextBlock.TextAlignment="Right" />
        <DataGridTextColumn
            Width="60"
            Binding="{Binding Quantity, StringFormat={}{0:N0}}"
            Header="수량" />
        <DataGridTextColumn
            Width="100"
            Binding="{Binding Amount, StringFormat={}{0:N0}}"
            Header="금액" />
    </DataGrid.Columns>
</DataGrid>

* ItemsSource="{Binding AppContext.CurrentOrder.Items}" : AppContext에 CurrentOrder가 현재 진행 중인 주문을 가리키고, 사용자가 메뉴를 추가하면 Items에 입력됩니다. 그 목록을 DataGrid에 출력합니다.

* 키오스크이다보니 Column에 Width를 고정값으로 입력했습니다.

* Binding="{Binding UnitPrice, StringFormat={}{0:N0}}" : 숫자형의 데이터를 출력할때 이렇게 사용하면 소숫점 없이 정수만 출력되며, 3자리마다 ,가 추가됩니다.

2. SelectMenuViewModel.cs

/// <summary>
/// 메뉴 선택 커맨드
/// </summary>
public ICommand SelectProductCommand { get; set; }

private void Init()
{
    //...
    SelectProductCommand = new DelegateCommand<Product>(OnSelectProduct);
    PaymentCommand = new DelegateCommand(OnPayment);
}

private void OnSelectProduct(Product product)
{
    AppContext.CurrentOrder.Items.Insert(0, 
        new OrderDetail 
        {
            OrderId = AppContext.CurrentOrder.OrderId,
            ProductName = product.Name,
            UnitPrice = product.Price,
            Quantity = 1,
            Amount = product.Price * 1
        });
    AppContext.CurrentOrder.UpdateProperties();
}

* SelectProductCommand를 실행할 때 CommandParameter를 이용하려면, 커맨드를 생성할 때 입력받는 데이터유형을 지정해야 합니다.

* 일반적으로는 string, object등을 사용하지만, 여기서는 데이터의 유형을 정확하게 알기 때문에 Product라고 지정을 했습니다.

* AppContext.CurrentOrder.Items.Insert : 신규 아이템을 목록에 추가합니다. 이 때 추가된 데이터가 화면에 바로바로 보이기 위해서는 ObservableCollection으로 생성되어 있어야 합니다.

/// <summary>
/// 아이템들
/// </summary>
public IList<OrderDetail> Items { get; set; } = new ObservableCollection<OrderDetail>();
주의!!!!
처음 생성을 ObservableCollection을 했지만, 다른 코드에서 Items = aaaList.ToList() 등의 코드를 이용해서 List 형으로 변경해서 입력하면, 더 이상 데이터의 추가/삭제 내용이 화면에 바로 반영되지 않습니다.
Items.Clear(), Items.Add(), Items.Insert(), Items.Remove() 등의 메서드를 이용해서만 작업해야 합니다.

3. Order.cs

/// <summary>
/// 프로퍼티 업데이트
/// </summary>
public void UpdateProperties()
{
    TotalQuantity = Items.Sum(x => x.Quantity);
    TotalAmount = Items.Sum(x => x.Amount);
}

주문을 추가한 후에 총 수량과 총 금액을 업데이트 하는 메서드를 추가했습니다.

이 부분도 Items 프로퍼티를 INotifyCollectionChanged 형으로 변경한 후 추가/삭제시 자동으로 금액을 계산할 수 있지만, 이벤트 핸들러를 추가하고 사용해야 하기 때문에 권장하지는 않습니다.

4. 소스

kaki104/PrismKiosk at Part5/select-menu-addorder (github.com)

 

GitHub - kaki104/PrismKiosk

Contribute to kaki104/PrismKiosk development by creating an account on GitHub.

github.com

 

반응형

'WPF .NET' 카테고리의 다른 글

Kiosk 만들기 - Part7  (0) 2023.10.30
Kiosk 만들기 - Part6  (0) 2023.10.27
Kiosk 만들기 - Part4  (2) 2023.10.23
Kiosk 만들기 - Part3  (0) 2023.10.20
Prism - ContentControl에 화면 생성해서 넣기 1/2  (2) 2023.10.19
댓글