Custom Mark-up Extensions return RoutedEvents - wpf

I'm trying to create a generalized event for my Close buttons, where they have to close the window but before that set focus to the owner window. I don't want to have an event for every file for that, because that'd be pretty unpractical since I have 30+ windows in my application. (So if I wanted to change that behavior, i'd have to change on 30 files everytime)
I'm not sure if that's the correct approach, but I tried making a MarkUp Extension which returns a delegate(object sender, RoutedEventArgs e) Here is the code:
delegate void RoutedDelegate(object sender, RoutedEventArgs e);
[MarkupExtensionReturnType(typeof(RoutedEvent))]
public class CloseWindowExtension : MarkupExtension
{
Window win = null;
public Window Win
{
get { return this.win; }
set { this.win = value; }
}
public CloseWindowExtension(Window win)
: base()
{
this.win = win;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (win == null)
{
throw new InvalidOperationException("The window must be specified!");
}
return new RoutedDelegate(delegate(object sender, RoutedEventArgs e)
{
Extensions.FocusClose(win);
});
}
}
The FocusClose method gets a window, closes it, but sets focus to its owner before. But I can't make it work. When i set my button in the xaml,
Button Click="{e:CloseWindow {Binding win}}"
(win is my Window name), I get the error message:
Click="{e:CloseWindow {Binding win}}" is not valid. '{e:CloseWindow {Binding win}}' is not a valid event handler method name. Only instance methods on the generated or code-behind class are valid. Line 28 Position 17.
Am I doing something wrong? Is this the best approach or do I have another options?
Thanks in advance!
Clark

You can't use a markup extension to set an event handler. Instead, you can use an attached behavior, which allows you to bind a command to an event.
See this article by Marlon Grech for details

.NET 4.5+ supports markup extensions for events, so you can implement what you wanted now :)

Related

(WPF) Does .Close() method releases the window instance?

I'm creating a new window in On_Click method. First I tried this;
public partial class MainWindow : Window
{
CustomerOperations customerOperationsWindow;
public MainWindow()
{
customerOperationsWindow = new CustomerOperations();
InitializeComponent();
}
private void btnCustomer_Click(object sender, RoutedEventArgs e)
{
customerOperationsWindow.Owner = this;
customerOperationsWindow.Show();
}
}
It's not working so I started creating the window instance every time the user clicks on the Customers button. And I used the following codes.
private void btnCustomer_Click(object sender, RoutedEventArgs e)
{
CustomerOperations customerOperationsWindow = new CustomerOperations();
customerOperationsWindow.Owner = this;
customerOperationsWindow.Show();
}
In the new window, If user clicks to Main button, I want to navigate to main window.
private void btnMain_Click(object sender, RoutedEventArgs e)
{
this.Close();
this.Owner.Show();
}
First question: Does this.Close() releases the window instance?
Second question: Is this usage correct?
What do you think is the best practice?
Thank you all.
Window.Close() will dispose all resources allocated by the instance. That's why you cannot show it again once it was closed.
If you want to reuse the same Window instance, you should cancel the closing procedure to prevent disposal of internal resources and collapse the Window instead (by setting Window.Visibility to Visibility.Collapsed - Visibility.Collapsed is also the default value of an instantiated Window before Window.Show() is called).
Alternatively hide the Window by calling Window.Hide() (which will set the Visibility to Visibility.Hidden) instead of Window.Close().
Calling Window.Show will also set the window's visibility to Visibility.Visible.
As a matter of fact, showing a Window by setting Window.Visibility is the asynchronous version of Window.Show().
Generally, you switch between Window instances by using the Window.Activate method. Calling Window.Show on a Window that is currently showing/visible, does nothing.
public partial class MainWindow : Window
{
CustomerOperations CustomerOperationsWindow { get; }
public MainWindow()
{
InitializeComponent();
this.CustomerOperationsWindow = new CustomerOperations();
// Consider to move this logic to CustomerOperations class,
// where you can override the OnClosing method instead of subscribing to the event
this.CustomerOperationsWindow.Closing += CollapseWindow_OnClosing;
}
// Cancel close to prevent disposal and collapse Window instead
private void CollapseWindow_OnClosing(object sender, CancelEventArgs e)
{
e.Cancel = true;
this.CustomerOperationsWindow.Visibility = Visibility.Collapsed;
this.CustomerOperationsWindow.Owner.Activate();
}
private void btnCustomer_Click(object sender, RoutedEventArgs e)
{
this.CustomerOperationsWindow.Owner = this;
// Calling Show will set the Visibility to Visibility.Visible
this.CustomerOperationsWindow.Show();
}
}
Creating a Window instance allocates unmanaged resources. If this happens very frequently, you will keep the garbage collector busy. From a performance point of view you may want to avoid it and prefer to reuse the same instance.
In a common scenario this is not necessary. But since Window exposes a Hide() method, you may consider to use it instead of Close().
If you want to switch to the parent window, you can use the code this.Owner.Activate(); and if you want to close the current window, first this.Owner.Activate(); and then this.Close();.
When you enter this.Close(), the compiler does not execute the following lines after reaching it. And when a sample window still exists there is no need to recreate it
private void btnMain_Click(object sender, RoutedEventArgs e)
{
this.Owner.Activate();
this.Close();
}

How to receive the InkCanvas.StrokeCollected event in the view model

In using MVVM pattern, I have a custom inkcanvas with:
protected override void OnStrokeCollected(InkCanvasStrokeCollectedEventArgs e)
{
CustomStroke newStroke = new CustomStroke(e.Stroke.StylusPoints, e.Stroke.DrawingAttributes);
this.Strokes.Remove(e.Stroke);
this.Strokes.Add(newStroke);
InkCanvasStrokeCollectedEventArgs eNew = new InkCanvasStrokeCollectedEventArgs(newStroke);
// Raises the System.Windows.Controls.InkCanvas.StrokeCollected event.
base.OnStrokeCollected(eNew);
}
How do I get the view model to receive the InkCanvas.StrokeCollected event?
I can not bind the XAML to the strokes as the StrokeCollection.CollectionChanged event will be called three times by the custom inkcanvas.
Any help is appreciated.
Try this
public Window3()
{
InitializeComponent();
var vm=new ViewModel();
this.DataContext = vm;
canvas.StrokeCollected += vm.OnStrokeCollected;
}
ViewModel
public class ViewModel
{
public void OnStrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
{
}
}
Edit
if you want to do it without codebehind see the article EventTrigger
You simply bind it via XAML as you already did, which is the correct way to do it.
That you get 3 events, doesn't matter. Just handle the one you need.
For example, if you are only interested in the StrokeCollectedEvent, then just do
protected override void OnStrokeCollected(InkCanvasStrokeCollectedEventArgs e)
{
if(e.RoutedEvent != InkCanvas.StrokeCollectedEvent)
return;
// handle the event
}
For a full list of Events, consult the "Fields" Section of InkCanvas MSDN documentation. The fields ending with "Event" are RoutedEvent constants, which are passed in the InkCanvasStrokeCollectedEventArgs.

WPF shortcut in whole window

I'm developing MVVM application and I'd like to create global shortcuts for my application. It's necessary to navigatate application between modules. I have main window with code:
<UserControl.InputBindings>
<KeyBinding Command="{Binding ChangeModuleComand}"
Key="M"
Modifiers="Control"/>
</UserControl.InputBindings>
It works when I have focus on my window,
But there is a problem when i have focus on TextBox in that UserControl (Command is not fired).
Is that possible to catch that key pressure without lots of modifications in whole application?
A.
It sounds like your event is being handled before it bubbles up that far. Next I am wondering if you could move your input binding up to your window rather than a user control. My final option would be to remove your input bindings and create a global check in code.
Since you're already working with input bindings, I have added the code option below:
//In App.xaml.cs
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
EventManager.RegisterClassHandler(typeof(UIElement), UIElement.KeyDownEvent, new RoutedEventHandler(GlobalClick));
}
private void GlobalClick(object sender, RoutedEventArgs e)
{
var args = (KeyEventArgs) e;
if (args.KeyboardDevice.IsKeyDown(Key.M) &&
args.KeyboardDevice.IsKeyDown(Key.LeftCtrl) || args.KeyboardDevice.IsKeyDown(Key.RightCtrl))
MessageBox.Show("Works");
}
Documenting this answer for others, as there is a much simpler way to do this that is rarely referenced, and doesn't require touching the XAML at all.
To link a keyboard shortcut at the Window level, in the Window constructor simply add a new KeyBinding to the InputBindings collection. As the command, pass in your arbitrary command class that implements ICommand. For the execute method, simply implement whatever logic you need. In my example below, my WindowCommand class takes a delegate that it will execute whenever invoked. When I construct the new WindowCommand to pass in with my binding, I simply indicate in my initializer, the method that I want the WindowCommand to execute.
You can use this pattern to come up with your own quick keyboard shortcuts.
public YourWindow() //inside any WPF Window constructor
{
...
//add this one statement to bind a new keyboard command shortcut
InputBindings.Add(new KeyBinding( //add a new key-binding, and pass in your command object instance which contains the Execute method which WPF will execute
new WindowCommand(this)
{
ExecuteDelegate = TogglePause //REPLACE TogglePause with your method delegate
}, new KeyGesture(Key.P, ModifierKeys.Control)));
...
}
Create a simple WindowCommand class which takes an execution delegate to fire off any method set on it.
public class WindowCommand : ICommand
{
private MainWindow _window;
//Set this delegate when you initialize a new object. This is the method the command will execute. You can also change this delegate type if you need to.
public Action ExecuteDelegate { get; set; }
//You don't have to add a parameter that takes a constructor. I've just added one in case I need access to the window directly.
public WindowCommand(MainWindow window)
{
_window = window;
}
//always called before executing the command, mine just always returns true
public bool CanExecute(object parameter)
{
return true; //mine always returns true, yours can use a new CanExecute delegate, or add custom logic to this method instead.
}
public event EventHandler CanExecuteChanged; //i'm not using this, but it's required by the interface
//the important method that executes the actual command logic
public void Execute(object parameter)
{
if (ExecuteDelegate != null) //let's make sure the delegate was set
{
ExecuteDelegate();
}
else
{
throw new InvalidOperationException("ExecuteDelegate has not been set. There is no method to execute for this command.");
}
}
}
I'm sure this can be used for other controls as well, but haven't tried it.

Multiple Command Binding

Is it possible to bind the multiple commands to the button.
I have a user control, which i am calling in my main application (parent application).
I want to handle a click command on both the controls (the user control as well as on the main window). However i am only able to get one.
Is there any way in which i can get this.
Any help is really appreciated.
Code Snippet:
public class MainWindowFooterCommands
{
public static readonly RoutedUICommand FooterClickLocalCommand = new RoutedUICommand("Local Button Command", "FooterClickLocalCommand", typeof(MainWindowFooterCommands));
}
private void MainWindowFooterBindCommands()
{
CommandBinding cmdBindingBXClick = new CommandBinding(MainWindowFooterCommands.FooterClickLocalCommand);
cmdBindingBXClick.Executed += ClickCommandHandler;
CommandBindings.Add(cmdBindingBXClick);
}
void ClickCommandHandler(object sender, ExecutedRoutedEventArgs e)
{
//Do Something
}
//Parent Control holding an instance of the footer control.
class MainWindow {
public MainWindow()
{
CommandBinding cmdBindingBXClick1 = new CommandBinding(MainWindowFooterCommands.BXClickMainWindowCommand);
cmdBindingBXClick1.Executed += LoadParent;
CommandBindings.Add(cmdBindingBXClick1);
}
public void LoadParent(object sender, ExecutedRoutedEventArgs e)
{
LoadParentWindow();
}
}
Regards,
Tushar
You might be trying to aggregate multiple commands, which is a natural thing to want to do.
If you are using Prism, there is a class builtin for this called the CompositeCommand (scroll down a bit): https://msdn.microsoft.com/en-us/library/ff921126.aspx
Otherwise, Josh Smith has a very good article on his implementation called a "Command Group": http://www.codeproject.com/KB/WPF/commandgroup.aspx
There are some very nice scenarios you can rollup like this (for instance, "Save All"). A good tool for your bag of tricks.
AFAIK WPF doesnt offer anything out of the box to support multiple commandbindings at various levels, but you could try the following:
void ClickCommandHandler(object sender, ExecutedRoutedEventArgs e)
{
IInputElement parent = (IInputElement) LogicalTreeHelper.GetParent((DependencyObject)sender);
MainWindowFooterCommands.BXClickMainWindowCommand.Execute(e.Parameter, parent);
}
You might have to test whether your parent really is an IInputElement, though.

How can I toggle the main menu visibility using the Alt key in WPF?

I'd like the main menu in my WPF app to behave like the main menu in IE8:
it's not visible when the app starts
pressing and releasing Alt makes it visible
pressing and releasing Alt again makes it invisible again
repeat until bored
How can I do this? Does it have to be code?
Added in response to answers submitted, because I'm still having trouble:
My Shell code-behind now looks like this:
public partial class Shell : Window
{
public static readonly DependencyProperty IsMainMenuVisibleProperty;
static Shell()
{
FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata();
metadata.DefaultValue = false;
IsMainMenuVisibleProperty = DependencyProperty.Register(
"IsMainMenuVisible", typeof(bool), typeof(Shell), metadata);
}
public Shell()
{
InitializeComponent();
this.PreviewKeyUp += new KeyEventHandler(Shell_PreviewKeyUp);
}
void Shell_PreviewKeyUp(object sender, KeyEventArgs e)
{
if (e.SystemKey == Key.LeftAlt || e.SystemKey == Key.RightAlt)
{
if (IsMainMenuVisible == true)
IsMainMenuVisible = false;
else
IsMainMenuVisible = true;
}
}
public bool IsMainMenuVisible
{
get { return (bool)GetValue(IsMainMenuVisibleProperty); }
set { SetValue(IsMainMenuVisibleProperty, value); }
}
}
You can use the PreviewKeyDown event on the window. To detect the Alt key you will need to check the SystemKey property of the KeyEventArgs, as opposed to the Key property which you normally use for most other keys.
You can use this event to set a bool value which has been declared as a DependencyProperty in the windows code behind.
The menu's Visibility property can then be bound to this property using the BooleanToVisibilityConverter.
<Menu
Visibility={Binding Path=IsMenuVisibile,
RelativeSource={RelativeSource AncestorType=Window},
Converter={StaticResource BooleanToVisibilityConverter}}
/>
I just came across this problem myself. I tried hooking into the PreviewKeyDown event, but found it to be unreliable. Instead I found the InputManager class where you can hook into the EnterMenuMode from managed code. The manager exposes two events, for enter and exit. The trick is to not collapse the menu, but set it's container height to zero when it is to be hidden. To show it, simply clear the local value and it will take its previous height.
From my TopMenu user control:
public TopMenu()
{
InitializeComponent();
InputManager.Current.EnterMenuMode += OnEnterMenuMode;
InputManager.Current.LeaveMenuMode += OnLeaveMenuMode;
Height = 0;
}
private void OnLeaveMenuMode(object sender, System.EventArgs e)
{
Height = 0;
}
private void OnEnterMenuMode(object sender, System.EventArgs e)
{
ClearValue(HeightProperty);
}
I'd try looking into handling the PreviewKeyDown event on your window. I'm not sure if pressing Alt triggers this event or not, but if it does, then I'd toggle a bool which is bound to the visibility of the main menu of the window.
If PreviewKeyDown doesn't work, I'm not sure what else to try. You could look into getting at the actual Windows messages sent to your window, but that could get messy very quickly.
It would be better to use GetKeyboardState with VK_MENU to handle both left and right Alt, to mimic the behavior of IE / Windows Explorer (Vista+) you'll need to track the previously focused element to store focus, on a VK_MENU press whilst the focused element is within your main menu. You also want to be doing this work on PreviewKeyUp (not down).
See my answer to the following thread:
How to make WPF MenuBar visibile when ALT-key is pressed?
There I describe how to solve your problem with the class InputManager (from namespace System.Windows.Input).
You can register the classes events EnterMenuMode and LeaveMenuMode.

Resources