Giter VIP home page Giter VIP logo

Comments (18)

dymanoid avatar dymanoid commented on July 28, 2024 3

We have created our own FluentRibbonRegionAdapter for PRISM and use this way to enable the MVVM way of things.

from fluent.ribbon.

ZmorzynskiK avatar ZmorzynskiK commented on July 28, 2024 2

I've got a solution to this problem to be used in PrismLib, hopefully, it's valid.

In Prism, instead of Behavior we can use Adapter, just like @dymanoid mentioned.

So we can have an adapter for the Ribbon itself so that we can add single RibbonTabItems to it and we can have an adapter for RibbonTabItem so that we can add RibbonGroupBoxs to it, which enables even more granularity.

public class RibbonRegionAdapter : RegionAdapterBase<Fluent.Ribbon>
{
	public RibbonRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory) 
		: base(regionBehaviorFactory)
	{
	}

	protected override void Adapt(IRegion region, Fluent.Ribbon regionTarget)
	{
		// If control has child items, move them to the region
		if(regionTarget.Tabs.Count > 0)
		{
			foreach(object childItem in regionTarget.Tabs)
				region.Add(childItem);
		}
		
		// hookup for future changes
		region.Views.CollectionChanged += (sender, e) =>
		{
			switch(e.Action)
			{
				case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
					foreach(Fluent.RibbonTabItem item in e.NewItems)
						regionTarget.Tabs.Add(item);
					break;

				case System.Collections.Specialized.NotifyCollectionChangedAction.Remove:
					foreach(Fluent.RibbonTabItem item in e.OldItems)
						regionTarget.Tabs.Remove(item);
					break;

				case System.Collections.Specialized.NotifyCollectionChangedAction.Reset:
					{
						var views = sender as IViewsCollection;
						regionTarget.Tabs.Clear();
						foreach(Fluent.RibbonTabItem item in views)
							regionTarget.Tabs.Add(item);
						if(regionTarget.Tabs.Count > 0)
							regionTarget.SelectedTabIndex = 0; // force select first already
						break;
					}
			}
		};
	}

	protected override IRegion CreateRegion()
	{
		return new SingleActiveRegion();
	}
}

public class RibbonTabRegionAdapter : RegionAdapterBase<Fluent.RibbonTabItem>
{
	public RibbonTabRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory) 
		: base(regionBehaviorFactory)
	{
	}

	protected override void Adapt(IRegion region, Fluent.RibbonTabItem regionTarget)
	{
		// If control has child items, move them to the region
		if(regionTarget.Groups.Count > 0)
		{
			foreach(object childItem in regionTarget.Groups)
				region.Add(childItem);
		}
		
		// hookup for future changes
		region.Views.CollectionChanged += (sender, e) =>
		{
			switch(e.Action)
			{
				case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
					foreach(Fluent.RibbonGroupBox item in e.NewItems)
						regionTarget.Groups.Add(item);
					break;

				case System.Collections.Specialized.NotifyCollectionChangedAction.Remove:
					foreach(Fluent.RibbonGroupBox item in e.OldItems)
						regionTarget.Groups.Remove(item);
					break;

				case System.Collections.Specialized.NotifyCollectionChangedAction.Reset:
					{
						var views = sender as IViewsCollection;
						regionTarget.Groups.Clear();
						foreach(Fluent.RibbonGroupBox item in views)
							regionTarget.Groups.Add(item);
						break;
					}
			}
		};
	}

	protected override IRegion CreateRegion()
	{
		return new AllActiveRegion();
	}
}

Then you need to register them in your bootstrapper:

protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
{
	var mappings = base.ConfigureRegionAdapterMappings();
	if(mappings == null) return null;

	mappings.RegisterMapping(typeof(Fluent.Ribbon), 
		Microsoft.Practices.ServiceLocation.ServiceLocator.Current.GetInstance<RibbonRegionAdapter>());
	mappings.RegisterMapping(typeof(Fluent.RibbonTabItem),
		Microsoft.Practices.ServiceLocation.ServiceLocator.Current.GetInstance<RibbonTabRegionAdapter>());

	return mappings;
}

And then you just use this region as normal: so you create a Fluent:Ribbon element in XAML that has specified prism:RegionManager.RegionName and then you can use regular view registration within this region:

_regionManager.RegisterViewWithRegion(<name of the region>, typeof(<type of the Fluent:RibbonTabItem XAML control>));

(the same applies to registering Fluent:RibbonGroupBox XAML within Fluent:RibbonTabItem element)

You can also use ViewSortHint attribute on your ribbon views to have them sorted properly. That's because prism at some point calls NotifyCollectionChangedAction.Reset and views in IViewsCollection are sorted by this attribute (to be honest, the sorting should also be taken into account for NotifyCollectionChangedAction.Add action, but that will complicate things a little).

I briefly tested it and it looks ok. Hope this helps someone.

from fluent.ribbon.

philc71 avatar philc71 commented on July 28, 2024 1

I solved this issue with a behaviour, see below

                <fluent:Ribbon x:Name="Menu"
                               Grid.Row="0"
                               AutomaticStateManagement="False"
                               IsQuickAccessToolBarVisible="False">
                    <i:Interaction.Behaviors>
                        <behaviors:FluentRibbonTabBindingBehaviour Tabs="{Binding NavigationService.RibbonMenu}" />
                    </i:Interaction.Behaviors>
                </fluent:Ribbon>

public class FluentRibbonTabBindingBehaviour : Behavior
{
public static readonly DependencyProperty TabsProperty =
DependencyProperty.Register("Tabs", typeof(List),
typeof(FluentRibbonTabBindingBehaviour),
new FrameworkPropertyMetadata(null) { BindsTwoWayByDefault = true });

    public List<RibbonTabItem> Tabs
    {
        get { return (List<RibbonTabItem>)GetValue(TabsProperty); }
        set { SetValue(TabsProperty, value); }
    }

    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.Loaded += OnLoaded;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.Loaded -= OnLoaded;
    }

    void OnLoaded(object sender, RoutedEventArgs e)
    {
        if (Tabs != null && Tabs.Any())
        {
            AssociatedObject.Tabs.Clear();
            foreach (var tab in Tabs)
            {
                AssociatedObject.Tabs.Add(tab);
            }
        }
    }
}

from fluent.ribbon.

batzen avatar batzen commented on July 28, 2024

Also reported here: https://fluent.codeplex.com/workitem/22327

from fluent.ribbon.

MeirionHughes avatar MeirionHughes commented on July 28, 2024

I've just done a MVVM design with Microsoft's Ribbon and its near perfect with the use of DataTemplates and Styles. You pretty much bind up all the "collection" controls with ease. Just a shame its near impossible to re-style and re-template the dam thing and there is no backstage.

Fixing Fluent Ribbon so its more inline with System.Windows.Controls.Ribbon (Bindable and supports MVVM) would probably need a breaking change; especially with regard to the templates. Pretty much needs Ribbon to derive from Selector and the need to distinguish between the Item and the Item Container.

v4?

from fluent.ribbon.

batzen avatar batzen commented on July 28, 2024

As i already mentioned on codeplex, i got no plans to improve MVVM support myself.
I only use the ribbon at work and we built a factory which builds the bridge from MVVM to the ribbon for us. Sadly i am not allowed to share the code and my employer wouldn't be happy if i'd invest time improving MVVM support when we don't need it.

But as also mentioned before, if anyone has time to improve MVVM support i would be happy to accept pull requests. ;-)

from fluent.ribbon.

batzen avatar batzen commented on July 28, 2024

Nice to see that you found a solution but we have to support it native and not by adding our own way of databinding.

from fluent.ribbon.

chrisklepeis avatar chrisklepeis commented on July 28, 2024

The way I handle this is by using the Prism event aggregator with my Prism modules. When a module loads in the application, the module checks if the user has permission to a certain menu item, and if they do it publishes a message which is subscribed in the main window code behind, and the code behind has the logic to build the ribbon.

from fluent.ribbon.

ildoc avatar ildoc commented on July 28, 2024

@dymanoid can you please share your solution?
thanks :)

from fluent.ribbon.

znakeeye avatar znakeeye commented on July 28, 2024

@dymanoid can you please share your solution?

from fluent.ribbon.

noctis0430 avatar noctis0430 commented on July 28, 2024

Inspired by @philc71 's solution, I've manage to write another variant of behavior which uses CollectionChanged event of the observable collection to observe the ribbon tab items in the ViewModel. The general idea is that we subscribe to the CollectionChanged of the observable collection. Whenever items in the observable collection has changed, we will update the ribbon items accordingly. As mentioned by @batzen , this is not a native solution for the Ribbon.

Note that NotifyCollectionChangedAction.Replace and NotifyCollectionChangedAction.Reset is not implemented.

Here is the codes for the behavior:

public class RibbonBehaviour : Behavior<Ribbon>
{
    private static NotifyCollectionChangedEventHandler CollectionChangedEventHandler;

    private static void OnTabsChangeCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var behavior = d as RibbonBehaviour;
        var ribbon = behavior.AssociatedObject as Ribbon;
        var oldItems = e.OldValue as ObservableCollection<IRibbonTabItem>;
        var newItems = e.NewValue as ObservableCollection<IRibbonTabItem>;

        if (oldItems != null)
        {
            oldItems.CollectionChanged -= CollectionChangedEventHandler;
        }
        
        ribbon.Tabs.Clear();

        if (newItems != null)
        {
            newItems.CollectionChanged += CollectionChangedEventHandler;
            foreach (var item in newItems)
            {
                ribbon.Tabs.Add(item.View);
            }
        }
    }
    private static void OnTabsCollectionChanged(Ribbon ribbon, NotifyCollectionChangedEventArgs args)
    {
        switch (args.Action)
        {
            case NotifyCollectionChangedAction.Add:
                foreach (IRibbonTabItem item in args.NewItems)
                {
                    ribbon.Tabs.Add(item.View);
                }
                break;

            case NotifyCollectionChangedAction.Remove:
                foreach (IRibbonTabItem item in args.OldItems)
                {
                    ribbon.Tabs.Remove(item.View);
                }
                break;

            case NotifyCollectionChangedAction.Replace:
            case NotifyCollectionChangedAction.Reset:
                throw new NotImplementedException(string.Format("'{0}' action is not supported, yet", args.Action));
        }
    }
    
    public static readonly DependencyProperty TabItemsSourceProperty =
        DependencyProperty.Register("TabItemsSource", typeof(ObservableCollection<IRibbonTabItem>), typeof(RibbonBehaviour), new FrameworkPropertyMetadata(null, OnTabsChangeCallback) { BindsTwoWayByDefault = true });
    public ObservableCollection<IRibbonTabItem> TabItemsSource
    {
        get
        {
            return (ObservableCollection<IRibbonTabItem>)GetValue(TabItemsSourceProperty);
        }
        set
        {
            SetValue(TabItemsSourceProperty, value);
        }
    }
    
    protected override void OnAttached()
    {
        base.OnAttached();
        CollectionChangedEventHandler = (s, e) => OnTabsCollectionChanged(AssociatedObject, e);
    }
    protected override void OnDetaching()
    {
        base.OnDetaching();
        CollectionChangedEventHandler = null;
    }
}

Full solution (VS 2017) can be found here: BindableTabsDemo.zip

from fluent.ribbon.

violet701 avatar violet701 commented on July 28, 2024

@MeirionHughes Could you please give me an simple example or a template project about how you do MVVM design with Microsoft's Ribbon?I've just compare the fluent and the Microsoft's ribbon, but I cannot find a good way to implement mvvm by use them. This is my email:[email protected]. Thanks a lot!

from fluent.ribbon.

batzen avatar batzen commented on July 28, 2024

@violet701 may i ask why you prefer Microsoft's?

from fluent.ribbon.

violet701 avatar violet701 commented on July 28, 2024

@batzen sorry about my last comment. i am not mean that, but just what to know which one can use mvvm smoothly. i haven't decided which one to use in my project now

from fluent.ribbon.

batzen avatar batzen commented on July 28, 2024

@violet701 you don't have to be sorry. I'm just curious. MVVM support in the Microsoft Ribbon is definitely better than in Fluent.Ribbon but i had many deadlock issues during binding operations when i last used it. The custom window chrome from Microsoft will also cause you some headache i guess. And even Microsoft recently chose Fluent.Ribbon over their own Ribbon in the newer version of WinDBG. ;-)

from fluent.ribbon.

znakeeye avatar znakeeye commented on July 28, 2024

@ZmorzynskiK, does your adapter handle contextual tabs as well?

from fluent.ribbon.

ZmorzynskiK avatar ZmorzynskiK commented on July 28, 2024

@znakeeye I don't know to be honest, you will have to try it.

from fluent.ribbon.

batzen avatar batzen commented on July 28, 2024

Closing because no one seems interested in helping with this.

from fluent.ribbon.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.