-1

Here is my problem : I created a programm with Fluent ribbon, and when I want to disable a ribbon, I need to use the following code :

Code WPF :

<Fluent:RibbonGroupBox x:Name="GpRibbonFormats" ...>
  <Fluent:Button x:Name="AjoutTole" Header="{x:Static p:Resources.Ajouter}">
    <Fluent:Button.ToolTip>
      <Fluent:ScreenTip x:Name="ScreenTipAjoutTole"...>    
      </Fluent:ScreenTip>
    </Fluent:Button.ToolTip>
  </Fluent:Button>
  <Fluent:Button x:Name="EditQtyFormat" ...>
    <Fluent:Button.ToolTip>
      <Fluent:ScreenTip x:Name="ScreenTipEditQtyFormat"...>
      </Fluent:ScreenTip>
    </Fluent:Button.ToolTip>
  </Fluent:Button>
  <Fluent:Button x:Name="DeleteFormat" SizeDefinition="Large">
    <Fluent:Button.ToolTip>
      <Fluent:ScreenTip x:Name="ScreenTipDeleteFormat" ...>
      </Fluent:ScreenTip>
    </Fluent:Button.ToolTip>
  </Fluent:Button>
</Fluent:RibbonGroupBox>

Code Behind :

AjoutTole.IsEnabled = false;
ScreenTipAjoutTole.DisableReason = isBlocked;
EditQtyFormat.IsEnabled = false;
ScreenTipEditQtyFormat.DisableReason = isBlocked;
DeleteFormat.IsEnabled = false;
ScreenTipDeleteFormat.DisableReason = isBlocked;

It works fine but I would like to make a function like that, so I am sure I always send correct information in DisableReason :

DisableButton(Fluent:Button NameOfButton,string ReasonOfDisable)
{
    NameOfButton.IsEnabled = false;
    NameOfButton.AllScreenTipChild.DisableReason=ReasonOfDisable
}

The same way I would like to disable all a group of buttons :

DisableGroup(Fluent:RibbonGroupBox myGroup,string ReasonOfDisable)
{
    foreach(Fluent:Button button in myGroup)
    {
        button.isEnable=false;
        button.AllScreenTipChild.DisableReason=ReasonOfDisable;
    }
}

How such a thing is it possible?I want to be able to do it from codebehind.

Edit :

When trying to get the children of my button, I return one element of type System.Windows.Controls.Border, which name is "border", but I don't have such element in my XAML file. I also tried to get children of my RibbonGroupBox, but in that case I return one grid (grid2), and that grid is not even in the Ribbon...

Code used :

for (int i = 0; i < VisualTreeHelper.GetChildrenCount(DeleteOL); i++)
{
   var child = VisualTreeHelper.GetChild(DeleteOL, i);
   string monType = child.GetType().ToString();
   if(monType== "System.Windows.Controls.Border")
   {
      System.Windows.Controls.Border bb = (System.Windows.Controls.Border)child;
      string name = bb.Name;
   }
}

Edit 2 :

I confirm that getChild doesn't work on ribbon(why?), but I could find how to get list of buttons in a group :

foreach(var item in GpRibbonFormats.Items)
{
    if(item.GetType().ToString()=="Fluent.Button")
    {
         Fluent.Button button = (Fluent.Button)item;
         button.IsEnabled = false;
    }
}

Now I am still looking on how to find a button's ScreenTip

Siegfried.V
  • 1,508
  • 1
  • 16
  • 34
  • 2
    You should really consider using MVVM as it makes this kind of thing really easy. What's not working in your code, I don't see why it wouldn't work. – Roger Leblanc Feb 10 '18 at 17:55
  • My code works good, just would like to do it to make a unique function that change both button.IsEnable, and ScreenTip.DisableReason, for now for each button I have two lines of code, and as I have a lot of buttons, this would reduce my code. – Siegfried.V Feb 11 '18 at 12:31
  • Just edited question, so as you see I get strange results looking for children. If I do it inside the Grid(after Ribbon), the function works good. But inside Ribbon not – Siegfried.V Feb 12 '18 at 04:15

2 Answers2

2

You seem to mix namespace convention from XAML and C#, in C# you don't use : to reference a namespace, you use . separator instead. For example, StackPanel is inside System.Windows.Controls namespace, so you refer to it like this in C# :

System.Windows.Controls.StackPanel stackPanel = new System.Windows.Controls.StackPanel();

I never tried Fluent, but this code should work.

    public void DisableGroup(Fluent.RibbonGroupBox ribbonGroup, string reasonOfDisable)
    {
        foreach (var item in ribbonGroup.Items)
        {
            if (item is Fluent.Button)
            {
                DisableButton((Fluent.Button)item, reasonOfDisable);
            }
        }
    }

    public void DisableButton(Fluent.Button button, string reasonOfDisable)
    {
        button.IsEnabled = false;

        if (button.ToolTip is Fluent.ScreenTip)
        {
            Fluent.ScreenTip screenTip = (Fluent.ScreenTip)button.ToolTip;
            screenTip.DisableReason = reasonOfDisable;
        }
    }

To disable an entire group, you call it like this

    DisableGroup(GpRibbonFormats, "Ce groupe n'est pas disponible");

To disable only one button, you call it like this

    DisableButton(AjoutTole, "Ajouter est désactivé pour le moment");

By the way, Fluent.RibbonGroupBox inherits from ItemsControl, this control has its own IsEnabled property, you can probably disable an entire group by just setting the property to false (I've not tested it though), but you'll have to go through each button to set their screentip anyway.

    GpRibbonFormats.IsEnabled = false;

For this kind of thing, Binding are very powerful in WPF, you might want to read a bit on MVVM. It's not easy to implement at first, but once you get the hang of it, it's a game changer and really simplifies your code and logic.

Roger Leblanc
  • 1,543
  • 1
  • 10
  • 9
  • thanks it works perfect, in fact the missing part for me was (Fluent.ScreenTip)button.ToolTip; didn't think I could convert it like that. rearding binding I began to use it one or two months ago, I use it a lot for browsing listviews, but in the case of these buttons, I don't see how it would help me?In fact I can disable a full group just by set its value "IsEnabled", but in my case, I have many different conditions that can block a group, or just block one/several buttons, and not always the same. I see advantage of binding buttons if I always needed to block the same, but in that case? – Siegfried.V Feb 15 '18 at 06:55
  • or would you advise to make full MVVM? even when it seams not so necessary?just if I do so, I will have a big context, that's why for now I use binding only when necessary, if I want to display automatically some data modifies, in the case of these buttons, using binding doesn't make me save code (only then functions will be in context). Any advise of course is welcome. Also, I have 4 different WPF, and made one context file for both of them(4 different classes inside), would you then advise me to make 4 separate files? – Siegfried.V Feb 16 '18 at 03:33
  • MVVM is one way to achieve your goal, using code behind is another way. Both are valid, you decide which one you prefer. In MVVM, every buttons could be bound to a `Command`, the `CanExecute` of the `Command` would handle the `IsEnabled` property for you. For every button `ToolTip`, you could use a `MultiConverter` to bind to the parent group `IsEnabled` and `ToolTip` properties and choose between the parent `ToolTip` or a default value depending on the parent's `IsEnabled` value. MVVM will not make your code shorter, but it separate the view from the logic (ie: no need for ScreenTip cast). – Roger Leblanc Feb 16 '18 at 12:14
  • 1
    If one day you decide to change your `Fluent Ribbon` for another control, your `ViewModel` and `Converter` will not need to change at all, because it doesn't even know about the `Control` that are using its properties. – Roger Leblanc Feb 16 '18 at 12:17
  • Very clear thanks (In fact recently I changed my standard menu to Ribbon, and you're right this would have been a bit faster if I did like that), thanks for Ur help – Siegfried.V Feb 16 '18 at 13:02
  • Well it took time, but I finally understood that as you said, this method is not MVVM compatible... I believed if I use function "button.IsEnabled = false;", I then kept the link on my binding, but I noticed it removes the binding. So on the end I have no choice to use the binding only. My problem is sometimes I want to unlock the whole group, sometimes only one of the buttons... – Siegfried.V Jul 14 '18 at 04:37
1

It took time to me, but I finally understood what users were trying to explain to me (it is not obvious for somebody starting with MVVM, that's why I write it here).

I believed I could easily set my properties IsEnabled to true or false in the code(As in Roger Leblanc answer), then continue binding my ViewModel. It is not so, as when I set my IsEnable (to true) Property, it replaces IsEnabled="{Binding EnableEditNesting}" by IsEnabled=true, so after that no more binding is done(tell me if I am wrong).

On the end I did the following :

  • For the GroupBox that don't need different behaviour for each button, I just put a binding on its IsEnable parameter.

     <Fluent:RibbonGroupBox x:Name="GpRibbonFormats" IsEnabled="{Binding EnableGpRibbonFormats}" Header="{x:Static p:Resources.Stock}">
           <Fluent:RibbonGroupBox.ToolTip>
                 <Fluent:ScreenTip x:Name="ScreenTipGpRibbonFormats" Image="img\image_engrenage.png" Width="250" Text="{x:Static p:Resources.NestingSendToProduction}" DisableReason="{Binding EnableGpRibbonFormatsReason}">
    
                 </Fluent:ScreenTip>
           </Fluent:RibbonGroupBox.ToolTip>
           <Fluent:Button x:Name="AjoutTole" SizeDefinition="Large" LargeIcon="img\image_add.png" Header="{x:Static p:Resources.Ajouter}" Click="Add_ToleOL_Click">
    
           </Fluent:Button>
                    ...
     </Fluent:RibbonGroupBox>
    
  • For the GrouBox where I need specific behaviour on each button, I put a Binding for each of the buttons(nothing on the group), and when I need to disable all the group, I then disable buttons one by one.

    <Fluent:RibbonGroupBox x:Name="GpRibbonOL" Header="{x:Static p:Resources.NestingLaunchingOrder}">
         <Fluent:Button x:Name="DeleteOL" IsEnabled="{Binding EnableDeleteOL}" SizeDefinition="Large" LargeIcon="img\image_delete.png" Header="{x:Static p:Resources.Supprimer}" Click="Supprimer_OF">
              <Fluent:Button.ToolTip>
                  <Fluent:ScreenTip x:Name="ScreenTipDeleteOL" Image="img\image_delete.png" Title="Delete OL" Width="250" Text="Delete element" DisableReason="{Binding EnableEditNestingReason}">
                  </Fluent:ScreenTip>
              </Fluent:Button.ToolTip>
         </Fluent:Button>
         ...
    </Fluent:RibbonGroupBox>
    

ViewModel looks like that, so when I want to Enable/Disable, I just change the tooltip :

    private bool enableGpRibbonNesting;
    public bool EnableGpRibbonNesting
    {
        get { return enableGpRibbonNesting; }
        set
        {
            enableGpRibbonNesting = value;
            this.NotifyPropertyChanged("EnableGpRibbonNesting");
        }
    }
    private string enableGpRibbonNestingReason;
    public string EnableGpRibbonNestingReason
    {
        get { return enableGpRibbonNestingReason; }
        set
        {
            enableGpRibbonNestingReason = value;
            if (value == "")
            {
                EnableGpRibbonNesting = true;
            }
            else
            {
                EnableGpRibbonNesting = false;
            }
            this.NotifyPropertyChanged("EnableGpRibbonNestingReason");

        }
    }
Siegfried.V
  • 1,508
  • 1
  • 16
  • 34
  • Good job, this code will be easier to read for someone else looking at it. You're on the right track with MVVM! – Roger Leblanc Jul 14 '18 at 12:25
  • Thanks, do you think I may put my answer as the good one? because on the end, I understood your answer, even if it works fine(until you don't work with MVVM), is not a "good solution" (but a good tip). If you don't mind I'd put that one as good one. – Siegfried.V Jul 15 '18 at 18:25