LoginSignup
1
1

More than 3 years have passed since last update.

複数あるExpanderを同時に1つしか展開させない

Posted at

概要

開閉するコントロールなら何でもいいですが、ここでは複数のExpanderがあるとします。
これらを同時に1つしか開かせないようにするとを実現します。
下のような感じですね。
allowonlyonetrue.gif
そんなにわらわら並べないでTabでいいじゃんとかNavigation Railでいいじゃんとかは…まあそうですね。

前提

ReactivePropertyを使います。
Bindingを簡単にするというのもありますが、Pairwiseがあるのでこれを利用します。

実装

IReactivePrpoerty<bool>の配列等を受け取ってストリームに変換し、
trueの数が2以上になったら古い方のtrueをfalseに戻してやるような購読を行います。

Ext.cs
using Reactive.Bindings;
using Reactive.Bindings.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;

namespace AllowOnlyOneOpen.Extensions
{
    public static class Ext
    {
        public static IDisposable AllowOnlyOneTrue(this IEnumerable<IReactiveProperty<bool>> source)
            => source
            .CombineLatest()
            .Pairwise()
            .Where(x => x.NewItem.SkipWhile(n => !n).Skip(1).Any(n => n)) // コントロールの個数を考えるとCount()を素直に使ってよさそうですね
            .Subscribe(p => p.OldItem
                .Select((v, i) => (v, i))
                .Where(x => x.v)
                .Select(x => source.ElementAt(x.i).Value = false)
                .ToList());
    }
}

View

ただExpanderが4つ並んでいるだけです。MaterialDesignThemeは見栄えのためだけです。

MainWindow.xml
<Window
    x:Class="AllowOnlyOneOpen.Views.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:md="http://materialdesigninxaml.net/winfx/xaml/themes"
    xmlns:vm="clr-namespace:AllowOnlyOneOpen.ViewModels"
    Title="MainWindow"
    Width="400"
    Height="440"
    d:DataContext="{d:DesignInstance Type=vm:MainWindowViewModel,
                                     IsDesignTimeCreatable=True}"
    Foreground="{StaticResource MaterialDesignDarkForeground}"
    Background="{StaticResource MaterialDesignDarkBackground}"
    mc:Ignorable="d">
    <StackPanel>
        <Expander Header="A" IsExpanded="{Binding AIsExpanded.Value, Mode=TwoWay}">
            <Rectangle Height="100" Fill="Cyan" />
        </Expander>
        <Expander Header="B" IsExpanded="{Binding BIsExpanded.Value, Mode=TwoWay}">
            <Rectangle Height="100" Fill="Gray" />
        </Expander>
        <Expander Header="C" IsExpanded="{Binding CIsExpanded.Value, Mode=TwoWay}">
            <Rectangle Height="100" Fill="LightGreen" />
        </Expander>
        <Expander Header="D" IsExpanded="{Binding DIsExpanded.Value, Mode=TwoWay}">
            <Rectangle Height="100" Fill="Violet" />
        </Expander>
    </StackPanel>
</Window>

ViewModel

ViewにBindしているものを配列に放り込んで、先ほど作った拡張メソッドを使います。

MainWindowViewModel.cs
using AllowOnlyOneOpen.Extensions;
using Reactive.Bindings;

namespace AllowOnlyOneOpen.ViewModels
{
    public class MainWindowViewModel : ViewModelBase
    {
        public ReactivePropertySlim<bool> AIsExpanded { get; } = new();
        public ReactivePropertySlim<bool> BIsExpanded { get; } = new();
        public ReactivePropertySlim<bool> CIsExpanded { get; } = new();
        public ReactivePropertySlim<bool> DIsExpanded { get; } = new();

        public MainWindowViewModel()
        {
            var group = new [] { AIsExpanded, BIsExpanded, CIsExpanded, DIsExpanded };
            group.AllowOnlyOneTrue();
        }
    }
}

雑感

RadioButtonのグループ化を真似して添付プロパティにでもした方が楽かもしれない…

ソースコード

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1