Ribbon Tab that supports ICheckableCommandParameter

Ribbon for WPF Forum

Posted 15 years ago by Mark Hanson
Version: 4.5.0483
Avatar
Hi!

Can you give me a suggestion about making a Ribbon Tab to support ICheckableCommandParameter interface like Ribbon Button does?

Thanks!

[Modified at 01/06/2009 09:04 AM]

[Modified at 01/06/2009 09:05 AM]

Comments (7)

Posted 15 years ago by Mike Strobel - Software Engineer, CDC Software
Avatar
Hi Mark,

RibbonTab is not an ICommandSource, so it doesn't have a command associated with it. I'm not sure then why you would need a command parameter. If you can provide some more information about what you're ultimately trying to do, perhaps I could offer a suggestion :).

Cheers,
Mike
Posted 15 years ago by Mark Hanson
Avatar
Hi Mike,

Thanks for you answer but I have to disagree with you. The RibbonTab (i.e.ActiproSoftware.Windows.Controls.Ribbon.Controls.Tab) derives from ButtonBase which has as base class ControlBase class which is an ICommandSource. So, RibbonTab does have Command, CommandParameter and CommandTarget properties.
My issue here is that RibbonTab CommandParameter is not a ICheckableCommandParameter.
In conclusion, the RibbonTab is not a checkable control and I need it to be.

My solution so far was to subclass RibbonTab but I have some issues there and that is why I wanted to know which is the pattern for implementing checkable controls (like RibbonButton).

Thanks again.

Mark
Posted 15 years ago by Mike Strobel - Software Engineer, CDC Software
Avatar
Apologies, I was looking at the type hierarchy for the RibbonTab control in an old framework that I don't use anymore.

The CommandParameter dependency property is declared as type System.Object, but you can inspect the type and cast it to an ICheckableCommandParameter in order to set the IsChecked property in your CanExecute handler (or your Execute handler if appropriate). I believe what you want to do is bind the RibbonTab's IsChecked property to the ICheckableCommandParameter's IsChecked property. The trick part is picking the correct binding direction. I'm guessing you want the RibbonTab's local value to take precedence, so you should probably set the binding on the parameter:
<apribbon:RibbonTab x:Name="MyRibbon">
  <apribbon:RibbonTab.CommandParameter>
    <apribbon:CheckableCommandParameter IsChecked="{Binding ElementName=MyRibbon, Path=IsChecked, Mode=OneWayToSource}" />
  </apribbon:RibbonTab.CommandParameter> 
  ...
</apribbon:RibbonTab>
Mike
Posted 15 years ago by Mark Hanson
Avatar
Hi Mike,

My intention is to select the Tab when the CommandParameter (which is a CheckableCommandParameter) IsChecked property is true. In other words, the CommandParameter tells to the Tab to select itself.

I'll present my solution below.
I've implemented 2 classes. The first is CustomCheckableCommandParameter. This class extends ICheckableCommandParameter. The difference between built-in CheckableCommandParameter class and CustomCheckableCommandParameter is that the last one announces when IsChecked property changes. See the code below.

public class CustomCheckableCommandParameter : DependencyObject, ICheckableCommandParameter
    {
        #region Fields

        public static readonly DependencyProperty IsCheckedProperty;
        public static readonly DependencyProperty HandledProperty;
        public static readonly DependencyProperty TagProperty;

        #endregion Fields

        #region Properties

        public bool? IsChecked
        {
            get { return (bool)GetValue(IsCheckedProperty); }
            set { SetValue(IsCheckedProperty, value); }
        }

        public bool Handled
        {
            get { return (bool)GetValue(HandledProperty); }
            set { SetValue(HandledProperty, value); }
        }

        public object Tag
        {
            get { return (object)GetValue(TagProperty); }
            set { SetValue(TagProperty, value); }
        }


        #endregion Properties

        #region Events

        public event EventHandler IsCheckedChanged;

        #endregion Events

        static CustomCheckableCommandParameter()
        {
            IsCheckedProperty =
                DependencyProperty.Register(
                "IsChecked",
                typeof(bool),
                typeof(CustomCheckableCommandParameter),
                new PropertyMetadata(false, new PropertyChangedCallback(OnCheckedChanged)));

            HandledProperty =
                DependencyProperty.Register(
                "Handled",
                typeof(bool),
                typeof(CustomCheckableCommandParameter),
                new PropertyMetadata(false));

            TagProperty =
                DependencyProperty.Register(
                "Tag",
                typeof(object),
                typeof(CustomCheckableCommandParameter),
                new PropertyMetadata((object)null));
        }


        public CustomCheckableCommandParameter()
            : base()
        {
        }

        private static void OnCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            CustomCheckableCommandParameter checkableParameter = (CustomCheckableCommandParameter)d;
            checkableParameter.OnIsCheckedChanged();
        }

        protected virtual void OnIsCheckedChanged()
        {
            if (IsCheckedChanged != null)
            {
                IsCheckedChanged(this, EventArgs.Empty);
            }

        }
    }
The second class that I've implemented is CustomRibbonTab. It extends the ActiproSoftware.Windows.Controls.Ribbon.Controls.Tab class. See code below.

public class CustomRibbonTab : ActiproSoftware.Windows.Controls.Ribbon.Controls.Tab
    {
        public CustomRibbonTab()
            : base()
        {
        }

        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            if (e.Property == CommandParameterProperty)
            {
                HookUpIsChecked();
            }

            base.OnPropertyChanged(e);
        }
        
        private void HookUpIsChecked()
        {
            var param = CommandParameter as CustomCheckableCommandParameter;

            if (null != param)
            {
                param.IsCheckedChanged += delegate
                {
                    if (param.IsChecked == true)
                    {
                        var ribbon = UIHelper.FindAncestor(this, typeof(Ribbon)) as Ribbon;
                        if (null != ribbon)
                        {
                            ribbon.SelectedTab = this; //in this way I'm selecting the right tab
                            //this.IsChecked = true; // this line doesn't select the right tab
                        }
                    }
                };
            }
        }       
    }
Having these 2 classes the XAML code will look this way:


 <!-- "Suppliers" Tab -->
<p:CustomRibbonTab Label="Suppliers" Command="NavigationCommands.GoToPage" CommandTarget="{Binding ElementName=local, Path=TargetContentContainer}">
<p:CustomRibbonTab.CommandParameter>
    <p:CustomCheckableCommandParameter Tag="{x:Type p:SuppliersListPage}"/>
</p:CustomRibbonTab.CommandParameter>
</p:CustomRibbonTab>
The solution I presented does what I expect, logically speaking. But the visual result is unexpected.
The line of code
ribbon.SelectedTab = this
produces a translation to the left of the ribbon tabs.

Any idea will be welcome.
Thanks.

Mark
Posted 15 years ago by Mike Strobel - Software Engineer, CDC Software
Avatar
Hi Mark,

Did you try the binding example I provided? It should cause the tab to be selected whenever the command parameter's IsChecked property is set to 'true'. If that doesn't work, you may need to bind to the IsSelected property instead of IsChecked:
<apribbon:RibbonTab x:Name="MyRibbonTab">
  <apribbon:RibbonTab.CommandParameter>
    <apribbon:CheckableCommandParameter IsChecked="{Binding ElementName=MyRibbonTab, Path=(Selector.IsSelected), Mode=OneWayToSource}" />
  </apribbon:RibbonTab.CommandParameter> 
  ...
</apribbon:RibbonTab>
You shouldn't need to create your own ICheckableCommandParameter or RibbonTab implementations, and it's possible that the latter is causing some unexpected complications.

Mike

[Modified at 01/07/2009 10:29 AM]
Posted 15 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Thanks for emailing the sample project over Mark. We've found the layout issue that was causing the problem whe you set SelectedTab. It's corrected for the next maintenance release.


Actipro Software Support

Posted 15 years ago by Mark Hanson
Avatar
Hi Mike,

I have tried your solutions but they don't work.
As I told you my solution works fine except that it introduces some unexpected UI behaviour (it's a bug in the ribbon). Actipro guys know about that and they will fix it for the next release.
Anyway, thanks for your time.

Best wishes,
Mark
The latest build of this product (v24.1.1) was released 2 months ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.