How to automatically get hotkey on a WPF tooltip? - wpf

In my window, I have this:
<UIElement.InputBindings>
<KeyBinding Gesture="Ctrl+Left" Command="{Binding MoveTaskCommand}" CommandParameter="{x:Static plugin:MoveDirection.Left}"/>
...
And down in the tree, I have this:
<Button HorizontalAlignment="Left"
ToolTip="Promote to Parent Level (Ctrl+Left)" Command="{Binding MoveTaskCommand}"
CommandParameter="{x:Static plugin:MoveDirection.Left}">
...
I don't like the redundancy, and I don't like copy pasting my hotkey's "Ctrl+Left" into the tooltip. I'm 99% certain WPF has a better way of doing it than this.
Could someone point me in the right direction?

Could someone point me in the right direction?
Option 1: I would try using a custom Attached Property maybe "IsToolTipGesture". In the PropertyChangedCallback, check if there's a ToolTip and any UIElement.InputBindings. If there are, update the ToolTip with the appropriate information from the InputBindings.
Option 2: Another thing to try if you want this behavior on ALL elements below a certain root is using a similar approach with an attached property and recursively checking child elements (via VisualTreeHelper) to see if they have a ToolTip and any InputBindings.

Related

How do I bind to the X position of a MouseDragElementBehavior?

My goal is to display the X position of my control in a TextBlock as I drag it around.
xmlns:mb="http://schemas.microsoft.com/xaml/behaviors"
<cc:CardControl Name="SevenOfSpades" Canvas.Left="350" Canvas.Top="124" Width="60" Height="80" Face="S7">
<mb:Interaction.Behaviors>
<mb:MouseDragElementBehavior ConstrainToParentBounds="True"/>
</mb:Interaction.Behaviors>
</cc:CardControl>
<TextBlock Text="{Binding ElementName=SevenOfSpades, Path=(mb:Interaction.Behaviors)[0].X}"/>
I'm struggling with the syntax of the Binding Path. At runtime I get an exception:
InvalidOperationException: Property path is not valid. 'Interaction' does not have a public property named 'Behaviors'.
The property is there because the drag works when the TextBlock is removed. I've tried various combinations of parentheses, I even tried x:static. Any help?
Edit
Having reread WPF Attached Property Data Binding, it does not solve my problem. Path= is in the Xaml and parentheses are included. The error is not a binding error it's a runtime error that occurs inside InitializeComponent.
MouseDragElementBehavior is part of the Microsoft.Xaml.Behaviors.Wpf Nuget package installed into my project.
Ah, ok. In that case, the code for MouseDragElementBehavior is most certainly available, and even if it wasn't you could just open up the assembly with JustDecompile or something and browse it that way.
If you check the documentation for MouseDragElementBehavior you'll see this:
XProperty Dependency property for the X position of the dragged
element, relative to the left of the root element.
So basically you're trying to bind one dependency property (TextBlock.Text) to another (MouseDragElementBehavior.X), but in order for this to work they have to be part of the same visual or logical tree (which they aren't, MouseDragElementBehavior is a behavior). If one of them was an attached property then you could bind them directly, but in your case you have to link them together with either a property in your DataContext that supports INPC, or some kind of proxy object.
However, even if you do this, you're going to run into problems. If you click the "Go to Live Visual Tree" button while your application is running and look at the properties for your SevenOfSpades control you'll see this:
So far, so good. Now drag the control around a bit and repeat this process. Suddenly a RenderTransform field has appeared:
Looking back at the code for MouseDragElementBehavior reveals that sure enough, that behaviour does the drag by changing the render transform.
So basically you're trying to set the position with Canvas.Top/Canvas.Left, but the behaviour is setting it by applying a render transform offset. Pick one. I personally use MVVM where everything is implemented in the view model layer, so it's easy to bind Canvas.Top/Canvas.Left to properties there. If you want to continue using MouseDragElementBehavior then you'll need to bind both the position of your cards, as well as your TextBlock text, to the render transform instead:
<Canvas>
<Rectangle Name="SevenOfSpades" Width="60" Height="80" Fill="Blue">
<Rectangle.RenderTransform>
<TranslateTransform X="350" Y="124" />
</Rectangle.RenderTransform>
<mb:Interaction.Behaviors>
<mb:MouseDragElementBehavior ConstrainToParentBounds="True" />
</mb:Interaction.Behaviors>
</Rectangle>
<TextBlock Text="{Binding ElementName=SevenOfSpades, Path=RenderTransform.Value.OffsetX}" />
</Canvas>

XAML - Relatively Position Control Outside of Document Flow

I'm looking to create a UI element in a WPF XAML UserControl with something that looks and works roughly like Google Suggest - a TextBox with a ListBox that appears beneath it showing suggestions that match the text entered in the TextBox. In simplified form, my XAML looks like this:
<StackPanel>
[...controls above...]
<TextBox ... />
<ListBox ItemsSource="{Binding SearchHints}"
Visibility="{Binding HasSearchHints}" MaxHeight="100" />
[...controls below...]
</StackPanel>
What I'm struggling to achieve is I want the ListBox to float above any content that may come below it, much like the dropdown part of a ComboBox does. Currently it pushes any controls below it downwards. I figure this must be possible because the ComboBox control essentially does exactly what I want to do. In CSS it would be a matter of setting the element to position:relative but there doesn't seem to be an immediately obvious equivalent in XAML. Any ideas?
Used Icepickle's comment - the element did exactly what I wanted.

Handle WPF button click and enable disable

I have a UI with a few controls.
Initially when form loads, search button will be disabled, once all
search criteria are given, search button will be enabled
automatically.
On click on search button, I want to call the method
using MVVM pattern and bind the result in grid
XAML:
<Button Name="btnGetDetails" Content="Get Details" Grid.Row="2" Command="{Binding SearchCommand}"/>
What code is required in model, view model and XAML?
Command will be executed only when the button will be clicked on. If you need to do something to the button then you should do it to the Window that contains the button (assuming your button is in a Window). Now if you want to stick with the MVVM pattern then you should not use the Window.OnLoaded, because that would put code in your code behind. One option is to use System.Windows.Interactivity, which you have download seperately. Here is what it will look like:
<Window x:Class="..."
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="clr
-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding ...}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Window>
As for what your Model, View and ViewModel should be, I think you should check out some tutorials on the web. There are some very good explanations on how to implement the MVVM pattern. I found this youtube video pretty informative myself:
http://www.youtube.com/watch?v=EpGvqVtSYjs
So some MVVM basics here. You're on the right track, just missing a step. Your Command implementation in your view model should (potentially) accept two inputs: An Action representing the executing code, and a Predicate that returns true/false on whether you can execute the code in the Action block. So, in your view model, define your command along the lines of (note: this is a sample from one of my projects):
this.executeCommand = new RelayCommand(this.OnExecuteClicked, this.OnCanExecuteChanged);
The OnCanExecuteChanged method will return a bool based on whatever criteria you set up. So, if you want the submit button enabled when property A and property B have been properly set up, then return true, else return false. The internal workings of your command implementation will take care of the rest. Do a search for a RelayCommand implementation (if you don't have it already) or a DelegateCommand for further samples.

WPF bug? MouseGesture on Single and DoubleClick

We have implemented a simple InputBinding for mouse click gestures, the code is something like below:
<Image.InputBindings>
<MouseBinding MouseAction="LeftClick" Command="{Binding OpenDialogCommand}" />
<MouseBinding MouseAction="LeftDoubleClick" Command="{Binding OpenDialogCommand}" />
</Image.InputBindings>
We expected that WPF is able to recognize Left and LeftDouble gestures and do the respective command. But in practice we found that the Left Click is evaluated first and the second click of the double click is treated as another single click. Since our command is to open a dialog doing a doubleclick will quickly open and close our dialog.
Does anyone meet such thing before?
Thanks.
S.
I think this is the desired behavior. Note that it is a LeftClick event, which doesn't necessarily mean "LeftSingleClick". You should check out the Snoop utility for investigating exactly which events are getting triggered.

WPF MVVM: Find out on which header context menu has been clicked

I am trying to use the MVVM pattern to write a WPF application. I am using WPF data grid (from the toolkit) which lacks the autofiltering feature. So I want to implement it. I've added a context menu to the column header template, it has MenuItem called "Filter" which should actually call the filtering method.
So I've set a MenuItem's command to be the appropriate DelegateCommand which goes to the ViewModel. The problem is that I need to pass the information about the actual column that has been right-clicked! If I wasn't using MVVM, I would implement an event handler which would receive a "sender" argument (the MenuItem), then I would find its parent (the ContextMenu), then its parent would give me the column. But how can I achieve the same thing here? How can I pass the sender to my command? Can this be done using ComandParameter?
I really don't want to use additional complicated patterns to achieve such a simple task. After all, MVVM should simplify the development and not vice versa...
Can you pass the Column header value as a Command Parameter and use that to get the Column details at the ViewModel?
You could try some relative source magic, but it might be easier on you if you can have a different ViewModel that you bind to for each header, like HeaderViewModelItem. From there you'd just be firing a DelegateCommand in your HeaderViewModelItem, rather on your larger viewmodel.
I've used this model with pretty good success. Gets around a little bit of databinding dance.
If you want to pass something into the command parameter it is important to note that a context menu is on its own visual tree. Luckily it still inherits the DataContext from its parent, so something like
<MenuItem CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}, Path=DataContext}" />
should get you the GridViewColumnHeader, or at least something in the visual tree of it.

Resources