Common toolbuttons/menu items (e.g. Save)

Topics: CAB & Smart Client Software Factory
Aug 3, 2006 at 5:04 AM
originally posted by: amackie

If I have multiple modules, they may all have certain functionality, e.g. Save, that should be represented by single toolbutton/menu-item. Whenever that command is invoked, it should be sent to the currently active module/work-item.

How do I share common toolbuttons/menu-items across multiple modules ?

I would hate to have 10 different Save buttons for 10 different modules!

Thanks,
Andy
Aug 3, 2006 at 6:24 AM
originally posted by: ChrisHolmes

Andy,

I can only speak from my experience, but I have found that it is very difficult to have one "Save" button for the entire CAB Application. In my particular app, we have sub-WorkItems within Modules, and it gets very complicated very quickly about what the "Save" button should be doing.

Instead of trying to manage one "Save" button, we have each WorkItem manage its own ToolStrip buttons. Each time a WorkItem is activated it clears the ToolStrip and loads its own buttons, which then have their own specific commands tied to them. Then it becomes very easy to know what to do when that particular "Save" button is clicked because there is a local CommandHandler to take care of it.

The only other way I could think of to handle this would be to create a Service that tracks the currently active WorkItem. Each time a WorkItem becomes the active WorkItem (via focus or some other tool; in our case we track the currently active one via the DeckWorkspaces) then it notifies the service that it is the new currently active WorkItem. When the Save button is fired every WorkItem that had a CommandHandler to handle the Save operation could first check the service to see if the currently active WorkItem is a match for itself - and if it is, then go forth with the save operation.
Aug 4, 2006 at 9:36 PM
originally posted by: pyhavaim

Well this is how I've done this and it seems to work very nice

Fist I noticed that there are some smartparts that acting similary (data insertion and display and navigation)
So I made simple DataViewer control witch is responcible to notify shell layout presenter witch view is currently activated and to bind this to dataNavigator toolbar object.

Basicaly this class only throws events to RootWorkitem "Hey I'm active", and "Bye" :)

public partial class DataViewer : UserControl, IDataView
{
BindingSource _bindingsSource;

EventPublication(EventTopicNames.OnDataViewDeactivated, PublicationScope.Global)
public event EventHandler<EventArgs> OnDataViewDeactivated;

EventPublication(EventTopicNames.OnDataViewActivated, PublicationScope.Global)
public event EventHandler<DataEventArgs<DataViewer>> OnDataViewActivated;

private void DataView_Enter(object sender, EventArgs e)
{
OnOnDataViewActivated(e);
}
private void DataView_Leave(object sender, EventArgs e)
{
OnOnDataViewDeactivated(e);
}
...
//On activateing this function declares Whitch view or smartpart is currently active
protected virtual void OnOnDataViewActivated(EventArgs eventArgs)
{
if (OnDataViewActivated != null)
{
OnDataViewActivated(this, new DataEventArgs<DataViewer>(this));
}
}

protected virtual void OnOnDataViewDeactivated(EventArgs eventArgs)
{
// ... some invalidation stuff before exiting the view
if (OnDataViewDeactivated != null)
{
OnDataViewDeactivated(this, eventArgs);
}
}

Now in Layout Presenter, witch is the shell form presenter
there I'm subscribing those events by
Notice I have DataEvent arg as a DataViewer object

EventSubscription(EventTopicNames.OnDataViewActivated, ThreadOption.UserInterface)
public void OnDataViewActivated(object sender, DataEventArgs<DataViewer> eventArgs)
{
_activeView = eventArgs.Data;
View.ShowNavigator(_activeView.BindingSource);
}

So this presenter knows the active view and I could in presenter easily call some functions in DataViewer.
like
public void OnClick()
{
_activeView.Save();
}
EventSubscription(EventTopicNames.OnDataViewDeactivated, ThreadOption.UserInterface)
public void OnOnDataViewDeactivated(object sender, System.EventArgs eventArgs)
{
View.HideNavigator();
}

Basically that's it...

And in inhherited DataViewer is only responsible to set the databinding property for DataViewer

SmartPart
public partial class ProductList : Infrastructure.Common.Components.Data.DataViewer, IProductView
{
...
private void ProductList_Load(object sender, EventArgs e)
{
productsBindingSource.DataSource = _presenter.LoadProducts();
base.BindingSource = productsBindingSource;
}
public override void SaveData()
{
MessageBox.Show("Save Product");
}
Aug 8, 2006 at 1:23 AM
originally posted by: amackie

OK thanks. The first way, of each item clearing the toolbar when it's activated and adding it's own commands is how we are doing things at present. To the user, it looks like there is just one Save button, for example.

What are other people's experience of tackling this problem ?
Aug 9, 2006 at 5:02 AM
originally posted by: pyhavaim

If each view has its own toolbar I use "Patching"

I just create a toolbar on the view.
In the LayoutPresenter (controls the main layout view) I subscribe 2 events Show and hide toolbar

EventSubscription(EventTopicNames.ShowToolstripInShell, ThreadOption.UserInterface)
public void OnShowToolstripInShell(object sender, DataEventArgs<ToolStrip> eventArgs)
{
View.ShowToolStrip(eventArgs.Data);
}

EventSubscription(EventTopicNames.HideToolstripInShell, ThreadOption.UserInterface)
public void OnHideToolstripInShell(object sender, DataEventArgs<ToolStrip> eventArgs)
{
View.HideToolStrip(eventArgs.Data);
}

In layout form ...

public void ShowToolStrip(ToolStrip tool)
{
if (this.toolStripContainer1.TopToolStripPanel.Controls.Contains(tool) == false)//if there is not such control then add it else display
{
this.toolStripContainer1.TopToolStripPanel.Controls.Add(tool);
tool.Parent = this.toolStripContainer1.TopToolStripPanel; //And here is the tric!!! Here main layout toolstrip container declares as parent of toolstrip of the another view.
toolStripContainer1.Invalidate();
}
else
{
tool.Visible = true;
}
}

public void HideToolStrip(ToolStrip tool)
{
//this.toolStripContainer1.TopToolStripPanel.Controls.Remove(tool);
tool.Visible = false;
}


In custom view presenter I have 2 publishing events...

EventPublication(EventTopicNames.ShowToolstripInShell, PublicationScope.Global)
public event EventHandler<DataEventArgs<ToolStrip>> ShowToolStrip;

EventPublication(EventTopicNames.HideToolstripInShell, PublicationScope.Global)
public event EventHandler<DataEventArgs<ToolStrip>> HideToolStrip;
...

And simply in the view (Where I have designed the toolbar)
private void RaportViewer_Leave(object sender, EventArgs e)
{
_presenter.HideMenu(toolStrip1);
}

private void RaportViewer_Enter(object sender, EventArgs e)
{
_presenter.ShowMenu(toolStrip1);
}
Feb 9, 2007 at 9:17 PM
We've had a similar issue. The easiest solution I've thought of is to check WorkItem status in the event handler. (i.e. If WorkItem.Status = Active Then ...). Since we are also having command handlers in the smartparts, I created an interface that each smartpart can implement. When the workitem is activated, the workitem will activate it's current smartpart. Then the smartpart can check a status of Active/Inactive. This seems to work pretty well, plus it is very straight forward.
Feb 22, 2007 at 1:22 PM
Edited Feb 22, 2007 at 1:22 PM
I wrote some extention to commands http://rapidupload.co.uk/download.php?file=991473. I think it can help you to solve this problem. The main idea of extention is to create one menu/toolbar button that many workitems/views can register to it. Each one create a nandler like
CommandExHandler("ZZ")
public void OnZZ(object sender, EventArgs e)
{}

Also user can add conditions to this method. Like workitem is active, so CommandEx will call to this method only if condition is true. So developper do not need add condition to code. You can see in example project how it work.
I hope it will help you.
Feb 22, 2007 at 5:18 PM
I believe that what you need is an Action. The workspace that contains the smartparts can register an action implementaion and through that make the click event of the tool fire that action with the tool you need.

Then, the workspace can fire the click event for the active smart part.