Q038. ComboBox.IsReadOnly プロパティ を True にしても編集できてしまうのですが・・・

A. このプロパティですが、根本的に仕様がおかしいように思えます。

まず以下の図をごらんください。


4つのコンボボックスに対し、アイテムが4つの県、さらに IsReadOnly と IsEditable プロパティを設定しました。プロパティの設定内容は 以下のXAML のとおりです。

<Window x:Class="ReadOnlyTestApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="180" Width="515">
    <Grid>
        
        <TextBlock Margin="42,24,251,105" Text="IsEditable = False  IsReadOnly = True" />
        <ComboBox Name="comboBox1" Height="22" 
                  Margin="42,42,251,0" VerticalAlignment="Top" Width="200" 
                  IsEditable="False" IsReadOnly="True" >
            <ComboBoxItem Content="東京都" />
            <ComboBoxItem Content="神奈川県" />
            <ComboBoxItem Content="千葉県" />
            <ComboBoxItem Content="埼玉県" />
        </ComboBox>

        <TextBlock Margin="42,80,251,49"  Text="IsEditable = True  IsReadOnly = True" />
        <ComboBox Name="comboBox2" Height="22"  
                  Margin="42,98,251,0" VerticalAlignment="Top" Width="200"
                  IsEditable="True" IsReadOnly="True" >
            <ComboBoxItem Content="東京都" />
            <ComboBoxItem Content="神奈川県" />
            <ComboBoxItem Content="千葉県" />
            <ComboBoxItem Content="埼玉県" />
        </ComboBox>

        <TextBlock Margin="262,24,31,105" Text="IsEditable = False  IsReadOnly = False" />
        <ComboBox Name="comboBox3" Height="22" 
                  Margin="262,42,31,0" VerticalAlignment="Top" Width="200" 
                  IsEditable="False" IsReadOnly="False">
            <ComboBoxItem Content="東京都" />
            <ComboBoxItem Content="神奈川県" />
            <ComboBoxItem Content="千葉県" />
            <ComboBoxItem Content="埼玉県" />
        </ComboBox>

        <TextBlock Margin="262,80,31,49"  Text="IsEditable = True  IsReadOnly = False" />
        <ComboBox Name="comboBox4" Height="22"  
                  Margin="262,98,31,0" VerticalAlignment="Top" Width="200"
                  IsEditable="True" IsReadOnly="False" >
            <ComboBoxItem Content="東京都" />
            <ComboBoxItem Content="神奈川県" />
            <ComboBoxItem Content="千葉県" />
            <ComboBoxItem Content="埼玉県" />
        </ComboBox>
    </Grid>
</Window>


しかし、読み取り専用にした ComboBox でも選択項目が変更できてしまいます。


読み取り専用にした(IsReadOnly = True)ComboBox でも選択項目が変更できるのってちょっとおかしくないでしょうか?またこの挙動ですが、MSDN のヘルプとも微妙に違ってます。以下ヘルプの抜粋ですが、IsReadOnly が true の場合でも 文字列を入力して ComboBox 内の項目を選択できるとありますが、実際はできません。

ComboBox.IsReadOnly プロパティ


選択専用モードを有効にする値を取得または設定します。選択専用モードでは、コンボ ボックスの内容は選択可能ですが、編集することはできません。


・・・・中略・・・・


IsEditable および IsReadOnly の値に従って、ユーザーが行える操作と行えない操作を次の表に示します。


IsReadOnly が true です。

IsReadOnly が false です。

IsEditable が true です。

  • 文字列を入力して ComboBox 内の項目を選択できない。

  • ComboBox 内の項目に対応しない文字列を入力できない。

  • ComboBox テキスト ボックス内の文字列の一部を選択できる。

  • ComboBox テキスト ボックス内の文字列をコピーすることはできるが、文字列を ComboBox テキスト ボックスに貼り付けることはできない。

  • 文字列を入力して ComboBox 内の項目を選択できる。

  • ComboBox 内の項目に対応しない文字列を入力できる。

  • ComboBox テキスト ボックス内の文字列の一部を選択できる。

  • ComboBox テキスト ボックス内の文字列をコピーでき、また貼り付けることもできる。

IsEditable が false です。

  • 文字列を入力して ComboBox 内の項目を選択できる。

  • ComboBox 内の項目に対応しない文字列を入力できない。

  • ComboBox 内の文字列の一部を選択できない。

  • ComboBox 内の文字列をコピーまたは貼り付けできない。

  • 文字列を入力して ComboBox 内の項目を選択できる。

  • ComboBox 内の項目に対応しない文字列を入力できない。

  • ComboBox 内の文字列の一部を選択できない。

  • ComboBox 内の文字列をコピーまたは貼り付けできない。


MSDN ドキュメント 「ComboBox.IsReadOnly プロパティ」

読み取り専用にするなら、選択項目は変更できすドロップダウンも表示されない仕様にすべきだと思います。そこで テンプレートを編集してみました。ReadOnly = True の場合、テンプレート内のトリガで CombBox と ComboBoxEditableTemplate リソース の IsHitVisible と IsTabStop を False にします。これで選択項目も変更できない ComboBox の出来上がりです。

<!-- ComboBox Style -->
<Style x:Key="ComboBoxFocusVisual">
    <Setter Property="Control.Template">
        <Setter.Value>
            <ControlTemplate>
                <Rectangle Margin="4,4,21,4" SnapsToDevicePixels="true" 
                           Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" 
                           StrokeThickness="1" StrokeDashArray="1 2"/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
<SolidColorBrush x:Key="ListBorder" Color="#FF7F9DB9"/>
<Style x:Key="ComboBoxTransparentButtonStyle" TargetType="{x:Type ToggleButton}">
    <Setter Property="MinWidth" Value="0"/>
    <Setter Property="MinHeight" Value="0"/>
    <Setter Property="Width" Value="Auto"/>
    <Setter Property="Height" Value="Auto"/>
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="Focusable" Value="false"/>
    <Setter Property="ClickMode" Value="Press"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ToggleButton}">
                <Grid Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition SharedSizeGroup="ComboBoxButton" Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <Microsoft_Windows_Themes:ScrollChrome x:Name="Chrome" Grid.Column="1" HasOuterBorder="false" 
                         Padding="1,0,0,0" RenderMouseOver="{TemplateBinding IsMouseOver}" 
                         RenderPressed="{TemplateBinding IsChecked}" 
                         Microsoft_Windows_Themes:ScrollChrome.ScrollGlyph="DownArrow" ThemeColor="NormalColor" 
                         Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
<Style x:Key="ComboBoxEditableTextBox" TargetType="{x:Type TextBox}">
    <Setter Property="OverridesDefaultStyle" Value="true"/>
    <Setter Property="AllowDrop" Value="true"/>
    <Setter Property="MinWidth" Value="0"/>
    <Setter Property="MinHeight" Value="0"/>
    <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
    <Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
    <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TextBox}">
                <ScrollViewer x:Name="PART_ContentHost" Background="Transparent" 
                              Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
<ControlTemplate x:Key="ComboBoxEditableTemplate" TargetType="{x:Type ComboBox}">
    <Grid SnapsToDevicePixels="true">
        <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" 
                Background="{TemplateBinding Background}" Padding="1">
            <Grid Grid.IsSharedSizeScope="true">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="1"/>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition SharedSizeGroup="ComboBoxButton" Width="Auto"/>
                </Grid.ColumnDefinitions>
                <TextBox x:Name="PART_EditableTextBox" Grid.Column="1" 
                         HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" 
                         IsReadOnly="{Binding IsReadOnly, RelativeSource={RelativeSource TemplatedParent}}" 
                         Margin="{TemplateBinding Padding}" Style="{StaticResource ComboBoxEditableTextBox}" 
                         VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
                <ToggleButton Background="{x:Null}" Grid.ColumnSpan="3" IsChecked="{Binding IsDropDownOpen, 
                              Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" 
                              Style="{StaticResource ComboBoxTransparentButtonStyle}"/>
            </Grid>
        </Border>
        <Popup x:Name="PART_Popup" AllowsTransparency="true" Focusable="false" IsOpen="{Binding IsDropDownOpen, 
               RelativeSource={RelativeSource TemplatedParent}}" 
               PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
            <Microsoft_Windows_Themes:SystemDropShadowChrome x:Name="Shdw" Color="Transparent" 
                         MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{TemplateBinding ActualWidth}">
                <Border x:Name="DropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" 
                        BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
                    <ScrollViewer x:Name="DropDownScrollViewer">
                        <Grid RenderOptions.ClearTypeHint="Enabled">
                            <Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
                                <Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=DropDownBorder}" 
                                           Height="{Binding ActualHeight, ElementName=DropDownBorder}" 
                                           Width="{Binding ActualWidth, ElementName=DropDownBorder}"/>
                            </Canvas>
                            <ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" 
                                            SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                        </Grid>
                    </ScrollViewer>
                </Border>
            </Microsoft_Windows_Themes:SystemDropShadowChrome>
        </Popup>
    </Grid>
    <ControlTemplate.Triggers>
        <Trigger Property="HasItems" Value="false">
            <Setter Property="MinHeight" TargetName="DropDownBorder" Value="95"/>
        </Trigger>
        <Trigger Property="IsEnabled" Value="false">
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
            <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
        </Trigger>
        <Trigger Property="IsGrouping" Value="true">
            <Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
        </Trigger>
        <Trigger Property="IsReadOnly" Value="true">
            <Setter Property="IsHitTestVisible" Value="False"/>
            <Setter Property="IsTabStop" Value="False"/>
        </Trigger>
        <Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true">
            <Setter Property="Margin" TargetName="Shdw" Value="0,0,5,5"/>
            <Setter Property="Color" TargetName="Shdw" Value="#71000000"/>
        </Trigger>
        <Trigger Property="ScrollViewer.CanContentScroll" SourceName="DropDownScrollViewer" Value="false">
            <Setter Property="Canvas.Top" TargetName="OpaqueRect" 
                    Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}"/>
            <Setter Property="Canvas.Left" TargetName="OpaqueRect" 
                    Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>
<Style TargetType="{x:Type ComboBox}">
    <Setter Property="FocusVisualStyle" Value="{StaticResource ComboBoxFocusVisual}"/>
    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"/>
    <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
    <Setter Property="BorderBrush" Value="{StaticResource ListBorder}"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="Padding" Value="1"/>
    <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
    <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
    <Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
    <Setter Property="ScrollViewer.PanningMode" Value="Both"/>
    <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
    <Setter Property="VerticalContentAlignment" Value="Center"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ComboBox}">
                <Grid SnapsToDevicePixels="true">
                    <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" 
                            BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="1">
                        <Grid Grid.IsSharedSizeScope="true">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="1"/>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition SharedSizeGroup="ComboBoxButton" Width="Auto"/>
                            </Grid.ColumnDefinitions>
                            <Border x:Name="SelectedItemBorder" Grid.ColumnSpan="2" Margin="{TemplateBinding Padding}"/>
                            <ContentPresenter ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" 
                                      ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" 
                                      Content="{TemplateBinding SelectionBoxItem}" Grid.Column="1" 
                                      ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}" 
                                      HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                      Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                                      VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                            <ToggleButton Grid.ColumnSpan="3" IsChecked="{Binding IsDropDownOpen, 
                                          Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" 
                                          Style="{StaticResource ComboBoxTransparentButtonStyle}"/>
                        </Grid>
                    </Border>
                    <Popup x:Name="PART_Popup" AllowsTransparency="true" Focusable="false" 
                           IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" 
                           PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
                        <Microsoft_Windows_Themes:SystemDropShadowChrome x:Name="Shdw" Color="Transparent" 
                                    MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{TemplateBinding ActualWidth}">
                            <Border x:Name="DropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" 
                                    BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
                                <ScrollViewer x:Name="DropDownScrollViewer">
                                    <Grid RenderOptions.ClearTypeHint="Enabled">
                                        <Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
                                            <Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=DropDownBorder}" 
                                                       Height="{Binding ActualHeight, ElementName=DropDownBorder}" 
                                                       Width="{Binding ActualWidth, ElementName=DropDownBorder}"/>
                                        </Canvas>
                                        <ItemsPresenter x:Name="ItemsPresenter" 
                                                        KeyboardNavigation.DirectionalNavigation="Contained" 
                                                        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                                    </Grid>
                                </ScrollViewer>
                            </Border>
                        </Microsoft_Windows_Themes:SystemDropShadowChrome>
                    </Popup>
                </Grid>
                <ControlTemplate.Triggers>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="IsSelectionBoxHighlighted" Value="true"/>
                            <Condition Property="IsDropDownOpen" Value="false"/>
                        </MultiTrigger.Conditions>
                        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
                    </MultiTrigger>
                    <Trigger Property="IsSelectionBoxHighlighted" Value="true">
                        <Setter Property="Background" TargetName="SelectedItemBorder" 
                                Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                    </Trigger>
                    <Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true">
                        <Setter Property="Margin" TargetName="Shdw" Value="0,0,5,5"/>
                        <Setter Property="Color" TargetName="Shdw" Value="#71000000"/>
                    </Trigger>
                    <Trigger Property="HasItems" Value="false">
                        <Setter Property="MinHeight" TargetName="DropDownBorder" Value="95"/>
                    </Trigger>
                    <Trigger Property="IsDropDownOpen" Value="true" >
                        <Setter Property="Background" Value="LemonChiffon"/>
                        <Setter Property="Background" TargetName="DropDownBorder" Value="LemonChiffon"/>
                    </Trigger>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                        <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                    </Trigger>
                    <Trigger Property="IsGrouping" Value="true">
                        <Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
                    </Trigger>
                    <Trigger Property="IsReadOnly" Value="true">
                        <Setter Property="IsHitTestVisible" Value="False"/>
                        <Setter Property="IsTabStop" Value="False"/>
                    </Trigger>
                    <Trigger Property="ScrollViewer.CanContentScroll" SourceName="DropDownScrollViewer" Value="false">
                        <Setter Property="Canvas.Top" TargetName="OpaqueRect" 
                                Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}"/>
                        <Setter Property="Canvas.Left" TargetName="OpaqueRect" 
                                Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <Trigger Property="IsEditable" Value="true">
            <Setter Property="IsTabStop" Value="false"/>
            <Setter Property="Padding" Value="0,1"/>
            <Setter Property="Template" Value="{StaticResource ComboBoxEditableTemplate}"/>
        </Trigger>
    </Style.Triggers>
</Style>


これで ReadOnly = True に設定した ComboBox は、選択項目を変更できなければドロップダウンも表示しません。


WPF FAQ の目次に戻る