Disabled context menu won't disappear after rightclick - c#

I have a ContextMenu as part of a TabControl such as:
<TabControl Name="MyTabControl">
<TabControl.ContextMenu>
<ContextMenu Name="MyContextMenu" IsEnabled="False" StaysOpen="True">
<MenuItem Header="Item1"></MenuItem>
...
</ContextMenu>
</TabControl.ContextMenu>
</TabControl>
As you can see, the menu is disabled. It will be enabled later in the program, for now however, it should remain in its disabled state. The problem is, when I right-click the TabControl and the disabled menu shows, it simply stays where it was first opened, no other right-click will move it, nor will a left-click somewhere make it disappear.
The only way to get rid of it, would be either by enabling it and then right/left-clicking, or by using MyContextMenu.Visibility = Visibility.Collapsed/Hidden;
I tried setting the StaysOpen-property to False. Then the menu will open once in its disabled state. After left-clicking disappear and then not appear again even if it gets enabled.
The only way around it could be changing the StaysOpen-property along with the IsEnabled-property, but it is a bit weird that the menu opens exactly once in its disabled state and then not anymore.
I hope anybody could explain to me, why exactly a disabled menu won't close and the property StaysOpen at false makes it open exactly once, because it seems like a weird behaviour to me.

It seems that the behaviour of context menu items is quite strange - what you've described is in .Net 4.0 and if you target yor app to .Net 3.5 for instance you will notice the opposite behaviour - you can't make the menu stay opened if the single item is disabled, it just disappears immediately after it shows on right click.
However I think the preferrable way to manage the enabled state of a context menu item (and also the OnClick action it should do) is by a Command.
First you should specify a datacontext for your view, let's say it is the class ViewModel.cs. Then create a command by implementing the ICommand interface, something like this:
public class MyCommand : ICommand
And then you have a CanExecute method which does exactly what it's name says - decides if the command can be executed or not. And the enabled state of the menu item also depends on the return value of this method. So you can keep it returning false as long as you need in and the menu will behave correctly and the menu item will be disabled. Then when you want you can make it return true (by some logic in the method itself) and you'll have again a properly working context menu with enabled menu item.
If you want to disable the entire menu, use the same approach but for the menu.
And if you need to make all the items in the menu disabled (which I think is different from the entire menu), then you can use something like this:
<TabControl Name="MyTabControl" Background="Green">
<TabControl.ContextMenu>
<ContextMenu Name="MyContextMenu" StaysOpen="True" ItemsSource="{Binding Items}">
<ContextMenu.ItemTemplate>
<DataTemplate >
<MenuItem Header="{Binding Header}" IsEnabled="False" />
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</TabControl.ContextMenu>
</TabControl>
Here your ItemsSource (Items) is for instance List<MenuItem>, and MenuItem itselw would be your Model (thinking in MVVM) for each of your menu items, which should have property Header that contains the header you want to see in each menu item. Instead of setting the IsEnabled to false as in my dummy example, you can bind it to a property in order to have control on when it must be true and false.

Related

Visual UI Automation Verify shows DisconnectedItem in ContextMenu (WPF)

Stackoverflow.
I've looked through a good amount of questions\answers, and still not found the clue to the issue I'm facing in Visual UI Automation Verify (2.0).
I have a ContextMenu in TreeList (DevExpress control):
<dxg:TreeListControl.ContextMenu>
<ContextMenu x:Name="TreeListContextMenu" DataContext="{Binding Path=Data, Source={StaticResource Proxy}}" ItemsSource="{Binding Path=Data.SelectedItemContextMenuItemCollection, Source={StaticResource Proxy}}"/>
</dxg:TreeListControl.ContextMenu>
I have a simple binding proxy, like this one declared with the following code:
<dataStructures:BindingProxy x:Key="Proxy" Data="{Binding}" />
DataContext of the User Control and DataContext of the ContextMenu are bound to Data property on the proxy (because ContextMenu doesn't belong to the same visual tree).
In the non-shared MenuItem style I'm assigning the value from Data property of the proxy to the attached property of the MenuItem:
<Setter Property="helpers:ParentControlDataContextBindingHelper.ParentControlDataContext" Value="{Binding Path=Data, Source={StaticResource Proxy}}" />
The reason it's done this way:
In the tree there are various objects. Each object might have different ContextMenu item list based on object's certain property value. I'm handling the "ContextMenuOpening" event by routing it to the Command:
<i:Interaction.Triggers>
<i:EventTrigger EventName="ContextMenuOpening">
<prism:InvokeCommandAction Command="{Binding ContextMenuOpeningCommand}"/>
</i:EventTrigger>
<i:Interaction.Triggers/>
In ContextMenuOpeningCommand command I'm clearing and then filling up SelectedItemContextMenuItemCollection (which consists of MenuItem Visual Models - binding sources for Header, Command and Icon for the MenuItem) based on the property of the SelectedItem
When the context menu is called the first time - it appears on Visual UI Automation Verify (with the correct ContextMenuItem headers), but it can't be tested after it is closed - its contents in Visual UI Automation Verify are DisconnnectedItems. If I call next ContextMenu on the different type of element and the list of ContextMenu items is different - Visual UI Automation Verify doesn't show even headers of the menu items (the headers are empty because DataContext of menu item is DisconnectedItem after menu closes). If I'm using "hardcoded" ContextMenu with all bindings and items in XAML - everything is ok.
I'm not sure if this behavior is actually by design or not. Is it possible to test ContextMenu with non-static items collection with Visual UI Automation Verify? Inspect.exe in WindowsKits behaves the same way. What's more important - how I can get around that to be able to test the menu with VisualUIAVerify?

Caliburn Micro Hello Screens

I have a 3 part question about the "Hello Screens" Caliburn Micro example. (I'm using a WPF port of the project). This concerns the behavior of CustomerView.xaml which contains a "Save" and "Close" button pair. Willing to accept answers to just 1 and 2 as 3 probably becomes trivial at this point!
Whenever the AddressViewModel is activated by the DialogConductorViewModel the Save and Close buttons become disabled. This happens here in the EditAddress function of CustomerViewModel:
Dialogs.ShowDialog(new AddressViewModel());
1) How do the user controls of CustomerView know to become disabled here? My first thought was that CustomerViewModel (CMV) must've deactivated and maybe CM disables the the CustomerView UserControl for us as a result. However, if you override OnDeactivate in CMV the view model isn't being deactivated whenever it shows a dialog with the "ShowDialog" function.
2) From a WPF standpoint how do all the controls on CustomerView become disabled? If I put this code somewhere in CustomerView.xaml and run the project, the CustomerView UserControl remains enabled throughout:
<TextBlock Text="{Binding Path=IsEnabled,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"></TextBlock>
However, if I bind to IsEnabled on any control I place onto CustomerView.xaml that control's IsEnabled property is being set to false when a dialog is shown:
<TextBlock Text="{Binding Path=IsEnabled,
ElementName=TryClose }"></TextBlock>
3) I would like to write a style datatrigger to alter other aspects of CustomerView's appearance whenever it's displaying a dialog. Ideally I would be binding to a property on CustomerViewModel but the best I can tell, this ViewModel doesn't inherently know whenever it's displaying a Dialog through the DialogManager unless there's something I've missed. Does CM give me a way to detect whenever this dialog is being displayed from the point of view of the CustVM?
Thanks,
Sean

RoutedEventArgs.Source give me wrong value? [duplicate]

I am new to WPF and am going through the examples of Professional WPF in .net 4.5. In the commands chapter, there is an example where multiple controls can send the same command. I am using a Button, CheckBox and MenuItem to trigger the New command.
The issue I am facing is that if MenuItem is pressed for the first time, the source shows correctly. However, after clicking the Button or CheckBox, then clicking MenuItem shows me the source of the last control Button or CheckBox, whichever was pressed. I couldn't find what was wrong with my code or why is this behavior shown by MenuItem in WPF.
Below is the code.
<Window x:Class="WpfApplication1.CommandSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CommandSample" Height="300" Width="300">
<Window.CommandBindings>
<CommandBinding Command="New" Executed="CommandBinding_Executed" />
</Window.CommandBindings>
<StackPanel>
<Button Command="New" MaxWidth="80" MaxHeight="30" Content="{x:Static ApplicationCommands.New}" />
<Menu MaxHeight="30" VerticalAlignment="Top">
<MenuItem Header="File">
<MenuItem Command="New"></MenuItem>
</MenuItem>
</Menu>
<CheckBox Command="New"></CheckBox>
</StackPanel>
</Window>
namespace WpfApplication1 {
public partial class CommandSample: Window {
public CommandSample() {
InitializeComponent();
}
private void CommandBinding_Executed(object sender,ExecutedRoutedEventArgs e)
{
MessageBox.Show("New Command launched by " + e.Source);
}
}
}
Yes this is correct (or at least that's how it's designed). Routed commands start routing based on the CommandTarget you specify. If one isn't specified typically the object raising the event uses itself as the starting point (so the MenuItem in this case). So the routing starts with the MenuItem in this case as you might expect. Nothing handles it there so the CommandManager goes up the parent chain. When it hits an element that is a FocusScope (like the Menu), it checks the FocusedElement of the "parent" FocusScope (e.g. the FocusScope of the parent of the Menu which in this case is the Window). If there is a FocusedElement (which there will be one once you have focused an element in the window's focus scope such as your button, checkbox, a textbox that you might put in that stackpanel, etc.) then the CommandManager starts routing the event from that element. When it does that it creates a new ExecutedRoutedEventArgs where the OriginalSource is that starting element (so the button, checkbox, textbox) and then continues routing up the tree.
So when you first ran the app, the FocusedElement of the Window (that's the root focus scope in your example) is null so there is no re-routing needed so the CommandManager just kept going up the parent chain past the Menu and that is why MenuItem appeared as the Source & OriginalSource. When you clicked on the Button you gave that keyboard focus and as part of it also became the logically focused element of its focus scope (i.e. the FocusedElement of its containing FocusScope). So when the MenuItem was subsequently clicked and the CommandManager ultimately reached the Menu, it then re-routed over to the Button (or whatever you focused in the window's focusscope) and started routing up from there. I say this is expected because with routed command you want the routing to go through the logically focused element so that for example, the Cut command of a menu item would trigger a cut of the TextBox in the Window that had focus.

Change a section of a window upon click event

I have a treeview at the left side of the screen, and when I click on any of the TreeViewItem, I want the right side of the screen to change accordingly.
For example, clicking on 'Project' would display on the right half of the screen, a label for project name along with the project name in a text box, and a similar label-textbox pair for some other fields. Clicking on a sub-option of 'Project' such as 'Task 1' should change the right half of the screen such that instead of labels and textboxes for project name and details, it should now be for task name/details. Atm, I only care about label-textbox pairs but in the future I'll need some more sophisticated options, maybe buttons and tables.
What I thought of was to have a grid premade for each option, when I clicked on 'Project' there would be a grid which displays all the info for a Project. And when I then clicked on 'Task 1', the Project grid should be hidden and the Task grid should be displayed with the fields filled out.
Is this possible? What should I be using to create templates that I can then choose from?
Firoz already mentioned the important bit. A rough guess is that you're not using MVVM pattern, so to minimize the adaption effort, you could add a Content Control to your window and set the content of this control whenever a selection is made. You can put any User Control in there.
Using MVVM would mean you bind that Content Control to a property on your ViewModel (of type UIElement or UserControl) and set an instance whenever a bound selected values changes. Speaking of selected Value, I think the default TreeView is not really Binding-friendly, so you might end up with behaviours that do the binding for you.
What you are asking to do is quite easy and possible, but I don't think you are thinking quite big enough.
As your project grows and the number of different things that you want to show expands, then you are going to need to show and hide more and more controls. This is quite quickly going to get unmanageable. Instead think about some other controls deal with this, in some ways you are doing something very like a tabbed dialog, just with a hierarchical set of tabs.
A tabbed dialog has a panel and a set of tabs, when you click on each tab, the content of the panel changes. In fact you can create UserControls one for each specialised set of UI that you want to display, e.g. you could have a ProjectControl that displays all of your project textboxes, labels, buttons etc.
In addition WPF has this neat feature called DataTemplates, these define how a type of data should look when it is displayed. So if you where to have a
public class MyProject
{
public string Name {get;set;}
}
Then you could define
<DataTemplate DataType="{x:Type MyProject}>
<TextBox Text="{Binding Name}"/>
</DataTemplate>
And WPF will automatically convert the data into to its visual form if you set it as the content of the tab panel.
However this type of displaying content in a panel is not the only WPF control that does this. There is also something called a NavigationFrame, which also can be used wrapped into a Window as a NavigationWindow. This control provides you ways to navigate to the next Page to display. Pages can be just like the UserControls in a tabbed dialog, but can also be URIs, enabling you to link in content from the web if you wish. In addition you can call NavigateTo from other controls enabling you build much more usable interfaces.
I worked through the process of building a full windows control panel style interface in
http://alski.net/post/2012/01/11/WPF-Wizards.aspx
and http://alski.net/post/2012/01/13/WPF-Wizards-part-2-Glass.aspx
I've added later VS2012 style glows in
http://alski.net/post/2013/09/14/WPF-Re-creating-VS2012Office-2013-window-glow.aspx
And then released the entire source code as open source at
http://winchrome.codeplex.com/
This comes with support for embedding Navigation panels with
<WinChrome:SearchableNavigationWindow
x:Class="WinChrome.Win7Demo.MainWindow"
...
xmlns:WinChrome="clr-namespace:WinChrome;assembly=WinChrome"
Style="{StaticResource Win7NavigationWindow}">
<WinChrome:SearchableNavigationWindow.Navigation>
<view:Navigation x:Name="navigationTree"/>
</WinChrome:SearchableNavigationWindow.Navigation>
(Full source code)
Where the navigation window is embedded as, but can also be a TreeView.
<UserControl x:Class="WinChrome.View.Navigation" ...>
<ScrollViewer HorizontalScrollBarVisibility="Disabled" Padding="12,0"
VerticalScrollBarVisibility="Auto" >
<StackPanel>
<Button
Margin="0,12,0,0" Style="{StaticResource LinkNavigatorButtonStyle}"
Content="Home"
Command="{Binding
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Win7Demo:MainWindow}, AncestorLevel=1},
Path=GoHomeCommand}" />
</StackPanel>
</ScrollViewer>
(Full source code)

How do you bind the checkmarks of submenus in a context menu defined externally?

In our application we have a central resource where we define all system-wide menus for items. These menu items are already bound to system-wide-defined commands for our objects. For instance, anywhere in the app where we are dealing with a 'Foo' object, we simply attach the 'FooContextMenu' resource. Works great.
BUT... one of the menus defines a submenu representing enumeration values and as such, we want the apropriate menu item to be checked depending on the value of the enum-typed property on the object. For instance, everywhere in the UI that displays a 'Foo' object, we want to show this context menu...
FooContextMenu
|__First Foo command
|__Set Foo Encoding
| |__EnumValueA
| |__EnumValueB
| |__EnumValueC // <-- Show checkbox if 'Foo.SomeEnumProp' == 'C'
| |__EnumValueD
|__Other Foo command
|__Last Foo command
Now again, since the commands and context menu resources are defined centrally, they all work to execute the code just fine. What we can't figure out is how to globally deal with that checkbox. While we could add the 'ContextMenuOpening' code everywhere, that's the problem. We have to add that everywhere, but I can't imagine that's how you have to do it.
I'm sure I'm missing something blindingly obvious considering this is basic Windows app (any OS actually) behavior, but I just can't see it. (I'm wondering if the context menus pick up the data context of the item they're attached to and I can do simple bindings, but that's just a guess.) Thoughts?
The ContextMenu behaves a bit differently with regard to the DataContext.
<ContextMenu>
<MenuItem Header="Foo"
Command="{Binding FooCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.SelectedItems}"/>
</ContextMenu>
The DataContext of the MenuItem within the ContextMenu will be that of the parent, which is the ViewModel which most likely exposes the collection which the ListBox.ItemsSource is bound to in the above example.
If you wanted to work directly with the Model being represented within the listing of items, you would need to use the relative pathing of the given item as seen above in the CommandParamter scenario. If you want to simply expose the common command with your ViewModel, then you can use the binding as you are used to as the DataContext will be the ViewModel representing the listing of items.
Specific to your example it could look something like this if you were keeping the common behavior within the ViewModel, versus the Model.
<ContextMenu>
<MenuItem Header="Foo">
<CheckBox
Visibility="{Binding Path=YourProperty,
Converter={StaticResource BooleanToVisibilityConverter}}">
My CheckBox
</CheckBox>
</MenuItem>
</ContextMenu>
You could then have your property within your ViewModel call out to a Service where you have everything centralized. If you need the Model to make your decision within the converter, you will need to use the relative pathing to get the SelectedItem to pass as a parameter.

Categories