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.
Related
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.
Is it possible to put Button in WPF slider thumb? Not custom thumb itself but simply add button on it?
I'm not sure how you would put a button in it without customizing the Thumb. If you're interested in customization:
It looks like this sample customizes the Thumb as well as the rest of the slider.
<Style x:Key="CustomThumbForSlider" TargetType="{x:Type Thumb}">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Ellipse Fill="#FF8F4040" Stroke="#FF000000" Height="15" Width="15"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Having never personally done this, my guess would be that you just replace the following portion with the control template you're using:
<Ellipse Fill="#FF8F4040" Stroke="#FF000000" Height="15" Width="15"/>
EDIT: Unless you explicitly need the functionality of a button, you should probably use something like a TextBlock to display your value.
I'm not sure I understand the question, but : the whole interest of WPF is the ability to customize each element with ControlTemplates. Here is an article from Josh Smith explaining it : Understanding Templates in WPF and here a tutorial on customizing sliders : Custom sliders.
Edit: forgot to put the link title. My bad.
Im making a custom button called "ShardButton" which uses a Style to control its content as well as its event triggers:
<Style x:Key="ButtonStyle" TargetType="{x:Type obj:ShardButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type obj:ShardButton}">
<Grid>
<Path .../>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
RecognizesAccessKey="True"/>
</Grid>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="ButtonBase.Click">
!!!SOMETHING HERE!!!
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Im using the above XAML in the main Window1.xaml file, and creating a xmlns to the "obj" namespace, so what I want to do is to call a custom event handling method on the ShardButton class, how do I do that in XAML?
I cant seem to find the right way to phrase it for a Google search...
Thanks for any help you can give me!
Mark
You can't directly execute event handlers in EventTriggers, they were'nt created for this purpose.
However, if every instance of your ShardButton must do the same thing on click, simply override the OnClick method in your ShardButton class and do whatever you have to do here.
If you don't desire this behavior but instead want to have a specific handler executed for each ShardButton instance that is using this specific style defined in Window1.xaml, add another setter:
<Setter Property="Command" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type obj:Window1}}, Path=MyCommand}" />
Where MyCommand is an instance property of Window1 returning a custom implementation of ICommand that will invoke your handler. This line of code assumes that every button using this Style is somewhere in the visual tree of Window1, which should be probably the case (or the Style would be defined elsewhere).
You can use an EventSetter in your Style:
<Style ...>
<EventSetter Event="Click" Handler="yourHandlerMethod"/>
</Style>
Then in your code-behind:
private void yourHandlerMethod(object sender, RoutedEventArgs e)
{
...
}
I'm writing a very basic WPF dialog and want to apply a simple style to all objects that inherit from the Control class. The code I'm using:
<Window.Resources>
<Style TargetType="{x:Type Control}">
<Setter Property="Margin" Value="20"/>
</Style>
</Window.Resources>
<StackPanel>
<TextBlock Text="some text"/>
<TextBox x:Name="x_NameTextBox"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Name="x_CancelButton" Click="x_CancelButton_Click" Content="Cancel"/>
<Button x:Name="x_OkButton" Click="x_OkButton_Click" Content="OK"/>
</StackPanel>
</StackPanel>
</Window>
The Style defined above doesn't change the layout of the window at all unless I specify a key and set the style on each individual object, which is exactly what I'm trying to avoid. It also works for more specific types (setting the TargetType to Button, for example.)
Any ideas why this isn't working?
Every control when it gets instantiated it gets its Style from the explicitly defined resource or look for the immediate parent where it can get a default style. In your case the Button control will get its default Style from the platform because your App haven't defined one. Now that platform Button Style has no way to know about your custom defined Control base style. Because styles will look for a base style only when you explicitly define BasedOn
So you got only two ways
1. Define Style for every control - which you don't want I think.
2. Define Styles for the controls you are interested and set the BasedOn
<Style TargetType="{x:Type Control}">
<Setter Property="Margin" Value="20"/>
</Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Control}}">
</Style>
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.