none
ListBox.AnchorItemであるListBoxItemの枠色を変更したい

    質問


  • ListBoxItemの枠色をListBox.AnchorItemであるかどうかで枠色を変えたいと思っています。

    MainWindow.xaml

    <Window x:Class="テスト.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:テスト"
            mc:Ignorable="d"
            Title="MainWindow" SizeToContent="WidthAndHeight">
        <Window.Resources>
            <Style TargetType="ListBoxItem">
                <Setter Property="OverridesDefaultStyle" Value="True" />
                <Setter Property="FocusVisualStyle" Value="{x:Null}" />
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ContentControl}">
                            <Border x:Name="ContentBorder"
                                    Background="{TemplateBinding Background}" 
                                    BorderThickness="1">
                                <ContentPresenter />
                            </Border>
                            <ControlTemplate.Triggers>
                                <!--★★★ListBox.AnchorItemの要素であれば色を変えたい。IsFocusedではなく-->
                                <Trigger Property="IsFocused" Value="True">
                                    <Setter TargetName="ContentBorder" Property="BorderBrush" Value="Red" />
                                    <Setter TargetName="ContentBorder" Property="BorderThickness" Value="1" />
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
                <Style.Triggers>
                    <Trigger Property="IsSelected" Value="True">
                        <Setter Property="Background" Value="Aqua" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Window.Resources>
        <Window.DataContext>
            <local:MainWindowViewModel/>
        </Window.DataContext>
        <StackPanel>
            <ListBox x:Name="listBox" SelectionMode="Extended" >
                <ListBoxItem Content="aaa" />
                <ListBoxItem Content="bbb" />
                <ListBoxItem Content="ccc" />
                <ListBoxItem Content="ddd" />
                <ListBoxItem Content="eee" />
            </ListBox>
    
            <Button Content="選択クリア" Click="Button_Click"/>
        </StackPanel>
    
    </Window>

    MainWindow.xaml.cs

        /// <summary>
        /// MainWindow.xaml の相互作用ロジック
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            private void Button_Click(object sender, RoutedEventArgs e)
            {
                // 選択を解除する
                listBox.SelectedIndex = -1;
            }
        }

    上記プログラムを実行すると

    1.cccを選択

    


    2. 選択クリアをクリック
    選択が解除される



     ※このときにListBox.AnchorItemであるcccに枠を表示し続けたい。

    3. aaaをShift+クリックで選択

    cccがAnchorItemであるので、cccからaaaが選択される。

    ----------------------------------------------------------------

    上記コードでは、IsFocuedかどうかで、枠色を赤に変えていますが、ListBox.AnchorItemのListBoxItemであるかどうかで枠色を変えたいと思っています。何か良い方法はないでしょうか。

    お教えいただけますと幸いです。



    • 編集済み yj0529 2019年5月22日 8:45
    2019年5月22日 8:44

回答

  • リフレクションで掘るしかないです

    <Window x:Class="WpfApp1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp1"
            mc:Ignorable="d"
            Title="MainWindow" Height="300" Width="300">
        <Window.Resources>
            <local:EqualConverter x:Key="eq" />
            
            <Style TargetType="ListBoxItem">
                <Setter Property="OverridesDefaultStyle" Value="True" />
                <Setter Property="FocusVisualStyle" Value="{x:Null}" />
    
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ContentControl}">
                            <Border x:Name="ContentBorder"
                                    Background="{TemplateBinding Background}" 
                                    BorderThickness="1">
                                <ContentPresenter />
                            </Border>
                            <ControlTemplate.Triggers>
    
                                <!--★★★ListBox.AnchorItemの要素であれば色を変えたい。IsFocusedではなく-->
                                <DataTrigger Value="true">
                                    <DataTrigger.Binding>
                                        <MultiBinding Converter="{StaticResource ResourceKey=eq}">
                                            <Binding Path="(local:ListBoxEx.Anchor).Item" RelativeSource="{RelativeSource Mode=FindAncestor,AncestorType={x:Type local:ListBoxEx}}"/>
                                            <Binding Path="." />
                                        </MultiBinding>
                                    </DataTrigger.Binding>
    
                                    <Setter TargetName="ContentBorder" Property="BorderBrush" Value="Red" />
                                    <Setter TargetName="ContentBorder" Property="BorderThickness" Value="1" />
                                </DataTrigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
                <Style.Triggers>
                    <Trigger Property="IsSelected" Value="True">
                        <Setter Property="Background" Value="Aqua" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Window.Resources>
    
    
        <DockPanel>
            <StackPanel DockPanel.Dock="Bottom">
                <Button Content="選択クリア" Click="Button_Click" />
                <TextBlock Text="{Binding Path=Anchor.Index,ElementName=listBox}" />
                <TextBlock Text="{Binding Path=Anchor.Item,ElementName=listBox}" />
            </StackPanel>
    
            <local:ListBoxEx x:Name="listBox" SelectionMode="Extended" Margin="5"
                             ItemsSource="{Binding}" 
                             ScrollViewer.VerticalScrollBarVisibility="Visible"
                             VirtualizingPanel.IsVirtualizing="true"
                             VirtualizingPanel.VirtualizationMode="Recycling">
            </local:ListBoxEx>
        </DockPanel>
    
    </Window>
    namespace WpfApp1
    {
        using System;
        using System.Collections.Generic;
        using System.Globalization;
        using System.Windows;
        using System.Windows.Controls;
        using System.Windows.Data;
    
        public partial class MainWindow : Window
        {
            class TestItem
            {
                public int Value { get; set; }
                public override string ToString() { return Value.ToString(); }
            }
    
            public MainWindow()
            {
                InitializeComponent();
    
                var list = new List<TestItem>();
                for (int i = 0; i < 1000; i++)
                {
                    TestItem test = new TestItem();
                    test.Value = i;
                    list.Add(test);
                }
                this.DataContext = list;
            }
    
            private void Button_Click(object sender, RoutedEventArgs e)
            {
                // 選択を解除する
                listBox.SelectedIndex = -1;
            }
        }
    
        class EqualConverter : IMultiValueConverter
        {
            public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
            {
                return object.Equals(values[0], values[1]);
            }
    
            public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
            {
                throw new NotSupportedException();
            }
        }
    
        class ListBoxEx : ListBox
        {
            public AnchorItem Anchor
            {
                get { return (AnchorItem)GetValue(AnchorProperty); }
                private set { SetValue(AnchorPropertyKey, value); }
            }
    
            public static readonly DependencyPropertyKey AnchorPropertyKey
                    = DependencyProperty.RegisterReadOnly
                    ("Anchor"
                    , typeof(AnchorItem)
                    , typeof(ListBoxEx)
                    , new FrameworkPropertyMetadata(new AnchorItem(), FrameworkPropertyMetadataOptions.Inherits));
    
            public static readonly DependencyProperty AnchorProperty = AnchorPropertyKey.DependencyProperty;
    
            protected override void OnSelectionChanged(SelectionChangedEventArgs e)
            {
                base.OnSelectionChanged(e);
                Dispatcher.BeginInvoke(new Action(() =>
                {
                    AnchorItem @new = new AnchorItem(base.AnchorItem);
                    this.Anchor = @new;
                }), System.Windows.Threading.DispatcherPriority.Normal);
            }
        }
    
        class AnchorItem
        {
            static System.Reflection.PropertyInfo piIndex;
            static System.Reflection.PropertyInfo piItem;
    
            public AnchorItem() : this(null)
            {
    
            }
            public AnchorItem(object info)
            {
                if (info == null)
                {
                    Index = -1;
                    Item = null;
                }
                else
                {
                    if (piIndex == null)
                    {
                        piIndex = info.GetType().GetProperty("Index", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                    }
                    if (piItem == null)
                    {
                        piItem = info.GetType().GetProperty("Item", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                    }
    
                    Index = (int)piIndex.GetValue(info);
                    Item = piItem.GetValue(info);
                }
    
            }
            public int Index { get; private set; }
            public object Item { get; private set; }
        }
    }

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク yj0529 2019年5月23日 0:47
    2019年5月22日 13:24

すべての返信

  • リフレクションで掘るしかないです

    <Window x:Class="WpfApp1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp1"
            mc:Ignorable="d"
            Title="MainWindow" Height="300" Width="300">
        <Window.Resources>
            <local:EqualConverter x:Key="eq" />
            
            <Style TargetType="ListBoxItem">
                <Setter Property="OverridesDefaultStyle" Value="True" />
                <Setter Property="FocusVisualStyle" Value="{x:Null}" />
    
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ContentControl}">
                            <Border x:Name="ContentBorder"
                                    Background="{TemplateBinding Background}" 
                                    BorderThickness="1">
                                <ContentPresenter />
                            </Border>
                            <ControlTemplate.Triggers>
    
                                <!--★★★ListBox.AnchorItemの要素であれば色を変えたい。IsFocusedではなく-->
                                <DataTrigger Value="true">
                                    <DataTrigger.Binding>
                                        <MultiBinding Converter="{StaticResource ResourceKey=eq}">
                                            <Binding Path="(local:ListBoxEx.Anchor).Item" RelativeSource="{RelativeSource Mode=FindAncestor,AncestorType={x:Type local:ListBoxEx}}"/>
                                            <Binding Path="." />
                                        </MultiBinding>
                                    </DataTrigger.Binding>
    
                                    <Setter TargetName="ContentBorder" Property="BorderBrush" Value="Red" />
                                    <Setter TargetName="ContentBorder" Property="BorderThickness" Value="1" />
                                </DataTrigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
                <Style.Triggers>
                    <Trigger Property="IsSelected" Value="True">
                        <Setter Property="Background" Value="Aqua" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Window.Resources>
    
    
        <DockPanel>
            <StackPanel DockPanel.Dock="Bottom">
                <Button Content="選択クリア" Click="Button_Click" />
                <TextBlock Text="{Binding Path=Anchor.Index,ElementName=listBox}" />
                <TextBlock Text="{Binding Path=Anchor.Item,ElementName=listBox}" />
            </StackPanel>
    
            <local:ListBoxEx x:Name="listBox" SelectionMode="Extended" Margin="5"
                             ItemsSource="{Binding}" 
                             ScrollViewer.VerticalScrollBarVisibility="Visible"
                             VirtualizingPanel.IsVirtualizing="true"
                             VirtualizingPanel.VirtualizationMode="Recycling">
            </local:ListBoxEx>
        </DockPanel>
    
    </Window>
    namespace WpfApp1
    {
        using System;
        using System.Collections.Generic;
        using System.Globalization;
        using System.Windows;
        using System.Windows.Controls;
        using System.Windows.Data;
    
        public partial class MainWindow : Window
        {
            class TestItem
            {
                public int Value { get; set; }
                public override string ToString() { return Value.ToString(); }
            }
    
            public MainWindow()
            {
                InitializeComponent();
    
                var list = new List<TestItem>();
                for (int i = 0; i < 1000; i++)
                {
                    TestItem test = new TestItem();
                    test.Value = i;
                    list.Add(test);
                }
                this.DataContext = list;
            }
    
            private void Button_Click(object sender, RoutedEventArgs e)
            {
                // 選択を解除する
                listBox.SelectedIndex = -1;
            }
        }
    
        class EqualConverter : IMultiValueConverter
        {
            public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
            {
                return object.Equals(values[0], values[1]);
            }
    
            public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
            {
                throw new NotSupportedException();
            }
        }
    
        class ListBoxEx : ListBox
        {
            public AnchorItem Anchor
            {
                get { return (AnchorItem)GetValue(AnchorProperty); }
                private set { SetValue(AnchorPropertyKey, value); }
            }
    
            public static readonly DependencyPropertyKey AnchorPropertyKey
                    = DependencyProperty.RegisterReadOnly
                    ("Anchor"
                    , typeof(AnchorItem)
                    , typeof(ListBoxEx)
                    , new FrameworkPropertyMetadata(new AnchorItem(), FrameworkPropertyMetadataOptions.Inherits));
    
            public static readonly DependencyProperty AnchorProperty = AnchorPropertyKey.DependencyProperty;
    
            protected override void OnSelectionChanged(SelectionChangedEventArgs e)
            {
                base.OnSelectionChanged(e);
                Dispatcher.BeginInvoke(new Action(() =>
                {
                    AnchorItem @new = new AnchorItem(base.AnchorItem);
                    this.Anchor = @new;
                }), System.Windows.Threading.DispatcherPriority.Normal);
            }
        }
    
        class AnchorItem
        {
            static System.Reflection.PropertyInfo piIndex;
            static System.Reflection.PropertyInfo piItem;
    
            public AnchorItem() : this(null)
            {
    
            }
            public AnchorItem(object info)
            {
                if (info == null)
                {
                    Index = -1;
                    Item = null;
                }
                else
                {
                    if (piIndex == null)
                    {
                        piIndex = info.GetType().GetProperty("Index", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                    }
                    if (piItem == null)
                    {
                        piItem = info.GetType().GetProperty("Item", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                    }
    
                    Index = (int)piIndex.GetValue(info);
                    Item = piItem.GetValue(info);
                }
    
            }
            public int Index { get; private set; }
            public object Item { get; private set; }
        }
    }

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク yj0529 2019年5月23日 0:47
    2019年5月22日 13:24
  • gekka様

    ご記載いただいた内容がまさにやりたかったことでした。非常に参考になります。

    ありがとうございます!

    2019年5月23日 0:47