In this application I placed a Menu inside a Usercontrol.
<UserControl>
<Grid>
<Menu x:Name="mainMenu">
<MenuItem Header="File">
<MenuItem Header="Open Analysis">
<MenuItem Header="Load all" />
<MenuItem Header="Select from tracks" />
</MenuItem>
</MenuItem>
</Menu>
</Grid>
</UserControl>
I would like to route and bubble all "click" events on the menu to a single Click event in the Usercontrol.
So I implemented:
public static readonly RoutedEvent ClickEvent = eventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MenuItem));
public event RoutedEventHandler Click
{
add { AddHandler(ClickEvent, value); }
remove { RemoveHandler(ClickEvent, value); }
}
But that fails on the exception:
{"RoutedEvent Name 'Click' for OwnerType 'System.Windows.Controls.MenuItem'
already used."}
I suppose this is the event is registered multiple time's since there are multiple MenuItems, so it fails the second time it wants to register.
But how can I "bundle" (like a Singular handler attachment point) all those events into one single event in the usercontrol in a good mainainanble way? Since some submenus are fed by the Database thus are dynamically build-up.
In addition to what's already been said, I would look into a simple messaging solution.
Each time you click on any menu item, you can broadcast a message of type MenuItemClickedMessage.
public class MenuItemClickedMessage
{
public MenuItem MenuItemClicked { get; set; }
}
You can then have a single component that listens to that event. Once it receives the event, you can extract the menu item from MenuItemClicked property and react accordingly.
This will require some research into simple messaging solutions: http://rachel53461.wordpress.com/2011/05/28/switching-between-viewsusercontrols-using-mvvm/
Contrary to intuition, the error you are receiving is not due to multiple menu items, but due to the Click event already existing. The event registration is static, and it is already used by the WPF Framework.
How would you solve this?
You can create a style for your menu; styles can contain EventSetters in WPF. This allows you to register a click handler for any menu item in your container:
<Grid>
<Grid.Resources>
<Style TargetType="MenuItem">
<EventSetter Event="Click" Handler="OnMenuItemClick" />
</Style>
</Grid.Resources>
<Menu x:Name="mainMenu">
...
</Menu>
</Grid>
Then in your handler make sure to set the Handled property to true, to stop bubbling:
private void OnMenuItemClick(object sender, RoutedEventArgs e)
{
e.Handled = true;
}
Update
You should consider not firing the 'Click' event on the UserControl for this, since it could be a design issue: when someone uses your UserControl and the Click event is fired on the user control, you expect a 'Click', not a click on a menu item inside the control.
Consider using a custom event name instead, something like MenuItemClick, where you can supply the identifier of the menu item in the event args.
Although I would strongly suggest not to do this, but instead declare a distinct new event as Bas has pointed out, you could simply reuse the existing RoutedEvent from class MenuItem:
public partial class YourUserControl : UserControl
{
...
public event RoutedEventHandler Click
{
add { AddHandler(MenuItem.ClickEvent, value); }
remove { RemoveHandler(MenuItem.ClickEvent, value); }
}
}
MenuItemClick - is a routed event, and use RoutedEventArgs.OriginalSource, rather than sender. This points to the control that originally fired the event.
<Grid>
<Menu x:Name="mainMenu">
<MenuItem Header="File" Click="OnFileMenuItemClicked">
<MenuItem Header="Open Analysis">
<MenuItem Header="Load all" />
<MenuItem Header="Select from tracks" />
</MenuItem>
</MenuItem>
</Menu>
</Grid>
private void OnFileMenuItemClicked(object sender, RoutedEventArgs e)
{
MenuItem item = e.OriginalSource as MenuItem;
if(null != item)
{
// Handle the menuItem click here
}
}
Related
There is an application I want to make using C#. This is part of it's UI:
The white area below the menu bar represents a TreeView. I want the Tree_View menu to appear everywhere in the white area when right-clicking with the mouse.
This is the XAML code for the Tree_View object (mind the foo function there):
<MenuItem Name="Menu_Tree" Header="_Tree_View">
<MenuItem Header="_New_Scene" IsCheckable="false" Click = foo/>
<MenuItem Header="_Copy_This_Scene" IsCheckable="false"/>
<MenuItem Header="_Remove_This_Scene" IsCheckable="false"/>
<Separator />
<MenuItem Header="_New_Shot" IsCheckable="false"/>
<MenuItem Header="_Copy_This_Shot" IsCheckable="false"/>
<MenuItem Header="_Remove_This_Shot" IsCheckable="false"/>
<Separator />
<MenuItem Header="_Move_This_Shot_Up" IsCheckable="false"/>
<MenuItem Header="_Move_This_Shot_Down" IsCheckable="false"/>
</MenuItem>
and this is the XAML code for the Tree_View object:
<Grid Name="TreeHolder" Column="0" Margin="20,10,10,10" Background="DimGray">
<TreeView Name="myTree" MouseRightButtonDown="something" ToolTip="Right Click to Add or Remove Scenes and Scots.">
</TreeView>
</Grid>
This is the something function which should be triggered by right-click:
private void something(object sender, MouseButtonEventArgs e)
{
ContextMenu cm = new ContextMenu();
//cm.Items.Add("Add a New Scene ?", ... );
Menu_Tree.ContextMenu = cm;
}
Since nothing works there, I want to ask the following:
a) How to make the Tree_view menu items appear in the Tree_View object white area as well?
b) If so, how will I make it an enabled menu, for example being able to trigger the foo function?
(In other worlds, make an exact copy of the menu list, make it visible with right click and make it work as well)
I strongly believe that it has to do with the ContextMenu which I am not able to use fairly, so any help would be highly appreciated.
The items to add are MenuItems. You can create each item as you would with any other object, set its Header property and assign a Click event handler. The sender is the TreeView itself.
private void something(object sender, MouseButtonEventArgs e)
{
var cm = new ContextMenu();
var newSceneMenuItem = new MenuItem { Header = "_New_Scene" };
newSceneMenuItem.Click += OnNewSceneClick;
cm.Items.Add(newSceneMenuItem);
var treeView = (TreeView) sender;
treeView.ContextMenu = cm;
}
private void OnNewSceneClick(object sender, RoutedEventArgs e)
{
MessageBox.Show("I am a Message Box.", "New Scene clicked");
}
However, you can do this much easier in XAML without the right click handler.
<Grid Name="TreeHolder" Column="0" Margin="20,10,10,10" Background="DimGray">
<TreeView Name="myTree" ToolTip="Right Click to Add or Remove Scenes and Scots.">
<TreeView.ContextMenu>
<ContextMenu>
<MenuItem Header="_New_Scene" Click="foo"/>
<MenuItem Header="_Copy_This_Scene"/>
<MenuItem Header="_Remove_This_Scene"/>
<Separator />
<MenuItem Header="_New_Shot"/>
<MenuItem Header="_Copy_This_Shot"/>
<MenuItem Header="_Remove_This_Shot"/>
<Separator />
<MenuItem Header="_Move_This_Shot_Up"/>
<MenuItem Header="_Move_This_Shot_Down"/>
</ContextMenu>
</TreeView.ContextMenu>
</TreeView>
</Grid>
You do not need to set the IsCheckable to false, that is already the default value. Instead of assigning a Click event handler you could use commands, but I guess this would be a too advanced topic for you question, since it does not look like you are employing the MVVM pattern.
I am making an app that has a notify icon in WPF. I am using HardCodet NotifyIcon. They do have a tutorial on code project and it is pretty useful but it does not have any explanation on how to set up OnClick or Click event when the buttons in the context menu are pressed.
I have gone through every property in NotifyIcon.ContextMenu.Items and NotifyIcon.ContextMenu.Items.GetItemAt(i) (TaskbarIcon NotifyIcon = (TaskbarIcon) FindResource("MyNotifyIcon")) but there is nothing I found. I also tried typecasting the buttons to MenuItem and using its Click event but it didn't help.
This is my App.xaml:
<Application.Resources>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:tb="http://www.hardcodet.net/taskbar">
<tb:TaskbarIcon x:Key="MyNotifyIcon"
ToolTipText="Hello There">
<tb:TaskbarIcon.ContextMenu>
<ContextMenu Background="White">
<MenuItem Header="Open"/>
<MenuItem Header="Settings"/>
<MenuItem Header="Sign Out"/>
<MenuItem Header="Help"/>
<MenuItem Header="Exit"/>
</ContextMenu>
</tb:TaskbarIcon.ContextMenu>
</tb:TaskbarIcon>
</ResourceDictionary>
</Application.Resources>
I need the buttons to control the MainWindow e.g. change the Visibility etc..
There is no difference to other controls, you can just set up a Click handler on each MenuItem.
<MenuItem Header="Open" Click="Open_OnClick"/>
In your example, you would implement the event handler in App.xaml.cs.
public partial class App : Application
{
// ...application code.
private void Open_OnClick(object sender, RoutedEventArgs e)
{
// ...do something.
}
}
You could also assign a view model as DataContext to TaskbarIcon and use a command instead.
In WPF I can define a Command for copying content like this:
<MenuItem Header="Copy" Command="ApplicationCommands.Copy" >
Works great and the menu item is only enabled if there is something to copy.
I want to make another MenuItem that is also only enabled if there is something to copy in my app. How can I copy that behavior?
If I'm reading your question correctly, then the two menu items have the same criteria for if they are enabled. If that is the case, I think you could just bind your second menu item's IsEnabled property to the first menu item's IsEnabled property.
(Just confirmed that this works for me).
As an example:
<MenuItem x:Name="CopyItem" Header="Copy" Command="ApplicationCommands.Copy"/>
<MenuItem Header="Something Else" IsEnabled="{Binding ElementName=CopyItem, Path=IsEnabled}"/>
You can use the same command "Copy", and distinguish between two different menu items by CommandParameter:
<MenuItem Header="Copy" Command="ApplicationCommands.Copy" CommandParameter="PlainCopy">
<MenuItem Header="TrickyCopy" Command="ApplicationCommands.Copy" CommandParameter="TrickyCopy">
Then in Command handler check parameter:
private void OnCopy(object sender, ExecutedRoutedEventArgs e)
{
if (e.Parameter == 'PlainCopy')
{
...
}
else
{
...
}
}
I have a resource dictionary inside which I have a context menu:
<ResourceDictionary x:Class="MyApp.Components.MyContextMenu"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyApp"
xmlns:components="clr-namespace:MyApp.Components">
<ContextMenu ContextMenuOpening="OnContextMenuOpening">
and the resource dictionary XAML has this code behind:
using System;
using System.Windows;
using System.Windows.Controls;
namespace MyApp.Components
{
public partial class MyContextMenu : ResourceDictionary
{
public MyContextMenu()
{
InitializeComponent();
}
void OnContextMenuOpening(object sender, ContextMenuEventArgs e)
{
Console.WriteLine("here i am!");
}
}
}
The log is not appearing. I wonder why the event is not firing or getting to the right place -- is the problem because I have wrapped the context menu inside this resource dictionary?
Update: Interestingly if I remove the code-behind function, I get an error during compilation:
does not contain a definition for 'ContextMenu_OnContextMenuOpening'
and no extension method 'ContextMenu_OnContextMenuOpening' accepting a
first argument of type 'MyApp.Components.MyContextMenu' could be found
(are you missing a using directive or an assembly reference?)
Update 2: Looks like that both Console.WriteLine and Debug.WriteLine produce output, but only "randomly" and especially when I'm clicking near the bottom of the item. Some sort of collision detection not working maybe?
ContextMenuOpening event must be handled on an ancestor of the ContextMenu not on the ContextMenu itself. If you try handling it on the ContextMenu the event only fires when you right click once ContextMenu is already open.
It is a bug in the framework: http://connect.microsoft.com/VisualStudio/feedback/details/353112/contextmenu-opening-event-doesnt-fire-properly
A contextmenu's opening event doesn't fire on the first right click.
It only fires when you do two sequential right clicks while not moving
the mouse.
I believe kurrazyman has the right answer, but it took me a while to understand it.
In my case I had a TreeView control with a context menu.
Using myTreeView.ContextMenu.ContextMenuOpening didn't work, but using myTreeView.ContextMenuOpening did.
Its not a bug, it is working... here is the most common mistake that most people is doing with ContextMenuOpening event... consider this two different scenario to figure out the actual cause of this problem,
SCENARIO 1 (This will not work):
<ListBox Name="lb_sizes" Height="120">
<ListBox.ContextMenu>
<ContextMenu ContextMenuOpening="My_ContextMenuOpening">
<MenuItem Header="Delete"/>
<MenuItem Header="Delete All"/>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
SCENARIO 2 (This will work):
<ListBox Name="lb_sizes" Height="120" ContextMenuOpening="My_ContextMenuOpening">
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete"/>
<MenuItem Header="Delete All"/>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
The only difference is assigning ContextMenuOpening event to proper element...
in scenario 1 it is assigned (attached) to <ContextMenu> element and in scenario 2, it is assigned to <ListBox> element which is proper way to do it and should work.
I'm using IsVisibleChanged event instead:
private void ContextMenu_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var isVisible = (bool)e.NewValue;
if (isVisible)
{
//...
}
}
Are dynamic resources truly dynamic? If I define a DynamicResource, I realise that an expression is created (where?) that is not translated into a resource until runtime, however, What I do not understans is whether this dynamicresouce, once built, is now "Static"
For instance, if I create a context menu via a dynamicresource, are the menuitems which are created at runtime on access then static, even if they are bound?
If so, how can i create a dynamic context menu in XAML?
This is a very complex subject because there are so many kinds of dynamism within WPF. I will start with a simple example to help you understand some basic concepts you need, then proceed to explain the various ways in which a ContextMenu can be dynamically updated and/or replaced, and how DynamicResource fits into the picture.
Initial example: Dynamically updating ContextMenu referenced through StaticResource
Let's say you have the following:
<Window>
<Window.Resources>
<ContextMenu x:Key="Vegetables">
<MenuItem Header="Broccoli" />
<MenuItem Header="Cucumber" />
<MenuItem Header="Cauliflower" />
</ContextMenu>
</Window.Resources>
<Grid>
<Ellipse ContextMenu="{StaticResource Vegetables}" />
<TextBox ContextMenu="{StaticResource Vegetables}" ... />
...
</Grid>
</Window>
** Note the use of StaticResource for now.
This XAML will:
Construct a ContextMenu object with three MenuItems and add it to Window.Resources
Construct an Ellipse object with a reference to the ContextMenu
Construct a TextBox object with a reference to the ContextMenu
Since both the Ellipse and the TextBox have references to the same ContextMenu, updating the ContextMenu will change the options available on each. For example the following will add "Carrots" to the menu when a button is clicked.
public void Button_Click(object sender, EventArgs e)
{
var menu = (ContextMenu)Resources["Vegetables"];
menu.Items.Add(new MenuItem { Header = "Carrots" });
}
In this sense every ContextMenu is dynamic: Its items can be modified at any time and the changes will immediately take effect. This is true even when the ContextMenu is actually open (dropped down) on the screen.
Dynamic ContextMenu updated through data binding
Another way in which a single ContextMenu object is dynamic is that it responds to data binding. Instead of setting individual MenuItems you can bind to a collection, for example:
<Window.Resources>
<ContextMenu x:Key="Vegetables" ItemsSource="{Binding VegetableList}" />
</Window.Resources>
This assumes VegetableList is declared as an ObservableCollection or some other type that implements the INotifyCollectionChanged interface. Any changes you make to the collection will instantly update the ContextMenu, even if it is open. For example:
public void Button_Click(object sender, EventArgs e)
{
VegetableList.Add("Carrots");
}
Note that this kind of collection update need not be made in code: You can also bind the vegetable list to a ListView, DataGrid, etc so that changes may be made by the end-user. These changes will also show up in your ContextMenu.
Switching ContextMenus using code
You can also replace the ContextMenu of an item with a completely different ContextMenu. For example:
<Window>
<Window.Resources>
<ContextMenu x:Key="Vegetables">
<MenuItem Header="Broccoli" />
<MenuItem Header="Cucumber" />
</ContextMenu>
<ContextMenu x:Key="Fruits">
<MenuItem Header="Apple" />
<MenuItem Header="Banana" />
</ContextMenu>
</Window.Resources>
<Grid>
<Ellipse x:Name="Oval" ContextMenu="{StaticResource Vegetables}" />
...
</Grid>
</Window>
The menu can be replaced in code like this:
public void Button_Click(object sender, EventArgs e)
{
Oval.ContextMenu = (ContextMenu)Resources.Find("Fruits");
}
Note that instead of modifying the existing ContextMenu we are switching to a completely different ContextMenu. In this situation both ContextMenus are built immediately when the window is first constructed, but the Fruits menu is not used until it is switched.
If you want to avoid constructing the Fruits menu until it was necessary you could construct it in the Button_Click handler instead of doing it in XAML:
public void Button_Click(object sender, EventArgs e)
{
Oval.ContextMenu =
new ContextMenu { ItemsSource = new[] { "Apples", "Bananas" } };
}
In this example, every time you click on the button a new ContextMenu will be constructed and assigned to the oval. Any ContextMenu defined in Window.Resources still exists but is unused (unless another control uses it).
Switching ContextMenus using DynamicResource
Using DynamicResource allows you to switch between ContextMenus without explicitly assigning it code. For example:
<Window>
<Window.Resources>
<ContextMenu x:Key="Vegetables">
<MenuItem Header="Broccoli" />
<MenuItem Header="Cucumber" />
</ContextMenu>
</Window.Resources>
<Grid>
<Ellipse ContextMenu="{DynamicResource Vegetables}" />
...
</Grid>
</Window>
Because this XAML uses DynamicResource instead of StaticResource, modifying the dictionary will update the ContextMenu property of the Ellipse. For example:
public void Button_Click(object sender, EventArgs e)
{
Resources["Vegetables"] =
new ContextMenu { ItemsSource = new[] {"Zucchini", "Tomatoes"} };
}
The key concept here is that DynamicResource vs StaticResource only controls when the dictionary is lookup is done. If StaticResource is used in the above example, assigning to Resources["Vegetables"] will not update the Ellipse's ContextMenu property.
On the other hand, if you are updating the ContextMenu itself (by changing its Items collection or via data binding), it does not matter whether you use DynamicResource or StaticResource: In each case any changes you make to the ContextMenu will be immediately visible.
Updating individual ContextMenu ITEMS using data binding
The very best way to update a ContextMenu based on properties of the item that is right-clicked is to use data binding:
<ContextMenu x:Key="SelfUpdatingMenu">
<MenuItem Header="Delete" IsEnabled="{Binding IsDeletable}" />
...
</ContextMenu>
This will cause the "Delete" menu item to be automatically grayed out unless the item has its IsDeletable flag set. No code is necessary (or even desirable) in this case.
If you want to hide the item instead of simply graying it out, set Visibility instead of IsEnabled:
<MenuItem Header="Delete"
Visibility="{Binding IsDeletable, Converter={x:Static BooleanToVisibilityConverter}}" />
If you want to add/remove items from a ContextMenu based on your data, you can bind using a CompositeCollection. The syntax is a bit more complex, but it is still quite straightforward:
<ContextMenu x:Key="MenuWithEmbeddedList">
<ContextMenu.ItemsSource>
<CompositeCollection>
<MenuItem Header="This item is always present" />
<MenuItem Header="So is this one" />
<Separator /> <!-- draw a bar -->
<CollectionContainer Collection="{Binding MyChoicesList}" />
<Separator />
<MenuItem Header="Fixed item at bottom of menu" />
</CompositeCollection>
</ContextMenu.ItemsSource>
</ContextMenu>
Assuming "MyChoicesList" is an ObservableCollection (or any other class that implements INotifyCollectionChanged), items added/removed/updated in this collection will be immediately visible on the ContextMenu.
Updating individual ContextMenu ITEMS without data binding
When at all possible you should control your ContextMenu items using data binding. They work very well, are nearly foolproof, and greatly simplify your code. Only if data binding can't be made to work does it make sense to use code to update your menu items. In this case you can build your ContextMenu by handling the ContextMenu.Opened event and doing updates within this event. For example:
<ContextMenu x:Key="Vegetables" Opened="Vegetables_Opened">
<MenuItem Header="Broccoli" />
<MenuItem Header="Green Peppers" />
</ContextMenu>
With this code:
public void Vegetables_Opened(object sender, RoutedEventArgs e)
{
var menu = (ContextMenu)sender;
var data = (MyDataClass)menu.DataContext
var oldCarrots = (
from item in menu.Items
where (string)item.Header=="Carrots"
select item
).FirstOrDefault();
if(oldCarrots!=null)
menu.Items.Remove(oldCarrots);
if(ComplexCalculationOnDataItem(data) && UnrelatedCondition())
menu.Items.Add(new MenuItem { Header = "Carrots" });
}
Alternatively this code could simply change menu.ItemsSource if you were using data binding.
Switching ContextMenus using Triggers
Another technique commonly used to update ContextMenus is to use a Trigger or DataTrigger to switch between a default context menu and a custom context menu depending on the triggering condition. This can handle situations where you want to use data binding but need to replace the menu as a whole rather than update parts of it.
Here is an illustration of what this looks like:
<ControlTemplate ...>
<ControlTemplate.Resources>
<ContextMenu x:Key="NormalMenu">
...
</ContextMenu>
<ContextMenu x:Key="AlternateMenu">
...
</ContextMenu>
</ControlTemplate.Resources>
...
<ListBox x:Name="MyList" ContextMenu="{StaticResource NormalMenu}">
...
<ControlTemplate.Triggers>
<Trigger Property="IsSpecialSomethingOrOther" Value="True">
<Setter TargetName="MyList" Property="ContextMenu" Value="{StaticResource AlternateMenu}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
In this scenario it is still possible to use data binding to control individual items in both NormalMenu and AlternateMenu.
Releasing ContextMenu resources when the menu is closed
If resources used in a ContextMenu are expensive to keep in RAM you may want to release them. If you are using data binding this is likely to happen automatically, as the DataContext is removed when the menu is closed. If you are using code instead you may have to catch the Closed event on the ContextMenu to deallocate whatever you created in response to the Opened event.
Delayed construction of ContextMenu from XAML
If you have a very complex ContextMenu that you want to code in XAML but don't want to load except when it is needed, two basic techniques are available:
Put it in a separate ResourceDictionary. When necessary, load that ResourceDictionary and add it to MergedDictionaries. As long as you used DynamicResource, the merged value will be picked up.
Put it in a ControlTemplate or DataTemplate. The menu will not actually be instantiated until the template is first used.
However neither of these techniques by itself will cause the loading to happen when the context menu is opened - only when the containing template is instantiated or the dictionary is merged. To accomplish that you must use a ContextMenu with an empty ItemsSource then assign the ItemsSource in the Opened event. However the value of the ItemsSource can be loaded from a ResourceDictionary in a separate file:
<ResourceDictionary ...>
<x:Array x:Key="ComplexContextMenuContents">
<MenuItem Header="Broccoli" />
<MenuItem Header="Green Beans" />
... complex content here ...
</x:Array>
</ResourceDictionary>
with this code in the Opened event:
var dict = (ResourceDictionary)Application.LoadComponent(...);
menu.ItemsSource = dict["ComplexMenuContents"];
and this code in the Closed event:
menu.ItemsSource = null;
Actually if you have only a single x:Array, you may as well skip the ResourceDictionary. If your XAML's outermost element is the x:Array the Opened event code is simply:
menu.ItemsSource = Application.LoadComponent(....)
Summary of critical concepts
DynamicResource is used only for switching values based on which resource dictionaries are loaded and what they contain: When updating the contents of the dictionaries, DynamicResource automatically updates the properties. StaticResource only reads them when the XAML is loaded.
No matter whether DynamicResource or StaticResource is used, the ContextMenu is created when the resource dictionary is loaded not when the menu is opened.
ContextMenus are very dynamic in that you can manipulate them using data binding or code and the changes immediately take effect.
In most cases you should update your ContextMenu using data bindings, not in code.
Completely replacing menus can be done with code, triggers, or DynamicResource.
If contents must be loaded into RAM only when the menu is open, they can be loaded from a separate file in the Opened event and cleared out in the Closed event.