かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

UWPのListBox/ListViewで、選択状態によって見た目を変える

選択されてるときには、何かを出したいとか出したくないとか、そういう要件です。

まずは、バインドされてる要素が自分は今選択されているのかどうかを知っておくと話が早くなります。それについては、以下の記事を参照。

blog.okazuki.jp

あとは、このIsSelectedプロパティを見て表示・非表示あたりを切り替えてやればOKだと思われます。とりあえずやってみましょう。

データの入れ物を作ります。

using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace App27.ViewModels
{
    public class Person : BindableBase
    {
        private bool isSelected;

        public bool IsSelected
        {
            get { return this.isSelected; }
            set { this.SetProperty(ref this.isSelected, value); }
        }

        private string name;

        public string Name
        {
            get { return this.name; }
            set { this.SetProperty(ref this.name, value); }
        }

    }
}

ここのIsSelectedの選択状態が入るようにします。ListBoxを拡張したコントロールを作って選択状態と、上記クラスのIsSelectedをバインドしておきます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;

namespace App27.Controls
{
    public class ListBoxEx : ListBox
    {
        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            base.PrepareContainerForItemOverride(element, item);
            var b = new Binding();
            b.Path = new PropertyPath("IsSelected");
            b.Source = item;
            b.Mode = BindingMode.TwoWay;
            ((ListBoxItem)element).SetBinding(ListBoxItem.IsSelectedProperty, b);
        }

    }
}

次に、画面を組み立てます。今回は選択状態のときはボタンを消すという処理をしています。

<Page x:Class="App27.Views.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:App27.Views"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      xmlns:Mvvm="using:Prism.Windows.Mvvm"
      xmlns:Controls="using:App27.Controls"
      xmlns:ViewModels="using:App27.ViewModels"
      Mvvm:ViewModelLocator.AutoWireViewModel="True"
      mc:Ignorable="d">


    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Controls:ListBoxEx ItemsSource="{x:Bind ViewModel.Users}">
            <ListBox.ItemTemplate>
                <DataTemplate x:DataType="ViewModels:Person">
                    <StackPanel>
                        <TextBlock Text="{x:Bind Name}" />
                        <Button x:Name="ButtonDelete"
                                Content="削除" 
                                Visibility="{x:Bind IsSelected, Mode=OneWay, Converter={StaticResource InverseBooleanToVisibilityConverter}}"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </Controls:ListBoxEx>
    </Grid>
</Page>

InverseBooleanToVisibilityConverterは、まぁ誰もが実装するような以下のようなコンバータです。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;

namespace App27.Converters
{
    public class BooleanToVisibilityConverter : IValueConverter
    {
        public bool IsInverse { get; set; }

        public object Convert(object value, Type targetType, object parameter, string language)
        {
            var b = (bool)value;
            if (this.IsInverse)
            {
                b = !b;
            }
            return b ? Visibility.Visible : Visibility.Collapsed;
        }

        public object ConvertBack(object value, Type targetType, object parameter, string language)
        {
            var v = (Visibility)value;
            if (this.IsInverse)
            {
                return v == Visibility.Collapsed;
            }
            else
            {
                return v == Visibility.Visible;
            }
        }
    }
}

App.xamlで以下のような雰囲気で定義しています。

<Prism:PrismUnityApplication x:Class="App27.App"
                             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                             xmlns:Prism="using:Prism.Unity.Windows"
                             xmlns:Converters="using:App27.Converters"
                             xmlns:local="using:App27"
                             RequestedTheme="Light">
    <Prism:PrismUnityApplication.Resources>
        <Converters:BooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter"
                                                 IsInverse="True" />
    </Prism:PrismUnityApplication.Resources>
</Prism:PrismUnityApplication>

因みにPrism.Windows使ってます!それについては下記を参照してください。

github.com