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?
Related
I'm trying to clean up the way I use DataContexts with my UserControls, and currently am running into a problem where I need to databind a UserControl inside of a TabItem to the parent Window's DataContext.
Here is a sketch of what my Window looks like:
As you can see, this Window owns a TabControl that contains TabItems that are dynamically added via the "Tabs" ItemSource. Databinding at this point is working because "Tabs" gets populated with Tab 1.
Tab 1 contains a UserControl that needs access to multiple string properties in the DiagnosticsViewModel, but when I run my application, the Output window indicates that all bindings have failed. For example:
System.Windows.Data Error: 4 : Cannot find source for binding with
reference 'RelativeSource FindAncestor,
AncestorType='System.Windows.Window', AncestorLevel='1''.
BindingExpression:Path=Property1; DataItem=null; target element is
'Tab1UserControl' (Name=''); target property is 'UCName' (type
'String')
The XAML for the UserControl in Tab 1 looks something like this:
<Grid>
<uc:Tab1UserControl UCName="{Binding Property1, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
</Grid>
where UCName is a String DependencyProperty.
If I am telling WPF that I want to look up the tree and bind to the DataContext for the nearest Window, and my Window's DataContext is set to DiagnosticsViewModel, why isn't it using it for my UserControl's DataContext? I have not set DataContext = this in my UserControl, as I have done improperly many times in the past, with the expectation that my UserControl will be able to inherit the DataContext from its parent.
I would like to see if Snoop can shed light on my problem, but this GUI is being displayed from a MFC application, and Snoop doesn't seem to be able to attach to my WPF dialog.
If you change the source of a binding using RelativeSource, ElementName or the like, the binding will be directly to the element you specify - not its data context. Which means that in your code the user control will try to bind to a property called Property1 on the Diagnostics class itself.
Try using
<Grid>
<uc:Tab1UserControl UCName="{Binding Path=DataContext.Property1, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
</Grid>
in the code for your user control and see if that fixes the problem.
(As an aside, the idea of the user control itself knowing that the window it belongs to will have a datacontext with a given property seems like a code smell to me, especially since the point of user controls is that they're reusable - it'd feel better to have a dependency property on the user control and then bind that to the appropriate property when you use it. This may just be due to me lacking context, though.)
Is it possible to bind the IsEnabled property of a button to a context menu item that is represented by a NotifyIcon?
When I push the menu item, it starts a method that disables my btnSave. In that case, I would like to "turn off" the MenuItem too. I attempted it this way, but it's not working:
<Window.Resources>
<ContextMenu x:Key="NotifierContextMenu" Placement="MousePoint">
<MenuItem Header="Start" Click="start_timer" IsEnabled="{Binding ElementName=btnSave, Path=IsEnabled}"/>
</ContextMenu>
</Window.Resources>
I believe your problem stems from a ContextMenu not existing within the visual tree, so ElementName bindings will be unsuccessful, unless you take some additional steps to correct the Name Scope, or resolve the DataContext to the visual tree.
Most of the techniques are covered fairly comprehensively in this answer: ElementName Binding from MenuItem in ContextMenu.
I've had success in the past with binding to a named element by reference, in your case, that would look something like this:
<MenuItem Header="Start"
IsEnabled="{Binding IsEnabled, Source={x:Reference btnSave}}"
Click="btnSave_Click"/>
I have a list view that is populated from a mysql table, I want to be able to double click on a item in the listview and bring up a new window with more information. How do I pass the first column value to the new window (this is the id of the item)? This way I can make another query to get the rest of the info about the item.
Was having a similar issue with a ListBox wanting to open a window (Different View) with the SelectedItem as the context (in my case, so I can edit it).
The three options I've found are:
1. Code Behind
2. Using Attached Behaviors
3. Using Blend's i:Interaction and EventToCommand using MVVM-Light.
I went with the 3rd option, and it looks something along these lines:
<ListBox x:Name="You_Need_This_Name"
ItemsSource="{Binding Your_Collection_Name_Here}"
SelectedItem="{Binding Your_Property_Name_Here, UpdateSourceTrigger=PropertyChanged}"
... rest of your needed stuff here ...
>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<Command:EventToCommand Command="{Binding Your_Command_Name_Here}"
CommandParameter="{Binding ElementName=You_Need_This_Name,Path=SelectedItem}" />
</i:EventTrigger>
</i:Interaction.Triggers>
That's about it ... when you double click on the item you want, your method on the ViewModel will be called with the SelectedItem as parameter, and you can do whatever you want there :)
What is meant with MVVM is that you will have for example a ViewModel containing a property SelectedThing bound to the SelectedItem of the listview and a command that gets executed using EventCommand on the MouseDoubleClick event of the View which will execute in the end the operation you want on the SelectedThing which could be also passed in as parameter to the command also by binding.
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.
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.