티스토리 뷰

반응형

GridView, ListView에 있는 SelectedItems 프로퍼티는 바인딩이 되지 않는다. 그래서, 바인딩 가능한 Behavior를 만들어 보았다.

Windows 8.1 Moden App에서 테스트를 했는데, Windwos 8에서도 가능하리라 생각된다.

 

참고 포스트

Behaviors in Windows 8.1 Store Apps

http://www.julmar.com/blog/programming/behaviors-in-windows-8-1-store-apps/

 

1. Behavior(08-05-2015 update)

 

 

public class SelectedItemsBehavior : DependencyObject, IBehavior

{

 

/// <summary>

 

/// 비헤이비어에 붙어있는 프로퍼티

 

/// </summary>

 

public static readonly DependencyProperty SelectedItemsProperty =

 

DependencyProperty.Register("SelectedItems", typeof (object), typeof (SelectedItemsBehavior),

 

new PropertyMetadata(null, OnSelectedItemsChanged));

 

/// <summary>

 

/// 이벤트 중복 방지

 

/// </summary>

 

private readonly object _isLock = new object();

 

/// <summary>

 

/// IsBusy

 

/// </summary>

 

private bool _isBusy;

 

/// <summary>

 

/// 뷰 모델의 프로퍼티 - 반드시 object로 만든다. 다른형은 프로퍼티 추가가 않된다.

 

/// </summary>

 

public object SelectedItems

{

 

get { return GetValue(SelectedItemsProperty); }

 

set { SetValue(SelectedItemsProperty, value); }

}

 

/// <summary>

 

/// 연결되어있는 오브젝트

 

/// </summary>

 

public DependencyObject AssociatedObject { get; private set; }

 

/// <summary>

 

/// Listview에 연결 될때 실행되는 메소드

 

/// </summary>

 

/// <param name="associatedObject"></param>

 

public void Attach(DependencyObject associatedObject)

{

 

if ((associatedObject != AssociatedObject) && !DesignMode.DesignModeEnabled)

{

 

if (AssociatedObject != null)

 

throw new InvalidOperationException("Cannot attach behavior multiple times.");

AssociatedObject

= associatedObject;

 

var control = AssociatedObject as ListViewBase;

 

if (control == null)

{

 

throw new InvalidOperationException("Cannot attach behavior you must have ListViewBase.");

}

 

//SelectedItems가 Runtime Object임

control

.SelectionChanged += control_SelectionChanged;

}

}

 

/// <summary>

 

/// ListView 연결 해제될때 실행되는 메소드

 

/// </summary>

 

public void Detach()

{

 

if (SelectedItems != null)

{

((

INotifyCollectionChanged) SelectedItems).CollectionChanged -= ViewModelToControl_CollectionChanged;

}

 

if (AssociatedObject != null)

{

((

ListViewBase) AssociatedObject).SelectionChanged -= control_SelectionChanged;

}

AssociatedObject

= null;

}

 

/// <summary>

 

/// 컨트롤에 변경 사항을 뷰모델로 반영

 

/// </summary>

 

/// <param name="sender"></param>

 

/// <param name="e"></param>

 

private void control_SelectionChanged(object sender, SelectionChangedEventArgs e)

{

 

lock (_isLock)

{

 

if (_isBusy) return;

_isBusy

= true;

}

 

var control = AssociatedObject as ListViewBase;

 

if (control == null) return;

 

var list = SelectedItems as IList;

 

if (list == null) return;

 

if (e.AddedItems != null && e.AddedItems.Any())

{

 

foreach (var addedItem in e.AddedItems)

{

 

var existIndex = list.IndexOf(addedItem);

 

if (existIndex == -1)

list

.Add(addedItem);

}

}

 

if (e.RemovedItems != null && e.RemovedItems.Any())

{

 

foreach (var removedItem in e.RemovedItems)

{

 

var existIndex = list.IndexOf(removedItem);

 

if (existIndex > -1)

list

.Remove(removedItem);

}

}

_isBusy

= false;

}

 

/// <summary>

 

/// 비헤이비어 프로퍼티 체인지 이벤트

 

/// </summary>

 

private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)

{

 

var source = (SelectedItemsBehavior) d;

 

//이전 프로퍼티에 붙어있던 이벤트 제거

 

var oldCollection = e.OldValue as INotifyCollectionChanged;

 

if (oldCollection != null)

{

oldCollection

.CollectionChanged -= source.ViewModelToControl_CollectionChanged;

}

 

//새 프로퍼티에 이벤트 연결

 

var newCollection = e.NewValue as INotifyCollectionChanged;

 

if (newCollection != null)

{

newCollection

.CollectionChanged += source.ViewModelToControl_CollectionChanged;

}

}

 

/// <summary>

 

/// 뷰모델의 프로퍼티에서 발생한 이벤트를 이용해서 컨트롤에 작업

 

/// </summary>

 

private void ViewModelToControl_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)

{

 

lock (_isLock)

{

 

if (_isBusy) return;

_isBusy

= true;

}

 

var control = AssociatedObject as ListViewBase;

 

//Reset처리

 

if (e.Action == NotifyCollectionChangedAction.Reset)

{

 

if (control == null) return;

control

.SelectedItems.Clear();

}

 

//삭제된 아이템 처리

 

if (e.OldItems != null)

{

 

foreach (var item in e.OldItems)

{

 

if (control != null && control.SelectedItems.Contains(item))

{

control

.SelectedItems.Remove(item);

}

}

}

 

//추가된 아이템 처리

 

if (e.NewItems != null)

{

 

try

{

 

foreach (var item in e.NewItems)

{

 

if (control != null && control.SelectedItems.Contains(item) == false)

{

control

.SelectedItems.Add(item);

}

}

}

 

catch (OutOfMemoryException ome)

{

 

EtcHelper.Instance.MsgBox(ome.Message);

}

 

catch (Exception ex)

{

 

Debug.Assert(false, ex.Message);

}

}

_isBusy

= false;

}

}

 

 

2. XAML

 

            <ListView x:Name="listView" Grid.Row="1" ItemsPanel="{StaticResource HItemsPanelTemplate}" SelectionMode="Multiple"
       ItemsSource="{Binding SelectedImage.ItemImages}" >
             <Interactivity:Interaction.Behaviors>
              <Behaviors:SelectedItemsBehavior SelectedItems="{Binding SelectedItemImages}"/>
             </Interactivity:Interaction.Behaviors>

    <ListView.ItemTemplate>
     <DataTemplate>
      <Grid>
       <Image Source="{Binding WorkedImage}"/>
       <StackPanel VerticalAlignment="Bottom" Background="{StaticResource DefaultImageTextBackgroundBrush}" >
        <TextBlock Style="{StaticResource DetailTitleStyle12}" Margin="5,10,0,5">
            <Run Text="{Binding FileName}"/>
            <Run Text="{Binding ExtName}"/>
        </TextBlock>
       </StackPanel>
      </Grid>
     </DataTemplate>
    </ListView.ItemTemplate>
   </ListView>


 

3. ViewModel

 

        private ICollection<FileInfoM> _selectedItemImages;
        /// <summary>
        /// 선택된 아이템들
        /// </summary>
        public ICollection<FileInfoM> SelectedItemImages
        {
            get { return _selectedItemImages; }
            set { _selectedItemImages = value; OnPropertyChanged(); }
        }

 

...

생성자에서 인스턴스

SelectedItemImages = new ObservableCollection<FileInfoM>();

...

 

        private DelegateCommand _selectAllCommand;
        /// <summary>
        /// Select All Command
        /// </summary>
        public DelegateCommand SelectAllCommand
        {
            get
            {
                return _selectAllCommand = _selectAllCommand ?? new DelegateCommand(
                args =>
                {
                    if (SelectedItemImages.Count != SelectedImage.ItemImages.Count)
                    {
                        foreach (var item in SelectedImage.ItemImages)
                        {
                            SelectedItemImages.Add(item);
                        }
                    }
                    else
                    {
                        foreach (var item in SelectedImage.ItemImages)
                        {
                            SelectedItemImages.Remove(item);
                        }
                    }
                });
            }
        }

 

4. 작업 화면

 

SelectAll 버튼을 클릭해서 하단의 2개 아이템 선택

 

전체 선택이 된 상태에서 다시 하번 누르면 선택 해제

 

5. 앱 개발이 마무리 단계라 소스를 올리지 못했는데, 나중에 요청이 있으면 간단한 셈플을 만들어서 올리도록 하겠다. 실버라이트용 Behavior는 약간 다르지만 기본적인 내용은 동일하니 참고해서 만들면 된다.

 

반응형
댓글