I'm new to WPF and in WinForms to add hotkeys to the form I've usually used ProcessCmdKey that made easy to override(add) key related functionality (similar to the way described here). Is there an easy way to assign hotkeys in WPF? I'm using Commands with keys, but sometimes that doesn't work (I think some other controls on window respond to that gestures and do their jobs, so that my command can't respond to predefined key gesture).
InputBindings are scoped with the control they are assigned to, if you assign a KeyBinding in the Window.InputBindings they will be fired throughout the window unless the same gesture is locally overridden by being defined somewhere down the tree.
For example this:
<UserControl.InputBindings>
<KeyBinding Gesture="CTRL+SHIFT+N"
Command="{Binding BtnNewChild_Command}"
CommandParameter="{Binding ElementName=view}" />
</UserControl.InputBindings>
doesnt work, it cant pass the view element as parameter, tho there is control named "view" in that UserControl. Does wpf have some "common" hotkey assign scenario?
The problem here would just be scope, the child controls have access to things declared higher up on the tree but not the other way around. You could possibly refactor this to create your view as a resource in the UserControl.Resources then you can reference it both in the CommandParameter and whereever you use it in the UserControl.
Related
I need my application to be notified when style is changed of a control. I need to to do some actions when the style is changed of a WPF control. Can I apply some eventtriggers or notify is some way.
Best Regards
There are a few possible solutions.
First you could subscribe a change handler to the Style property of your control if want to get notified somewhere in your C# code:
DependencyPropertyDescriptor.FromProperty(Button.StyleProperty, typeof(Button))
.AddValueChanged(btn, (s, e) =>
{
// Style has changed.
});
(Don't forget to call .RemoveValueChanged() after you're done.)
Another way would be to create a Binding with a source path set to the Style of your control. The binding target could for example be some kind of custom control or a ViewModel where you want to react to the change. Or if you don't have any of these available, you could just set the binding target to some Tag property and use a ValueConverter to intercept the change using something like Tag="{Binding Style, ElementName=btn, Converter={StaticResource MyStyleInterceptor}}".
You could also create a custom attached property for this purpose if you don't want to abuse Tag.
I'm getting a little confused about how to layout my code in MVVM - if I have a UserControl with a corresponding VM class, how should other controls consume my UserControl?
Should consumers bind directly to the VM or should I duplicate only a subset of these properties I want to actually be used as DependencyProperties of the UserControl?
For that matter, should the UserControl's VM be injected into the UserControl's code-behind or should the VM of any control that uses this UserControl contain it as a dependency and bind it to the UserControl instead?
Just to make it clear: Suppose I have a ListBox in a UserControl and use it in a Window that is already implemented with MVVM. But I'm confused about the implementation of the UserControl VM and the corresponding bindings.
I would think the ideal solution would be to expose the SelectedItems of the ListBox via dependency properties in the UserControl, and then the Window which uses the UserControl would bind to these.
Or should the Window's VM have a reference to the VM as a property, have it injected and bind directly to the properties on that instead?
Should dependency properties only be defined in UserControls or can / should they be defined in the VM?
I'm thinking the Window would bind, from within the XAML of the Window, either via
{Binding ElementName=myUserControl, Path=SelectedItems}
or
{Binding Path=MyViewModel.SelectedItems}
It just seems to make more sense to do it via the former, since the latter requires that the View knows about another VM?
> how should other controls consume my UserControl?
Via exposed dependancy properties on the usercontrol only.
>Should consumers bind directly to the VM or should I duplicate only a subset of these properties I want to actually be used as DependencyProperties of the UserControl?
Each control should be a stand alone entity, there shouldn't be any secret handshake (either to or from) to use the control. Think of the design like you are Microsoft and many different users will use your controls. So answer #1 is just as relevant; think a stand alone entity.
>I would think the ideal solution would be to expose the SelectedItems of the ListBox via dependency properties in the UserControl, and then the Window which uses the UserControl would bind to these.
The window which hosts your controls will have a View Model which contains a Observable list of data items. That will hold the data which the user control(s) will bind via their dependency properties. THink of it as a producer pattern with many consumers. The consumers are the controls. Whether the controls have VMs or not is immaterial to the running of the main program; for each control is its own island.
Keep in mind when working with WPF and MVVM that your View layer is merely a user-friendly way of drawing your Models and ViewModels, and that your View is not actually your application. Your View actually has to know the basics about your data layer so it can define how to draw it.
So if your application needs to display a list of Items and maintain a SelectedItem, than that should be in your ViewModel or Model somewhere, not in the actual View layer.
Typically for me UserControls are one of two things:
Either a standalone UserControl that can be used anywhere without a specific DataContext, and that expose DependencyProperties for any control-specific values. Examples are things like a Calendar control or a Popup control
<local:MyUserControl Items="{Binding SomeItemList}"
SelectedItem="{Binding SomeItem}" />
Or they are a UserControl that is meant to be used with a specific ViewModel only. This is far more common for me. The ViewModel is a property somewhere in the data layer, and I usually have an implicit DataTemplate in the application somewhere to tell WPF to use that UserControl anytime it needs to render that specific ViewModel
<DataTemplate DataType="{x:Type local:SomeViewModel}">
<local:MyUserControl />
</DataTemplate>
<ContentPresenter Content="{Binding SomeViewModelProperty}" />
Also, at no time should you set the DataContext of a UserControl from inside the UserControl itself, because the UI layer is only meant to be a UI representation of your data layer (your Models/ViewModels), and by setting the data layer from inside the UserControl you are making it so that the UserControl cannot be used to draw any other data object.
I have a rather interesting case with ComboBox control - CustomComboBox;
In the style of this ComboBox, Popup contains one custom control that requests a DataContext;
<ctrl:CustomGrid DataContext="{TemplateBinding DataContext}" GridName="{Binding Preferences.CurrentGridName}"/>
The idea:
to use this control several times on one page
to use it in a masterpage container
the masterpage control needs to have different DataContexts regarding the Page it is on
The logic:
In the overriden OnApplyTemplate I am getting the grid and connecting few eventhandlers
The problem:
The masterpage control is triggering OnApplyTemplate only once
The first appearance of the CustomComboBox is as expected.
However, every next apearance is with same DataContext, even when changing the datacontext of the CustomComboBox
These changes don't reach to change my CustomGrid DataContext
I am sure that something on the bindings or the presentation logic is bad...
Please throw some thoughts on, I would appreciate a hint here
Thanks
OnApplyTemplate is called when a ControlTemplate is applied to the control that overrides the method (neither its parent, nor children). If OnApplyTemplate is entered once, the overriding control must also be created once. I mean you simply have a single masterpage instance. This shouldn't be unexpected.
Speaking about Popups and DataContext, there often are issues with bindings from a Popup to outside it. So, I would rather write some code-behind to deliver correct context to Popups, instead of relying on Bindings. There sure is a problem of DataContextChanged event absence prior to SL5. To workaround this one, you should define your custom DependencyProperty on your CustomComboBox, bind it to the CustomComboBox's context and assign its value to the Popup in the PropertyChangedCallback.
I've created a ToggleButton to show and hide a piece of UI and I've sent its Content to an icon.
I now want to add a shortcut key to the ToggleButton but I'm unsure how to do with without binding a command as well. Since all I am doing is binding to the IsChecked state, I don't need a command to do any other functionality and creating an empty one seems incorrect.
Here is my ToggleButton as it stands currently non-functional and not responding when I press the indicated shortcut key.
<ToggleButton ToolTip="Command History"
MinWidth="24"
IsChecked="{Binding IsShowHistoryChecked}"
Margin="7">
<ToggleButton.InputBindings>
<KeyBinding Gesture="Ctrl+H" />
</ToggleButton.InputBindings>
<Image Source="/Amuse;component/Images/ComHistory256.png"
Width="24" />
</ToggleButton>
An InputBinding on the ToggleButton itself won't do the trick, but there are two good WPF solutions for your problem:
Use a RoutedCommand that updates the model.
Register an access key.
Why your InputBinding solution won't work
The InputBinding you have defined currently won't work because it doesn't list a command. It is easy to create a command that toggles a button, as follows:
public void Execute(object parameter)
{
((ToggleButton)parameter).IsChecked = !((ToggleButton)parameter).IsChecked;
}
However this will not achieve what you are looking for. You want Ctrl-H to toggle your button even when the button is not focused. An InputBinding will not accomplish this for you, since it only works when the button has focus. I will now discuss two solutions you can use.
Option 1: Use a RoutedCommand that updates the model
The whole point of WPF's architecture is that you never will need to "toggle a button" in the first place: Conceptually all keyboard and mouse actions in WPF should serve to toggle a bound property in your model or view model. The ToggleButton then just becomes the mechanism for accepting mouse clicks, but need not be the only one.
The name you chose for your "IsShowHistoryChecked" property indicates a fundamental problem in the way you're conceptualizing your view model. Your view model should not be designed around the view - rather, it should expose logical concepts such as a "ShowHistory" property. The view may bind this to a CheckBox or ToggleButton, or it may choose some other mechanism, or it may not expose it at all. The whole point of data binding and view models is that when you create the view model you don't care what the actual view will be like. In fact, during automated unit testing there will be no checkbox so "IsShowHistoryChecked" would clearly be a real misnomer.
So let's say you've properly separated your view from your view model and you have a "ShowHistory" property. First implement a "ToggleShowHistory" command in your view model that, when executed, toggles the ShowHistory property. Now all you have to do is assign this command an InputBinding of Ctrl-H at the view level and you're done. Even if the ToggleButton is removed from the view entirely the InputBinding will still take effect and Ctrl-H will still work. Nirvanna.
Option 2: Register an access key
Windows has a standard mechanism for associating keys with arbitrary buttons and labels, which is the "access key" concept. If you register an access key of "h" on the ToggleButton, pressing Alt-H will toggle the button, and so will just plain H if you don't have a TextBox or another control accept it first.
It is very simple to register the access key in code:
AccessKeyManager.Register("h", togleButton);
This registers "h" as the access text. Now if the user presses Alt-H anywhere in scope (or plain "h" if isn't handled by a TextBox), your button will toggle.
You can also do it in XAML. If you're showing text in your button, just use an underline before the access key letter:
<Button Text="Show _History" ... />
If you're showing something other than just text, include a hidden AccessText element in your button content:
<Button ...>
<Grid>
<AccessText Text="_h" Visibility="Collapsed" />
<Image ...>
</Grid>
</Button>
In case you're wondering, WPF has no built in mechanism to request that AccessKey registrations respond to Ctrl instead of Alt, so this will not allow you to set Ctrl-H as the access key.
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.