How to generate event handler automatically in WPF, C#? - c#

I'm currently learning to code WPF application after having used forms for a while.
I have a simple question, but can't seem to find an anwser anywhere.
Is it possible to automatically generate the 'private void eventName(...' code when creating an event ?
For example, if I create a WPF Form with a simple button and code :
Button x:Name = "mButton" Content = "Hello" Click = "mClick" /
Is there a trick to have the private void event handler create itself ? Cause right now, I either write it manually or double click in the event handler properties tab. In widowsForm, I could just double click and it would create itself.
This isn't a big issue for 1 or 2 but if I want to create a dozen buttons, it can become tedious.
I apologize if the question can seem obvious

Of-course, the more automated and lazier the better.
So a few tips:
You can generate a new event handler with an automated name like this:
Assign the x:Name before creating or assigning the event handler
Pick the default <New Event Handler> from the list of options IDE gives you for your event handler. it will generate something like:
MouseDoubleClick="mButton_MouseDoubleClick"
or Click="mButton_Click"
If the name is already taken, it will be prefixed with _1
If the x:Name is not assigned, it will be prefixed with Button_ instead of x:Name
You can generate any already-written event handler like this:
Right click on handler's name in XAML code ("mClick") and choose Go To Definition (The default shortkey is F12)
F12 does the same thing as double-clicking on an event handler value in properties window in WinForms. In case of default event (like Button's Click, it does the same as double-clicking directly on the control)
If you don't want the control to contain any code for event handler like:
<Button /> // handles the click event magically
Then you can add this to the container of all the buttons:
<Container.Resources>
<Style TargetType="Button">
<EventSetter Event="Click" Handler="mClick"/>
</Style>
</Container.Resources>
(obviously, I supposed the name of the container is Container. In your case it might be Window or Menu etc.)
Now every button inside this container has its Click handled by the same handler, in which you can redirect your logic to the right method:
Dictionary<string, Action> dic;
private void mClick(object sender, RoutedEventArgs e)
{
dic[(sender as Button).Name]();
}
These all still so tedious compared to MVVM pattern:
<ItemsControl ItemsSource="{Binding myButtons}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding ButtonText}" Command="{Binding ButtonAction}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

If you want to do this rather than use a databinding/command pattern, you can use the XAML designer in Visual Studio. If you start typing Click=" you should be prompted with a list of possible event handlers or a new one - selecting one and pressing tab will create the event handler in the code behind for you (you might want to rename it, or make sure you name the button in XAML first).

Related

WPF ComboBox PreviewMouseDown

I have a combobox that is editable and a textbox.
<TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="86,149,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/>
<ComboBox x:Name="comboBox" HorizontalAlignment="Left" VerticalAlignment="Top" Width="120" Margin="282,150,0,0" IsEditable="True" PreviewMouseDown="ComboBox_PreviewMouseDown"/>
I don't understand why ComboBox_PreviewMouseDown does not trigger, when the focus is on the textbox and I click on the combobox. It just highlights the text in the combobox and sets the focus. Clicking in the combobox when it already has the focus, PreviewMouseDown fires.
Is that what's happening here? Why is a PreviewMouseDown in an unfocused combobox not working?
When ComboBox.IsEditable is set to True, the ComboBox internally sets the focus (and keyboard focus) to the edit TextBox to make it instantly available for text input. This makes total sense as the intention when clicking the edit TextBox is always to enter or edit some text. Otherwise, the user would have to click the TextBox twice to make it receive focus for text input (keyboard focus).
So, to prevent focus stealing, the author marked the MouseDown event as handled i.e. RoutedEventArgs.Handled is set to true. (This is the reason why most non-preview events are marked handled by most controls).
Also, the author wanted to prevent the moving of the caret when clicked into the edit TextBox for the first time (to give it focus): the PreviewMouseDown event's RoutedEventArgs.Handled will only be set to true, if the edit TextBox has no keyboard focus and the drop-down panel is closed. (That's why the second click into the TextBox will pass through to be handled by an added event handler).
To achieve the behavior you expect, you have to handle the UIElement.PreviewGotKeyboardFocus event or the attached Keyboard.PreviewGotKeyboardFocusevent on the ComboBox.
Alternatively register the event handler using the UIElement.AddHandler method and set the handledEventsToo parameter to true:
this.MyComboBox.AddHandler(
UIElement.PreviewMouseDownEvent,
new RoutedEventHandler(MyComboBox_PreviewMouseDown),
true);
I ran into this same issue myself. A simple and effective workaround is to wrap your ComboBox in a lightweight ContentPresenter, then attach your PreviewMouseDown handler to that, like so:
<ContentPresenter x:Name="MyComboBoxWrapper"
PreviewMouseDown="MyComboBoxWrapper_PreviewMouseDown">
<ContentPresenter.Content>
<ComboBox x:Name="MyComboBox" />
</ContentPresenter.Content>
</ContentPresenter>
Additionally, since this control gets the PreviewMouseDown event before the ComboBox does, you not only can use it to pre-process events before the ComboBox even sees them, but you can cut off the ComboBox entirely by setting the event arg's handled property to 'true.'
Works like a charm! No subclassing or other tricks needed and it only requires a lightweight control in the tree!
Notes
As some may have considered, technically you could attach the PreviewMouseDown event to any ancestor of your ComboBox, but you then may have to include logic in that handler to determine if you're actually clicking on the ComboBox vs something else.
By using an explicit ContentPresenter (an incredibly lightweight element that itself doesn't have any rendering logic. It simply hosts other elements), you now have a dedicated PreviewMouseDown handler just for this control. Plus, it makes it more portable should you need to move it around since the two items can travel together.

Is using command binding or event handler a better approach to implement Close, Minimize and Maximize Buttons in WPF?

I am making the close, maximize and minimize buttons in WPF C#. I tried two different methods and both seemed to work with me but I just want to know which approach in general is considered a better practice for this kind of implementation.
Method one:
in XAML:
<Window.CommandBindings>
<CommandBinding Command="{x:Static SystemCommands.CloseWindowCommand}" CanExecute="CommandBinding_CanExecute_1" Executed="CommandBinding_Executed_1" />
<CommandBinding ... />
<CommandBinding ... />
</Window.CommandBindings>
<Button Command="{x:Static SystemCommands.CloseWindowCommand}" Content ="close"/>
<Button ... />
<Button .../>
in C#:
private void CommandBinding_Executed_1(object sender, ExecutedRoutedEventArgs e)
{
SystemCommands.CloseWindow(this);
}
...
Method two:
in XAML:
<Button Content="X" Click="CloseButton_Click" />
<Button .../>
<Button ... />
in C#:
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
Close();
}
...
Since the close, maximize and minimize buttons are typically part of a control, or a template of a control, you could handle the Click event of the buttons directly. There is no need nor reason to introduce any command bindings just to be able to handle the click of a Button internally in a control.
You could either hook up the event handlers in XAML or, if the buttons are defined in a ControlTemplate of a custom control, you could override the OnApplyTemplate() method of the control and hook up the event handlers programmatically as suggested here: https://blog.magnusmontin.net/2013/03/16/how-to-create-a-custom-window-in-wpf/.
Button click handler is simple and fast (to implement). If you are sure you are going to use just this button to close your window - click handler is good enough. If however there is possibility you are going to need more than one place to close the window (for example - you are going to use "close" menu option) - then it's better to not spread the same logic over multiple places and use a command, because command has all handling logic in one place (CommandBinding_Executed_1 in your case). Also, command has CanExecute logic so you will be able to automatically disable your close button (and all other "close" controls) in case window cannot be closed right now (with click handler you will have to code this logic yourself). Also, with command you can close your window from any control down the tree, even if that control has no reference to your window at all. This applies to "command vs click handler" in general, not specifically to the case with closing window where some of the above might seem a bit contrieved.

GridView in UWP

I are facing issue with GridView Control. We had a working Windows Store App on 8.1 where GridView left and right mouse clicks had different functionality. In the case of left mouse click, we used to use “ItemClick” event which performs navigation to another XAML page. On right click of GridItem, it gets selected and shows the appbar, we have used “SelectionChanged” event for this.
We are now migrating our existing windows store app to UWP Application, we have used same gridView Code, we find significant difference in functionality and look & feel, we don’t see GridView Item Selected like above picture. We see “ItemClick” and “SelectionChanged” are working together. The flow is something like that on left click on the item, the control goes to SelectionChanged event and then ItemClick. We were not able to differentiate actions like Left Mouse Click and Right Mouse click, since both events are getting fired up upon clicking on left click/tapping. We have different functionality on left and right clicks of mouse.
Need help on how to mimic windows 8.1 functionality in UWP.
My requirement was the I wanted to use Right Click/Long Tapped to select an item and take an action accordingly from App Bar Buttons and on Left Click/Tap should redirect me to the next XAML Page. The problem I was facing was the on Right Click, I wasnt able to detect that which items of GridView has been clicked and how can I add that into SelectedItem.
What I did was, I introduced extra Grid in DataTemplate of GridView. Within this Grid, I added RightTapped event.
The sample code snippet is
<GridView x:Name="ItemGridView"
ItemsSource="{Binding Source={StaticResource ItemsViewSource}}"
IsItemClickEnabled="True"
SelectionMode="Single" ItemClick="ItemGridView_ItemClick"
SelectionChanged="ItemGridView_SelectionChanged">
<GridView.ItemTemplate>
<DataTemplate>
<Grid RightTapped="Grid_RightTapped">
<Border Background="White" BorderThickness="0" Width="210" Height="85">
<TextBlock Text="{Binding FileName}" />
</Border>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
The event name is Grid_RightTapped. This helped me detect that from which GridViewItem, I got the long tap/right click.
The code-behind for this is:
private void Grid_RightTapped(object sender, RightTappedRoutedEventArgs e)
{
Song selectedItem = (sender as Grid).DataContext as Song;
//the above line will get the exact GridViewItem where the User Clicked
this.ItemGridView.SelectedItem = selectedItem;
//the above line will add the item into SelectedItem and hence, I can take any action after this which I require
}
}
The reason we are doing this way is, because now we can add clicked item into the GridView SelectedItem using Right Click. Now in UWP, clicked items are added into SelectedItem using left click only. And with left click, I can navigate to another page using ItemClick event.
You are correct, there has been a change in the interaction model behavior. According to MSDN article How to change the interaction mode (XAML)
For selection, set IsItemClickEnabled to false and SelectionMode to
any value except ListViewSelectionMode.None and handle the
SelectionChanged event (ItemClick is not raised in this case).
For invoke, set IsItemClickEnabled to true and SelectionMode to
ListViewSelectionMode.None and handle the ItemClick event
(SelectionChanged is not raised in this case).
Another combination is to set IsItemClickEnabled to false and
SelectionMode to ListViewSelectionMode.None. This is the read-only
configuration.
A final configuration, which is used least often, is to set
IsItemClickEnabled to true and SelectionMode to any value except
ListViewSelectionMode.None. In this configuration first ItemClick is
raised and then SelectionChanged is raised.
You seem to be using the last option - IsItemClickEnabled is set to true and SelectionMode is set to something that's not None. According the Microsoft, this is used least often so maybe it would be a good idea to rethink this design?
Since you haven't shared any code that you already tried, I will just throw in one idea: maybe playing around with Tappedand RightTapped event handlers could help you differentiate between the two more easily?
To identify left and right click, for right click you can use RightTapped event
<GridView x:Name="categoryItemsGV"
Margin="5,5,0,0"
IsItemClickEnabled="True"
ItemClick="categoryItemsGV_ItemClick"
IsRightTapEnabled="True"
RightTapped="categoryItemsGV_RightTapped"
SelectionMode="Single"
SizeChanged="categoryItemsGV_SizeChanged"
ItemsSource="{Binding}">
and .cs code is below:
private void categoryItemsGV_RightTapped(object sender, RightTappedRoutedEventArgs e)
{
var tablemod = (sender as GridView).SelectedItem;
}
From RightTapped the item over which the mouse was right clicked can be obtained from e.OriginalSource
<GridView x:Name="myGridView" VerticalAlignment="Center">
<GridView.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem Text="Reset"/>
<MenuFlyoutSeparator/>
<MenuFlyoutItem Text="Repeat"/>
<MenuFlyoutItem Text="Shuffle"/>
</MenuFlyout>
</GridView.ContextFlyout>
</GridView>
Private Sub myGridView_RightTapped(sender As Object, e As RightTappedRoutedEventArgs) Handles myGridView.RightTapped
myGridView.SelectedItem = e.OriginalSource
End Sub
Now that RightClick has selected the desired item, further action like delete, copy can be executed on it.

Use SendKeys in a UserControl

I want to use the C# System.Windows.Forms.SendKeys.SendWait() Method to send Keystrokes from an OnScreenKeyboard to a Textbox. Since I may use this OnScreenKeyboard at other places too I created a UserControl with View (for the Design of the Keyboard) and Viewmodel (basically for calling the Sendkeys.SendWait() Method) in an extra project.
Within this project I created a MainView where I included the UserControl via a ContentControl as you can see in the Code below. CurrentPage basically refers to the Viewmodel of the Keyboard.
<Window.Resources>
<DataTemplate DataType="{x:Type viewModel:KeyboardViewmodel}">
<view:KeyboardView/>
</DataTemplate>
</Window.Resources>
<Grid>
<Border Background="White">
<HeaderedContentControl Content="{Binding Path=CurrentPage}"/>
</Border>
<TextBox Width="120"/>
</Grid>
I now have the OnScreenKeyboard and a Textbox in my Window. Clicking into the Textbox and pressing buttons of my OnScreenKeyboard will result in text appearing in my Textbox. All Controls within the KeyboardView are set to avoid getting focus. This is necessary to maintain focus on the Textbox.
The Buttons in the KeyboardView all bind to the Command ClickCommandin my KeyboardViewmodel. Here is the code of the KeyboardViewmodel:
public class KeyboardViewmodel : BaseModel
{
public BaseCommand ClickCommand { get; set; }
public KeyboardViewmodel()
{
ClickCommand = new BaseCommand(PressAndRelease);
}
public void PressAndRelease(object key)
{
if (((string)key).Length <= 1)
SendKeys.SendWait((string)key);
else
SendKeys.SendWait("{" + (string)key + "}");
}
}
Now I did create a NuGet Package with these Files and imported them to the project where I want to use my OnScreenKeyboard.
I did do basically the same as when I tested the OnScreenKeyboard before.
But let me explain the structure of the project a little more:
I have a MainView + MainViewmodel. The MainViewmodel manages the navigation between available pages. To show these pages I have - as in the short example before - a ContentControl whose content is bound to a CurrentPage Property. The MainViewis a normal Window, all other Views are UserControls.
In one of these pages I need an OnScreenKeyboard (DetailsView + DetailsViewmodel). So it seemed logical to me to use another ContentControl within the DetailsView:
<Border Grid.Column="0" Grid.Row="4" Grid.ColumnSpan="3" Height="Auto" Width="Auto">
<ContentControl Content="{Binding Path=OnScreenKeyboard}"/>
</Border>
I create the KeyboardViewmodel in the constructor of the DetailsViewmodel. The constructor of the DetailsViewmodel is called in the MainViewmodel at startup.
So now everything works out fine so far, the OnScreenKeyboard is shown on the correct page in the correct place. If I click a button of the OnScreenKeyboard the proper bound command is called and the SendKeys.SendWait() Method is called.
But no text appears in the TextBox. I have a very bad understanding of the SendKeys.SendAwait() Method. Also, the MSDN Documentation seems to be not very exhaustive on this topic.
It states: "Sends the given keys to the active application, and then waits for the messages to be processed."
Now. The Active / Focused Application is my Application. So my guess is that the KeyStrokes should be processed by my Textbox.
My Questions:
Any guesses how to debug the 'SenWait()' Method further e.g. track where the strokes are really sent to or something like that?
Is this the correct way for sending KeyStrokes to an active Application? It seems like SendKeys comes from Windows Forms, I use WPF.
Should I just pass my Textbox as reference to the OnScreenKeyboard and write directly to the referenced Textbox? This would make me much less flexible in regards of reusability.
Update:
As pointed out in the comments this could probably be a duplicate question.
I am well aware of the various different solutions and have already considerd them:
http://wpfkb.codeplex.com/
http://www.codeproject.com/Articles/32568/A-Touch-Screen-Keyboard-Control-in-WPF
http://www.codeproject.com/Articles/145579/A-Software-Virtual-Keyboard-for-Your-WPF-Apps
But as one may understand these projects are looking all way too powerfull for my simple needs.
Here a screenshot to provide a better understanding of my needs:
It is really as simple as that. 4 rows of buttons that will never change, no other controls / functionality than sending the CommandParameter of the pressed button to the Textbox / Active Form.
Researching on that specific problem hasn't shown any problems like that. In most other SO Questions the problem is to send Data to another Window, not to send Data WITHIN the current Window.
So I don't consider this question as duplicate.

Using Events/Commands with XamlReader

I am dynamically building my datatemplate using XamlReader.Parse(string). The problem I have is that I can't put any events on any of the controls I create using the XamlReader. After doing some research online I've learned that this is a known limitation of XamlReader.
I don't know a lot about commands in WPF but could I somehow use them to gain the same result? If so how? If not is there any way I can handle an event in my code behind from a control created using Xaml Reader?
Below is an example of the datatemplate I create. I have the MenuItem_Click event handler defined in the the codebehind of the Window that will host this datatemplate.
I get the following error when trying to run it: System.Windows.Markup.XamlParseException was unhandled: Failed to create a 'Click' from the text 'MenuItem_Click'.
DataTemplate result = null;
StringBuilder sb = new StringBuilder();
sb.Append(#"<DataTemplate
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Grid Width=""Auto"" Height=""Auto"">
<TextBlock Text=""Hello"">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem
Header=""World""
Click=""MenuItem_Click""></MenuItem>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</Grid>
</DataTemplate>");
result = XamlReader.Parse(sb.ToString()) as DataTemplate;
Hoping a late answer might help others:
I found that I needed to bind the events after the parsing, and had to remove the click event from the Xaml string.
In my scenario I applied the resulting DataTemplate to an ItemTemplate, wired up the ItemSource, and then added the handler. This does mean the click event would be the same for all the items, but in my case the header was the information needed and the method was the same.
//Set the datatemplate to the result of the xaml parsing.
myListView.ItemTemplate = (DataTemplate)result;
//Add the itemtemplate first, otherwise there will be a visual child error
myListView.ItemsSource = this.ItemsSource;
//Attach click event.
myListView.AddHandler(MenuItem.ClickEvent, new RoutedEventHandler(MenuItem_Click));
And then the click event needs to get back to the original source, the sender will be the ListView in my case that was using the DataTemplate.
internal void MenuItem_Click(object sender, RoutedEventArgs e){
MenuItem mi = e.OriginalSource as MenuItem;
//At this point you can access the menuitem's header or other information as needed.
}
Take a look at this link. Most of the solutions there will apply with Parse as well. I'm not really a C# dev, so the only one I can really explain is the last one, which is something of an if-all-else-fails option:
First, you add ID's to your XAML instead of Click, etc attributes. Then you can use FindLogicalNode to get at nodes, and then wire up the events yourself.
For example, say you give your MenuItem ID="WorldMenuItem". Then in your code after calling parse, you can do this:
MenuItem worldMenuItem = (MenuItem)LogicalTreeHelper.FindLogicalNode(result, "WorldMenuItem");
worldMenuItem.Click += MenuItem_Click; // whatever your handler is

Categories