views:

1240

answers:

1

Hi,

I've overridden a ComboBox in order to add an extra button at the end of the combo - i'm using this to help navigate round my application.

Its a M-V-VM App that has an Edit-Save/Cancel mechanism that disables or enables the controls depending on whether or not the user is in 'edit mode'. I wanted my navigation button to always be available whether the View is enabled or disabled.

To achieve this I'm binding the ToggleButton and the ContentPresenter to a IsEditable property on my Model.

The toggle button enables and disables as expected but the Text of the combo stays enabled.

As the text is delivered by the ContentPresenter and the ContentPresenter has an IsEnabled property I can't understand why it wont disable?

Can anyone assist? Thanks Andy

<Style TargetType="local:EntityCombo">
    <Setter Property="SnapsToDevicePixels" Value="true"/>
    <Setter Property="OverridesDefaultStyle" Value="true"/>
    <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
    <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
    <Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
    <Setter Property="MinWidth" Value="120"/>
    <Setter Property="MinHeight" Value="20"/>
    <Setter Property="FontSize" Value="12" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:EntityCombo" >
                <Grid>
                    <ToggleButton 
                        IsEnabled="{Binding IsEditable}"
                        Name="ToggleButton" 
                        Template="{DynamicResource ComboBoxToggleButton}" 
                        Grid.Column="2" 
                        Focusable="false"
                        IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
                        ClickMode="Press">
                    </ToggleButton>
                    <Button Grid.Column="2" Style="{DynamicResource EntitySelectedButton}" Command="{TemplateBinding EntitySelected}" CommandParameter="{TemplateBinding SelectedItem}"></Button>
                    <ContentPresenter
                        IsEnabled="{Binding IsEditable}"
                        Name="ContentSite"
                        IsHitTestVisible="False" 
                        Content="{TemplateBinding SelectionBoxItem}"
                        ContentTemplate="{DynamicResource SelectionBoxItem}" 
                        ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
                        Margin="3,3,23,3"
                        VerticalAlignment="Center"
                        HorizontalAlignment="Left" />
                    <Popup 
                        Name="Popup"
                        Placement="Bottom"
                        IsOpen="{TemplateBinding IsDropDownOpen}"
                        AllowsTransparency="True" 
                        Focusable="False"
                        PopupAnimation="Slide">
                        <Grid 
          Name="DropDown"
          SnapsToDevicePixels="True"                
          MinWidth="{TemplateBinding ActualWidth}"
          MaxHeight="{TemplateBinding MaxDropDownHeight}">
                            <Border 
            x:Name="DropDownBorder"
            Background="{StaticResource WindowBackgroundBrush}"
            BorderThickness="1"
            BorderBrush="{StaticResource SolidBorderBrush}"/>
                            <ScrollViewer Margin="4,6,4,6" SnapsToDevicePixels="True">
                                <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
                            </ScrollViewer>
                        </Grid>
                    </Popup>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="HasItems" Value="false">
                        <Setter TargetName="DropDownBorder" Property="MinHeight" Value="95"/>
                    </Trigger>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter Property="Foreground" Value="DarkGray"/>
                    </Trigger>

                    <Trigger Property="IsEnabled" Value="true">
                        <Setter Property="Foreground" Value="Red"/>

                    </Trigger>
                    <Trigger Property="IsGrouping" Value="true">
                        <Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
                    </Trigger>
                    <Trigger SourceName="Popup" Property="Popup.AllowsTransparency" Value="true">
                        <Setter TargetName="DropDownBorder" Property="CornerRadius" Value="4"/>
                        <Setter TargetName="DropDownBorder" Property="Margin" Value="0,2,0,0"/>
                    </Trigger>

                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
    </Style.Triggers>
</Style>
+1  A: 

This is a tricky one, but I've been knee-deep in WPF long enough to be very persistent. :) After suggesting this in my last comment my insanity drove me to test it out and it is indeed the problem:

The template sets ComboBox.Foreground based on ComboBox.IsEnabled, but you want to style the content based on DataContext.IsEnabled, so you would need to style the ContentPresenter itself. But you cannot style ContentPresenter because it inherits from FrameworkElement, not Control. But you can replace the ContentPresenter with a ContentControl and style it with the appropriate triggers for your disabled appearance.

AndyM
Thanks for your suggestion, but the IsEditable is on my ViewModel which is the DataContext. The ToggleButton works fine i.e. Enables and Disables - its just the ContentPresenter won't enable/disable.
Andy Clarke
Sorry -- I didn't read that part closely enough. These binding quirks can be so aggravating. Have you tried enabling trace sources (PresentationTraceSources.TraceLevel="High") for that binding to see what's actually happening when it tries to get the value?
AndyM
Even if I just set the IsEnabled on the ContentPresenter to False, i.e. I don't bind at all - then the Text still shows as Enabled! :(
Andy Clarke
These binding quirks can drive you mad. I'm trying to cross reference your template against my own ComboBox override, but they're different in that you're using a ContentPresenter for your display item whereas I'm using a TextBox since it's an editable combo, so I bind IsReadOnly to the same property on the parent to disable text entry. Can you post the template you're using for SelectionBoxItem in the ContentPresenter?
AndyM
Thanks for your help. I'm not using a custom template for the SelectionBoxItem, I was thinking that my xaml would be using the default ComboBox one?
Andy Clarke
It depends on whether the text is editable, which I've been assuming to be the case because of wanting to disable Text, but now that I'm comparing our XAML I realize your template is based on the non-editable case. Doh! :) That explains what's happening. The template sets ComboBox.Foreground ofbased on ComboBox.IsEnabled, but you want to style the content based on View.IsEnabled. Unfortunately you can't style the ContentPresenter itself because it's a FrameworkElement and not a Control, but if you change the ContentPresenter to a ContentControl and apply the appropriate style it should work.
AndyM