wtorek, 29 listopada 2011

State-Based Navigation w Prism - interakcja z użytkownikiem

To już ostatni post związany z State-Based Navigation w Prism. W tym poście omówię w jaki sposób prowadzić interakcję z wcześniej stworzoną aplikacją.
Jak już wcześniej wspominałem State-Based Navigation nie nadaje się do skomplikowanych scenariuszy interakcji. Do tego celu służy View Based Navigation, któremu poświęcę kolejne posty.
Naszym zadaniem będzie napisanie prostego okienka umożliwiającego edycję danych zaznaczonej osoby.
Przykład jest bardzo prosty i obrazuje prostą czynność jaką jest edycja w kolekcji, zapis do bazy danych zostawiam dla Was do implementacji :). Aby nie komplikować zbytnio przykładu nie tworzę tutaj żadnego serwisu odpowiedzialnego za okna dialogowe, a użyję toolkitowego ChildWindow.

Najpierw XAML


Code:
<UserControl x:Class="CustomerModule.CustomerListView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:extToolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit/extended"
             mc:Ignorable="d" >
    <UserControl.Resources>
        <DataTemplate x:Key="ItemStandardTemplate">
            <StackPanel>
                <TextBlock>
                    <TextBlock.Text>
                        <MultiBinding StringFormat="{}{0} {1}" >
                            <Binding Path="FirstName" />
                            <Binding Path="LastName" />
                        </MultiBinding>
                    </TextBlock.Text>
                </TextBlock>
            </StackPanel>
        </DataTemplate>
        
        <DataTemplate x:Key="ItemSpecialTemplate">
            <StackPanel Margin="5">
                <Image Source="Images/Person.png" Width="30" Height="30" />
                <TextBlock Text="{Binding EmailAddress}" VerticalAlignment="Center" />
                <TextBlock VerticalAlignment="Center">
                    <TextBlock.Text>
                        <MultiBinding StringFormat="{}{0} {1}" >
                            <Binding Path="FirstName" />
                            <Binding Path="LastName" />
                        </MultiBinding>
                    </TextBlock.Text>
                </TextBlock>
            </StackPanel>
        </DataTemplate>
    </UserControl.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0">
            <ToggleButton Content="Zmień styl" Name="btnStyleIndicator" />
            <Button Content="Edytuj" Command="{Binding ShowWindow}" /> 
        </StackPanel>
        
        <extToolkit:BusyIndicator IsBusy="{Binding IsBusy}" BusyContent="Loading data..." Grid.Row="1">
            <ListBox ItemsSource="{Binding CustomerList, Mode=TwoWay}" SelectedItem="{Binding SelectedCustomer}">
                <ListBox.Style>
                    <Style TargetType="ListBox">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding ElementName=btnStyleIndicator, Path=IsChecked}" Value="True">
                                <Setter Property="ItemTemplate" Value="{StaticResource ItemSpecialTemplate}" />
                                <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
                                <Setter Property="ItemsPanel">
                                    <Setter.Value>
                                        <ItemsPanelTemplate>
                                            <WrapPanel />
                                        </ItemsPanelTemplate>
                                    </Setter.Value>
                                </Setter>
                            </DataTrigger>

                            <DataTrigger Binding="{Binding ElementName=btnStyleIndicator, Path=IsChecked}" Value="False">
                                <Setter Property="ItemTemplate" Value="{StaticResource ItemStandardTemplate}" />
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </ListBox.Style>
            </ListBox>
        </extToolkit:BusyIndicator>
        
        <extToolkit:ChildWindow IsModal="True" WindowState="{Binding WindowState}" WindowStartupLocation="Center">
            <Grid DataContext="{Binding SelectedCustomer}">
                <Grid.RowDefinitions>
                    <RowDefinition />
                    <RowDefinition />
                    <RowDefinition />
                </Grid.RowDefinitions>
                
                <Grid.ColumnDefinitions>
                    <ColumnDefinition />
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>
                
                <TextBlock Text="Imię:" Grid.Row="0" Grid.Column="0"/>
                <TextBox Text="{Binding FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Grid.Row="0" Grid.Column="1"/>

                <TextBlock Text="Nazwisko:" Grid.Row="1" Grid.Column="0"/>
                <TextBox Text="{Binding LastName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Grid.Row="1" Grid.Column="1"/>

                <TextBlock Text="Email:" Grid.Row="2" Grid.Column="0"/>
                <TextBox Text="{Binding EmailAddress, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Grid.Row="2" Grid.Column="1"/>
                
            </Grid>
        </extToolkit:ChildWindow> 
    </Grid>
</UserControl>

Nowe rzeczy które się tutaj pojawiły:
  • Dodanie przycisku edycji konkretnego klienta - bindowanie do komendy ShowWindow
  • Dodanie okienka prezentującego dane, oraz możliwość ich zmiany ChildWindow. 
Jak można zauważyć do okienka modyfikacji przekazywany jest aktualnie zaznaczony klient na liście.

ViewModel:


Code:
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using BusinessObjects;
using Infrastructure;
using Microsoft.Practices.Prism.Commands;
using Microsoft.Windows.Controls;
using Repositories.Customers;

namespace CustomerModule
{
    public class CustomerListViewModel : BaseViewModel
    {
        private readonly ICustomerRepository _customerRepository;

        private List<Customer> _customerList; 
        public List<Customer> CustomerList
        {
            get
            {
                IsBusy = true;
                if(_customerList != null)
                {
                    IsBusy = false;
                    return _customerList;
                }
                var loadCustomerTask = new Task(() =>
                                                    {
                                                        Thread.Sleep(5000);
                                                        CustomerList = _customerRepository.GetAllCustomers();
                                                        IsBusy = false;
                                                    });
                
                loadCustomerTask.Start();
                return _customerList;
            }
            set
            {
                _customerList = value;
                RaisePropertyChanged("CustomerList");
            }
        }

        private bool _isBusy;
        public bool IsBusy
        {
            get { return _isBusy; }
            set
            {
                _isBusy = value;
                RaisePropertyChanged("IsBusy");
            }
        }

        private Customer _selectedCustomer;
        public Customer SelectedCustomer
        {
            get { return _selectedCustomer; }
            set
            {
                _selectedCustomer = value;
                ShowWindow.RaiseCanExecuteChanged();
                RaisePropertyChanged("SelectedCustomer");
            }
        }

        private WindowState _windowState;
        public WindowState WindowState
        {
            get { return _windowState; }
            set
            {
                _windowState = value;
                RaisePropertyChanged("WindowState");
            }
        }

        public DelegateCommand ShowWindow { get; set; } 

        public CustomerListViewModel(ICustomerRepository customerRepository)
        {
            _customerRepository = customerRepository;
            ShowWindow = new DelegateCommand(() => WindowState = WindowState.Open, () => SelectedCustomer != null);
        }
    }
}

W ViewModelu dodane zostało obsłużenie komendy oraz właściwość która odpowiada za chowanie/pokazywanie okienka dialogowego do modyfikacji danych.

To ostatni posty z cyklu State-Based Navigation. Jest to najprostszy sposób nawigacji. W kolejnych postach zostanie omówiony View Based Navigation - znacznie bardziej złożony, ale dający przy tym dużo więcej możliwości.

Brak komentarzy:

Prześlij komentarz