fluentribbon / Fluent.Ribbon

WPF Ribbon control like in Office
http://fluentribbon.github.io
MIT License
2.56k stars 519 forks source link

Dynamic Ribbon Tabs #231

Closed Jcouls29 closed 8 years ago

Jcouls29 commented 8 years ago

I notice that MVVM support is not available right now, which I understand. So I tried going another route by having a base FluentWindow that would have a Ribbon switched in and out based on the ViewModel. So the Ribbon is switching in the same Window.

Looks like the back button (exit backstage) doesn't work after the Ribbon is replaced on the screen. I've tried a bunch of different things to no avail. Is this a bug? Is there another way to accomplish this?

batzen commented 8 years ago

Could you try the pre-release of version 4.0? If it does not work there too it would be nice if you could provide a repro for your issue.

Jcouls29 commented 8 years ago

I tried 4.0 Preview. No luck. So here is some info:

Main Window:

<fluent:RibbonWindow x:Class="MicrosoftStyleWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:fluent="clr-namespace:Fluent;assembly=Fluent"
        mc:Ignorable="d" WindowState="Maximized"
        Height="388.347" Width="708.687">
    <Grid>
        <ContentPresenter Content="{Binding}" />
    </Grid>
</fluent:RibbonWindow>

Then I have my two View Models:

Public Class EmptyViewModel : Inherits BaseViewModel
    ...
End Class

Public Class LoadedViewModel : Inherits BaseViewModel
    ...
End Class

My Views for the View Models:

Empty View

<UserControl x:Class="EmptyMainWindowView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:fluent="clr-namespace:Fluent;assembly=Fluent"
             xmlns:sys="clr-namespace:System;assembly=mscorlib"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <fluent:Ribbon x:Name="ribbon" Grid.Row="0">
            <!-- File Menu -->
            <fluent:Ribbon.Menu>
                <fluent:Backstage x:Name="FileMenu">
                    <fluent:Backstage.Style>
                        <Style TargetType="fluent:Backstage">
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding IsLoaded}" Value="False">
                                    <Setter Property="IsOpen" Value="True" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </fluent:Backstage.Style>
                    <fluent:BackstageTabControl>
                        <fluent:BackstageTabItem Header="New">
                            ...
                        </fluent:BackstageTabItem>

                        <fluent:BackstageTabItem Header="Open">
                            ...
                        </fluent:BackstageTabItem>
                        <fluent:SeparatorTabItem />
                        <fluent:BackstageTabItem Header="Options">
                            ...
                        </fluent:BackstageTabItem>
                    </fluent:BackstageTabControl>
                </fluent:Backstage>
            </fluent:Ribbon.Menu>
            <fluent:Ribbon.QuickAccessItems>
                <fluent:QuickAccessMenuItem IsChecked="True" IsEnabled="True">
                    <fluent:Button Header="Save" Icon="..\Icons\Save.png" Command="{Binding SaveCommand}" />
                </fluent:QuickAccessMenuItem>
                <fluent:QuickAccessMenuItem>
                    <fluent:Button Header="Undo" Icon="..\Icons\Undo.png" Command="{Binding CommandManager.UndoCommand}" />
                </fluent:QuickAccessMenuItem>
                <fluent:QuickAccessMenuItem>
                    <fluent:Button Header="Redo" Icon="..\Icons\Redo.png" Command="{Binding CommandManager.RedoCommand}" />
                </fluent:QuickAccessMenuItem>
            </fluent:Ribbon.QuickAccessItems>
            <fluent:Ribbon.Tabs>
                <fluent:RibbonTabItem Header="Home">
                    ...
                </fluent:RibbonTabItem>
            </fluent:Ribbon.Tabs>
        </fluent:Ribbon>
        ... Other Content ..
    </Grid>
</UserControl>

Loaded View

<UserControl x:Class="LoadedMainWindowView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:fluent="clr-namespace:Fluent;assembly=Fluent"
             xmlns:c="clr-namespace:System;assembly=mscorlib"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <fluent:Ribbon x:Name="ribbon" Grid.Row="0">
            <fluent:Ribbon.ContextualGroups>
                <fluent:RibbonContextualTabGroup Header="Variable"  x:Name="VariableGroup" Background="Green" BorderBrush="Green">
                    <fluent:RibbonContextualTabGroup.Style>
                        <Style TargetType="{x:Type fluent:RibbonContextualTabGroup}">
                            <Setter Property="Visibility" Value="Visible" />
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding SelectedItem}" Value="{x:Null}">
                                    <Setter Property="Visibility" Value="Hidden" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </fluent:RibbonContextualTabGroup.Style>
                </fluent:RibbonContextualTabGroup>
            </fluent:Ribbon.ContextualGroups>

            <!-- File Menu -->
            <fluent:Ribbon.Menu>
                <fluent:Backstage x:Name="FileMenu">
                    <fluent:Backstage.Style>
                        <Style TargetType="fluent:Backstage">
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding IsLoaded}" Value="False">
                                    <Setter Property="IsOpen" Value="True" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </fluent:Backstage.Style>
                    <fluent:BackstageTabControl>
                        <fluent:Button Header="Save" Command="{Binding SaveCommand}" IsEnabled="{Binding NeedToSave}" />
                        <fluent:BackstageTabItem Header="Save As" Visibility="Collapsed">
                            ...
                        </fluent:BackstageTabItem>
                        <fluent:Button Header="Close" Command="{Binding CloseCommand}" />
                        <fluent:SeparatorTabItem />
                        <fluent:BackstageTabItem Header="Options">
                            ...
                        </fluent:BackstageTabItem>
                    </fluent:BackstageTabControl>
                </fluent:Backstage>
            </fluent:Ribbon.Menu>

            <fluent:Ribbon.QuickAccessItems>
                <fluent:QuickAccessMenuItem IsChecked="True" IsEnabled="True">
                    <fluent:Button Header="Save" Icon="..\Icons\Save.png" Command="{Binding SaveCommand}" IsEnabled="{Binding NeedToSave}" />
                </fluent:QuickAccessMenuItem>
                <fluent:QuickAccessMenuItem>
                    <fluent:Button Header="Undo" Icon="..\Icons\Undo.png" Command="{Binding CommandManager.UndoCommand}" />
                </fluent:QuickAccessMenuItem>
                <fluent:QuickAccessMenuItem>
                    <fluent:Button Header="Redo" Icon="..\Icons\Redo.png" Command="{Binding CommandManager.RedoCommand}" />
                </fluent:QuickAccessMenuItem>
            </fluent:Ribbon.QuickAccessItems>

            <fluent:RibbonTabItem Header="Home" IsOpen="True">
                ...
            </fluent:RibbonTabItem>

            <fluent:RibbonTabItem Header="Resources">
                ...
            </fluent:RibbonTabItem>

            <fluent:RibbonTabItem Header="Variable Text" Group="{Binding ElementName=VariableGroup}">
                <fluent:RibbonTabItem.Style>
                    <Style TargetType="fluent:RibbonTabItem">
                        <Setter Property="Visibility" Value="Visible" />
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding SelectedItem}" Value="{x:Null}">
                                <Setter Property="Visibility" Value="Hidden" />
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </fluent:RibbonTabItem.Style>
                ...
            </fluent:RibbonTabItem>
        </fluent:Ribbon>
        ... Other Content ...
    </Grid>
</UserControl>

Data Templates

    <DataTemplate DataType="{x:Type local:EmptyViewModel}">
        <local:EmptyMainWindowView />
    </DataTemplate>

    <DataTemplate DataType="{x:Type local:LoadedViewModel}">
        <local:LoadedMainWindowView />
    </DataTemplate>

The datacontext of the main window is set at application startup and changed via a message bus. Unless necessary, I don't want to post that code. So when the application first starts up, the context is set to the Empty View Model and thus the Empty View is loaded with its ribbon. The back button works. When I load something and switch to the Loaded View Model, the Loaded View is loaded with the "new" ribbon. If I go to the File Menu and click the back button, it does NOT close. I can close it via "ESC" key though.

Hope this is enough information.

batzen commented 8 years ago

Without trying to reproduce the issue using your code I guess the fix is contained in the waiting PR. Will apply those changes so you can try if it fixes your problem.

Jcouls29 commented 8 years ago

Seems like it fixed the main issue. As a side note, some of the Animation(s) stopped working (Fade/Slide) but this is good enough for me. Thanks a lot!

batzen commented 8 years ago

The animations are disabled if the windows system animation-settings are set to disabled as we now fully rely on the values provided by the SystemParameters class.

Jcouls29 commented 8 years ago

Gotcha. I'll keep that in mind. However, The animations work on the first Ribbon and not the second. I figured it was probably related. It closes now, but the animation on the second doesn't. Thanks again

batzen commented 8 years ago

Will have a look at the missing animation on second ribbon in a few days. Will reopen this issue to keep track of the remaining issue.

batzen commented 8 years ago

I can't reproduce your missing animation issue. Could you please attach a small sample application showing your issue?

Jcouls29 commented 8 years ago

You'll have to give me a little bit to get it to you. Busy. But here are the message(s) coming back at me:

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='Fluent.Backstage', AncestorLevel='1''. BindingExpression:Path=IsOpenAnimationEnabled; DataItem=null; target element is 'BackstageTabControl' (Name=''); target property is 'NoTarget' (type 'Object') System.Windows.ResourceDictionary Warning: 9 : Resource not found; ResourceKey='Fade' System.Windows.ResourceDictionary Warning: 9 : Resource not found; ResourceKey='Fade'

I believe it has something to do with how I bind to the view(s). Because, I'm switching out the ribbon multiple times, it may be messing with the binds. I had a similar problem already with this and was able to fix it by using DataTemplates (before I was setting the DataContext in code and it caused issues).

batzen commented 8 years ago

Ok, now I at least get why the popup animations are missing. I used the properties from SystemParameters whereas I had to use the reource keys. That's fixed now.

The missing ancestor is strange. Do you only get that error message for IsOpenAnimationEnabled or also for IsOpen?

Jcouls29 commented 8 years ago

Yea. Only IsOpenAnimationEnabled. IsOpen never pops up with an error. It is a bit odd. I'm not sure yet how to fully reproduce in a simpler app. I'll let you know if I figure out how. It seems to work with simpler apps.

batzen commented 8 years ago

It's really strange as IsOpen and IsOpenAnimationEnabled are used in the same place and both are bound to the same ancestor...

Jcouls29 commented 8 years ago

Give me a day or two. I'll try to pull something together

batzen commented 8 years ago

I also get that binding error in the showcase application but it works fine there. Has to be some bug in WPF itself as IsOpen is found without problems. And the animations really don't work in your application?

Jcouls29 commented 8 years ago

Unfortunately, no. They work initially (The first ribbon). But when the new ribbon is switched in (like in the example submitted last week), it doesn't work. It's obvious too. It just disappears and appears immediately.

batzen commented 8 years ago

Ok, then I really need as small repro.

batzen commented 8 years ago

Any news?

Jcouls29 commented 8 years ago

Couldn't reproduce it in a simple app. And I can't send my current one of course. If I ever can get it to reproduce, I'll open up the issue again.

batzen commented 8 years ago

You could send it directly to my e-mail adress. Others already did that when they were unable to create a public repro.

Jcouls29 commented 8 years ago

Wish I could. But I recreated it with as much as I could of the original software and couldn't get it to break. It does right now with everything involved. Unfortunately, proprietary company software. Can't send out under any circumstances. If I can find a way to dup it, I will definitely let you know.