読者です 読者をやめる 読者になる 読者になる

かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

UWPでListView/GridViewのItemのIsSelectedプロパティをバインディングする

WPFだとStyleにBinding書けばいけるんですけど、UWPだと出来ない!由々しき事態。ということでUWPだとどうやるかというとListViewとかを拡張してごにょごにょする必要があります。

stackoverflow.com

こんな感じでListViewを拡張します。ListViewItemが作られるときにさくっと手を入れてバインドを作る感じです。

using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;

namespace App66
{
    public class ListViewEx : ListView
    {
        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            base.PrepareContainerForItemOverride(element, item);
            var binding = new Binding();
            binding.Path = new PropertyPath("IsSelected");
            binding.Mode = BindingMode.TwoWay;
            binding.Source = item;
            ((ListViewItem)element).SetBinding(ListViewItem.IsSelectedProperty, binding);
        }
    }
}

あとは、IsSelectedを持ったクラスのコレクションとItemsSourceをBindingすればOK。

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using Windows.UI.Popups;

namespace App66
{
    public class MainPageViewModel
    {
        public ObservableCollection<Person> People { get; } = new ObservableCollection<Person>();

        public void AddPerson()
        {
            this.People.Add(new Person { Name = Guid.NewGuid().ToString() });
        }

        public async void Dump()
        {
            var message = $"{string.Join(", ", this.People.Where(x => x.IsSelected).Select(x => x.Name).ToArray())} selected.";
            var dlg = new MessageDialog(message);
            await dlg.ShowAsync();
        }
    }

    public class Person : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public string Name { get; set; }

        private static readonly PropertyChangedEventArgs IsSelectedPropertyChangedEventArgs = new PropertyChangedEventArgs(nameof(IsSelected));

        private bool isSelected;

        public bool IsSelected
        {
            get { return this.isSelected; }
            set
            {
                if (this.isSelected == value) { return; }
                this.isSelected = value;
                this.PropertyChanged?.Invoke(this, IsSelectedPropertyChangedEventArgs);
            }
        }


        public override string ToString()
        {
            return this.Name;
        }
    }
}
<Page x:Class="App66.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:App66"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      xmlns:System="using:System"
      mc:Ignorable="d">
    <Page.DataContext>
        <local:MainPageViewModel />
    </Page.DataContext>
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal">
            <Button Content="Add"
                    Click="{x:Bind ViewModel.AddPerson}" />
            <Button Content="Dump"
                    Click="{x:Bind ViewModel.Dump}" />
        </StackPanel>
        <local:ListViewEx x:Name="ListView"
                          Grid.Row="1"
                          SelectionMode="Multiple"
                          ItemsSource="{x:Bind ViewModel.People}">
        </local:ListViewEx>
    </Grid>
</Page>

なかなかにメンドイ。