Implement Click Event on a WPF control inside WPF - c#

This is connected with my previous question as it's dealt with the same piece of code; now that I've accomplished the changing of the background of the button, the problem is that now i need to implement the same code but not not for ButtonPressed but for clicked button. I've added click handlers in the code but it's not working - the background is not changing. I tried different approaches, even with using bitmaps and imagesources, but it's not working, the change simply does not happen. Now I want to implement the change of the background image but it needs to be done in the XAML file, not in the .cs . Again the code:
<Button x:Class="proba4.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="120" d:DesignWidth="300" IsEnabled="True">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Image Name="Normal" Source="C:\stuff\off_button.gif"/>
<Image Name="Pressed" Source="C:\stuff\on_button.gif" Visibility="Hidden"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Normal" Property="Visibility" Value="Hidden"/>
<Setter TargetName="Pressed" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
Note that I've already looked for some button properties for clicking, but there was none, and all the implementations I've found on internet are dealing with adding Button_Click method in the .cs code of the control, and since that is not working for me, I need to find another way - hopefully something like fully implemented click control using WPF.
I guess this is delicate, but I will greatly appreciate any help with this, thanks.

It sounds like you want the behavior of a ToggleButton, not a Button. On Button clicking is a discrete event rather than a state that the control goes into that can be expressed by a property. A ToggleButton switches back and forth between two (or three) states when clicked, and the IsChecked property represents the state and can be bound in a Trigger like you're doing with IsPressed in your example.

Related

Events are triggered twice on a CustomControl

The problem is that the KeyDown event is triggered twice, the first one comes from the CustomTextBox named textSource inside the Style; the second, from control in the MainWindow named "CTbox".The linked question provides a solution where you filter on the EventHandler OnKeyDown the source e.Source != "textSource" which is working fine:
if (e.Source is CustomTextBox sourceTextBox && sourceTextBox.Name.Equals("textSource"))
{
return;
}
Basically I would like to know if there is any better solution to this and if someone can explain the reason why is this happening and how can be avoid it.
The style is mean to create a Hint Text or WaterMark in the CustomTextBox without relaying in the Focus events
Thanks in advance.
Following the code to create a Minimal, Complete, and Verifiable example of this behaviour
CustomTextBox Class:
public class CustomTextBox : TextBox
{
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
}
}
MainWindow:
<Window x:Class="WpfApp2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style x:Key="CustomTextBoxStyle"
TargetType="{x:Type local:CustomTextBox}">
<Setter Property="Foreground"
Value="#FF414042" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomTextBox}">
<Border Name="Border"
BorderBrush="#FF348781"
BorderThickness="0,0,0,4"
CornerRadius="2">
<ScrollViewer x:Name="PART_ContentHost"
Margin="0" />
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled" />
<VisualState x:Name="ReadOnly" />
<VisualState x:Name="MouseOver" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type local:CustomTextBox}"
BasedOn="{StaticResource CustomTextBoxStyle}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomTextBox}">
<Grid>
<local:CustomTextBox
Text="{TemplateBinding Text}"
x:Name="textSource"
Background="Transparent"
Panel.ZIndex="2"
Style="{StaticResource CustomTextBoxStyle}"/>
<TextBlock Text="{TemplateBinding Tag}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground"
Value="Transparent" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Text, Source={x:Reference textSource}}"
Value="">
<Setter Property="Foreground"
Value="Gray" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<local:CustomTextBox x:Name="CTbox" Tag="Hint Text Example" Height="25" Width="258"/>
</Grid>
</Window>
So you're getting two executions of your OnKeyDown handler because your XAML essentially builds a CustomTextBox within a CustomTextBox. With the routing strategy of WPF events and your visual tree looking like this:
the KeyDown event naturally will fire in both places (textsource, then bubble up to CustomTextBox). Side note, if you overrode OnPreviewKeyDown, you should get the reverse order - CTbox tunneling down to textSource.
To answer the second part of your question - how to avoid this, I think perhaps you should rethink your implementation here. My questions would be:
Should you even use an editable control (your CustomTextBox in this case) for your hinttext overlay in your control template? Since the hinttext should only be readable, perhaps a TextBlock would suffice.
Should you override the ControlTemplate for customtextbox to get this HintText functionality? Perhaps the better way is just to create a UserControl that provides this functionality. i.e. Grid containing your custom textbox and a textblock overlaying it. This will prevent you from having a visual tree with nested CustomTextBox's.
If you need a ControlTemplate so you can reuse it, why not make it the default control template of your CustomTextBox? If your "Tag" property is not bound, the hinttext will just naturally not show. This way you won't have nested CustomTextBoxes causing duplicate execution of OnKeyDown as well!
Why do you even need a CustomTextBox? Do you have other override code and behavior that you're not showing that requires this? I'm guessing this is because you showed a minimal sample and there's more - but just thought I'd ask :)
EDIT for comment
Given your clarifications/questions, I would've approached solving this via a custom control. I know what you have is technically a custom control, but I'm talking about the kind that comes with a themes\generic.xaml file :). If you're not familiar, I recommend creating a new VS project and making it of the "WPF custom control library" template. Then you should be able to add a new class of the template "Custom Control (WPF)". You'll see that VS has generated a themes\generic.xaml file for you - this is where you would hold the controltemplate for your CustomTextBox. I would get the default control template of a TextBox, and add in a TextBlock that's not hit test visible (so a user can click through to your editable area) and set a TemplateBinding on it for the HintText. You'll then be able to reuse this custom control everywhere(as it's compiled in a separate dll... you can also opt to keep it within your current project too), get the default behaviors of textbox that you didn't override, and won't have nested CustomTextBoxes.

Why does WPF IsKeyboardFocused provide false information?

I attempted to follow a pattern described in an answer to this question.
How to set focus to textbox using MVVM?
However, I am having trouble with the concept of keyboard focus. If I have notepad or some other application running at the same time as my WPF application and click on notepad to put the keyboard focus there, then do something to cause my other application to put the focus into one of its text boxes, then the trigger gives the visual cue that my application's text box now has the keyboard focus. However when I start typing I can see that is not the case because the text is actually going into notepad.
Here is the xaml for my trigger.
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding ReadyForDataEntry}" Value="True">
<Setter Property="FocusManager.FocusedElement" Value="{Binding RelativeSource={RelativeSource Self}}" />
</DataTrigger>
<Trigger Property="IsKeyboardFocused" Value="true">
<Setter Property="Background" Value="Lavender"/>
<Setter Property="BorderBrush" Value="Blue"/>
</Trigger>
</Style.Triggers>
</Style>
Essentially the textbox will sometimes light up with the border and background color indicating that IsKeyboardFocused = true for that textbox, even though keyboard entry will be received by whatever application (e.g., one note, notepad) was last clicked in. What am I missing? Why would that WPF control have IsKeyboardFocused set true when the keyboard focus is clearly not true at all?
You're not doing anything wrong; this is a known quirk of WPF.
When a control receives logical focus, WPF attempts to give it keyboard focus as well. However, when you assign keyboard focus in an inactive WPF application, the application behaves as if it's active. That means, among other things, a focused TextBox will show a blinking caret, and IsKeyboardFocused and related properties will be set.
I have seen this issue in the past, and it was trivial to reproduce.
Xaml:
<Window x:Class="WpfTest.FocusTest"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center">
<TextBox x:Name="_textBox"
Width="150">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="IsKeyboardFocused"
Value="true">
<Setter Property="Background"
Value="Lavender" />
<Setter Property="BorderBrush"
Value="Blue" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<Button Margin="0,7,0,0"
Content="_Click Me"
Click="OnButtonClick" />
</StackPanel>
</Grid>
</Window>
Code behind:
public partial class FocusTest
{
public FocusTest()
{
InitializeComponent();
}
private void OnButtonClick(object sender, RoutedEventArgs e)
{
_textBox.Text = "";
// NOTE: Requires System.Reactive.Core, available in NuGet.
System.Reactive.Concurrency.Scheduler.Default.Schedule(
TimeSpan.FromSeconds(5),
() => this.Dispatcher.BeginInvoke(new Action(this.SetFocus)));
}
private void SetFocus()
{
_textBox.Text = "Focused";
FocusManager.SetFocusedElement(_textBox, _textBox);
}
}
Hit the button, Alt+Tab over to Notepad, and wait 5 seconds for the TextBox to receive focus. The IsKeyboardFocused trigger is fired, and the blinking caret appears, but keyboard input is still sent to Notepad.
The key point here is that the problem only arises when an element is given focus while another application is active (hence the artificial delay). Note that the problem still arises if you replace the SetFocusedElement call with Keyboard.Focus(_textBox), _textBox.Focus(), and other variations.
Unfortunately, I am not aware of a reliable, non-hacky way of fixing this issue. I don't recall how much time I spent on it, but I ultimately decided it wasn't worth the trouble. It's just not something that comes up that often.

WPF, Telerik: Change control style when disabled

I have a trouble with Telerik control.
<Style x:Key="RadDropDownButtonStyle" TargetType="telerik:RadDropDownButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="telerik:RadDropDownButton">
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="Red" />...
So, this handles Disabled property. Text changes its color, but it is not contrast, something like watermark.
How can I disable this? And make disabled control more contrast?
Text changes its color, but it is not contrast, something like watermark.
For future references, this is caused by the Opacity property. When the opacity is lower than 1 it becomes lighter.
The problem you are describing is something very familiar. And I'm afraid there isn't any great way to get rid of this, so much so that the best advise that I can give you is to not even bother. If anything, it will just make using the themes and styles a real pain.
However there is an easy way to get your desired behaviour by replacing the IsEnabled="False" property with IsHitTestVisible="False" and Focusable="False"
These will make it impossible to click or focus the control by keyboard navigation, basically making it disabled. Now you can add some more style properties to make it look the way you believe a disabled control should look like. By for example setting the wanted Foreground and Background or you could even use an Opacity closer to 1 (ex: 0.9) which will make it less dark than the original but still dark enough to read properly.
I hope this helps you with your current problem, just leave a comment if you want me to clarify further.
EDIT 1: Apperently can overwrite the opacity change by using your own DataTemplate for the control. How to have 100% opacity even when control is disabled in wpf C#
EDIT 2: I'll give you an example of how to use the other properties correctly.
This is how you would define a disabled button normally, and doing so will make the text lighter and less readable.
<!-- Simple disabled button -->
<telerik:RadButton Content="Test Button 1" IsEnabled="False" />
<!-- Button with binding on IsEnabled -->
<telerik:RadButton Content="Test Button 2" IsEnabled="{Binding MyBinding}" />
Now I'll show you how you can mimic these results by using the properties IsHitTestVisible and Focusable.
<!-- Simple disabled button -->
<telerik:RadButton Content="Test Button 1" IsHitTestVisible="False" Focusable="False" />
<!-- Button with binding on IsEnabled -->
<telerik:RadButton Content="Test Button 2" IsHitTestVisible="{Binding MyBinding}" Focusable="{Binding MyBinding}" />
In the above examples, the buttons will look as if they are still enabled. However you won't be able to focus or click them. Of course we do want to see some difference to be able to tell that they can't be used.
<!-- Styled disabled button -->
<telerik:RadButton Content="Test Button 1" IsHitTestVisible="False" Focusable="False" >
<telerik:RadButton.Style>
<Style TargetType="telerik:RadButton">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=IsHitTestVisible}" Value="False">
<Setter Property="Opacity" Value="0.8"/>
</DataTrigger>
</Style.Triggers>
</Style>
</telerik:RadButton.Style>
</telerik:RadButton>

WPF Style can't work on multiple controls

I am using wpf style on my controls so that i can use an style on multiple controls at once. It usually works. for example i made an saveButtonStyle and i apply it on every save button on my application. But it doesn't work on MenuItems.
I made a style for my menuitems which contains an icon next to the items.
This is one screen shot of it.
You see the Datagrid has an ContextMenu and within it there is multiple menu items. in this case pay attention to Set Alarm. it has an icon. This Set Alarm Menu item is also in another menu datagrid next to this one. When i click that one
it appears too
But problem is when i right click back to the other datagrid the icon is gone and wont come back. this is the screen shot
Here is the style I made
<Style x:Key="menuItemAlert" TargetType="{x:Type MenuItem}">
<Setter Property="Icon">
<Setter.Value>
<Image Source="Content/AlertIcon.png" Width="20" Height="20" />
</Setter.Value>
</Setter>
</Style>
And here is how I apply it to my controls
<MenuItem x:Name="customerContextMenuSetAlarm" Header="SetAlarm" Style="{StaticResource menuItemAlert}" Click="customerContextMenuSetAlarm_Click"/>
Do you know why it happens?
style menuItemAlert creates only one instance of Image and can display it in one place only. to overcome this make a separate non-shared resource for that Image.
<Image x:Key="AlertIcon" x:Shared="False" Source="Content/AlertIcon.png" Width="20" Height="20" />
<Style x:Key="menuItemAlert" TargetType="{x:Type MenuItem}">
<Setter Property="Icon" Value="{StaticResource AlertIcon}"/>
</Style>

Silverlight Toggle Button Grouping

I'm developing a Silverlight app and would like to create a grouping of 5 toggle buttons (used for menu options) that animate when clicked (grow in size) and also cause any previously clicked buttons in the group to unclick and animate back to their shrunken size.
I know I could use a brute force approach where the app is directly aware of each button, but if I add or change the menu (add/remove a button) I'd have to remember to modify the code (which is bad since I'm forgetful). Is there a way to more intelligently group the buttons so that when one is clicked is can tell all the others in the group to unclick?
Thanks!
Todd
Special shout-out to Michael S. Scherotter for pointing me in the right direction to use RadioButton and a control template!
Here's the basic control template that I came up with. Put this in the App.xaml between the tags if you care to see it. The UX folks will give it a once over to pretty it up, but for now, it works as a radio button that looks like a toggle button (or just a button), but has a groupname.
An important note: this template doesn't have any of the basic button animation, so it won't press down when clicked... That's the UX work I mentioned above.
<Style x:Key="MenuButton" TargetType="RadioButton">
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="25"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Border BorderBrush="DarkGray" BorderThickness="3" CornerRadius="3" Background="Gray">
<!-- The ContentPresenter tags are used to insert on the button face for reuse -->
<ContentPresenter></ContentPresenter>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I was trying to do the same thing and I found this old post. Let me update it a bit...
Todd seems to be like many other lazy programmers, letting the UX guys do all the work. :) So here is Todd's solution with actual toggle buttons as radio buttons in a group:
<RadioButton GroupName="myGroup"
Style="{StaticResource MenuButton}"
Content="One"
IsChecked="True" />
<RadioButton GroupName="myGroup"
Style="{StaticResource MenuButton}"
Content="Two" />
<RadioButton GroupName="myGroup"
Style="{StaticResource MenuButton}"
Content="Three" />
<Style x:Key="MenuButton" TargetType="RadioButton">
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="25"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Border BorderBrush="DarkGray" BorderThickness="3" CornerRadius="3" Background="Gray">
<ToggleButton IsChecked="{Binding IsChecked, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}">
<ToggleButton.Content>
<ContentPresenter></ContentPresenter>
</ToggleButton.Content>
</ToggleButton>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And now you'll have your pretty button rollover effects and all that and only one toggle button will be able to be 'checked' at one time.
Both radio buttons and toggle buttons can be three state, and you can bind the 'IsThreeState' property of the toggle button as well in the same manner that I bind 'IsChecked' here. But by default they are two state.
Also, the long form binding is important here as the shortcut {TemplateBinding IsChecked} would default to one way and we need them to stay in sync both ways.
This example does not, of course, animate the buttons with size changing and all that Todd originally posted, but it does give you regular buttons that you can easily distinguish as being checked or not.
Give all of the RadioButton objects the same GroupName.

Categories