다음을 통해 공유


WPF: Disabling a RibbonButton

Introduction

If you try to disable a child RibbonButton of a RibbonMenuButton in WPF by setting its IsEnabled property to false you will notice that it is still being highlighted when you move the mouse over it and the dropdown is still closed when you click on the button.

This TechNet Wiki article is based on my answer in the following WPF forum thread: 
https://social.msdn.microsoft.com/Forums/en-US/556953f4-c283-4677-ba52-407c5ea28e70/disablehide-ribbonbutton?forum=wpf

Details

The issue is easy to reproduce:

<Ribbon>
    <RibbonGroup Header="Group A">
        <RibbonMenuButton Label="Button A">
            <RibbonMenuButton.Items>
                <RibbonButton Label="First" />
                <RibbonButton Label="Second" IsEnabled="False" />
                <RibbonButton Label="Third" />
            </RibbonMenuButton.Items>
        </RibbonMenuButton>
    </RibbonGroup>
</Ribbon>

https://magnusmontin.files.wordpress.com/2016/10/ribbon.png

Setting the IsFocusable or IsHitTestVisible property of the RibbonButton to false makes no difference. This is because an item in the RibbonMenuButton's Items (or ItemsSource) collection gets wrapped in a RibbonMenuItem container when the Ribbon UI is created at runtime, just like a visual ListBoxItem container gets created for each data item of a ListBox and a ComboBoxItem container gets created for each item in a ComboBox. And the container is still highlighted and clickable even if you disable its Content. 

By defining an ItemContainerStyle and set the IsHitTestVisibile property of the generated RibbonMenuItem container to false you can disable the RibbonButton properly.

Since you probably don't want to disable all the buttons you can handle the Loaded event of the RibbonMenuItem container and programmatically check the value of the IsEnabled property of the RibbonButton that you set in the XAML markup to determine whether to disable the container. You hook up the event handler using an EventSetter like this:

<Ribbon>
    <RibbonGroup Header="Group A">
        <RibbonMenuButton Label="Button A">
            <RibbonMenuButton.ItemContainerStyle>
                <Style TargetType="RibbonMenuItem">
                    <EventSetter Event="Loaded" Handler="OnLoaded" />
                </Style>
            </RibbonMenuButton.ItemContainerStyle>
            <RibbonMenuButton.Items>
                <RibbonButton Label="First" />
                <RibbonButton Label="Second" IsEnabled="False" />
                <RibbonButton Label="Third" />
            </RibbonMenuButton.Items>
        </RibbonMenuButton>
    </RibbonGroup>
</Ribbon>

You can then simply cast the DataContext of the RibbonMenuItem container to a RibbonButton and check the IsEnabled property of it and set the IsHitTestVisibile property of the container accordingly in the event handler:

private void  OnLoaded(object  sender, RoutedEventArgs e)
{
    RibbonMenuItem rmi = sender as  RibbonMenuItem;
    RibbonButton rb = rmi.DataContext as  RibbonButton;
    if  (rb != null  && !rb.IsEnabled)
        rmi.IsHitTestVisible = false;
}

This will make any RibbonButton in the RibbonMenu that you set the IsEnabled property of to false in the XAML markup truly disabled.

Additional Resources

ItemsControl.ItemContainerStyle Property:

RibbonMenuButton Class:

- https://msdn.microsoft.com/en-us/library/system.windows.controls.ribbon.ribbonmenubutton(v=vs.110).aspx