WPF - get all children from code

1.8k Views Asked by At

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

2

There are 2 best solutions below

2
Siegfried.V On BEST ANSWER

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");

        }
    }
6
Roger Leblanc On

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.