WPF Usercontrol with textboxes - wpf

I have a WPF user control with a number of textboxes, this is hosted on a WPF window.
The textboxes are not currently bound but I cannot type into any of them.
I have put a breakpoint in the KeyDown event of one of the textboxes and it hits it fine and I can see the key I pressed.
The textboxes are declared as
<TextBox Grid.Row="3"
Grid.Column="4"
x:Name="PostcodeSearch"
Style="{StaticResource SearchTextBox}"
KeyDown="PostcodeSearch_KeyDown"/>
The style is implemented as
<Style x:Key="SearchTextBox"
TargetType="{x:Type TextBox}">
<Setter Property="Control.Margin" Value="2"/>
<Setter Property="Height" Value="20"/>
<Setter Property="Width" Value="140"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
</Style>
I am hoping I have overlooked something obvious.
EDIT: I only added the KeyDown and KeyUp events just to prove that the keys presses were getting through. I do not have any custom functionality.

If your PostcodeSearch_KeyDown-method (or anybody else before the textbox in the event-chain, could also be some parent-control in the Preview_KeyDown-Event) sets e.Handled = true (e being the eventArgs), the event will not be propagated to further consumers (as the textbox) and thus no action will occur.
Another reason might be that your WPF-Window is hosted in a WinForms-Application, then you will need to call
ElementHost.EnableModelessKeyboardInterop(YourWindow);
To make keyboard interaction work (google/bing for WPF WinForms InterOp for a full explanation)

Related

Adding an ErrorTemplate to a WPF User Control disables TextBox input

I have a very basic custom control consisting of a Label and a Textbox. I've used my control for sometime without any issues.
I've now come to styling my application and have my style inside a XAML file containing just a ResourceDictionary. I have the following for my UserControl:
<Style TargetType="local:LabelEdit">
<Setter Property="Background" Value="{StaticResource BackgroundBrush}" />
<Setter Property="Foreground" Value="{StaticResource ForegroundBrush}" />
<Setter Property="BorderBrush" Value="{StaticResource BorderBrush}" />
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder />
<Image Source="/AJSoft.Controls;component/Resources/Icons/cross.ico" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="Foreground" Value="{StaticResource ErrorForegroundBrush}" />
<Setter Property="Background" Value="{StaticResource ErrorBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{StaticResource ErrorBorderBrush}" />
</Trigger>
</Style.Triggers>
</Style>
Everything works absolutely fine if I comment out the Setter for Validation.ErrorTemplate. If the ErrorTemplate is left intact, the cross shows (I haven't sorted out placement yet, but that can come later...), but the Textbox component of my UserControl does not show the caret or accept keyboard input. The rest of the controls in my Window work as expected.
Here are some screenies where I've deliberately put in some erroneous text to show how it looks.
The same problem happens even if I change that huge image to be a textblock with a small red "!" - the image is just for effect for now.
What am I doing that's causing the problem? I'm new to Validation in WPF...
EDIT: The image shown (big red cross) is just one example of what I've done. Even if I use a small image shown alongside the UserControl, I still get the same effect.
If you were to look at how error templates usually work, you'd see they apply to a single control.
Part of the issue you have here is you've got a label and textbox in one parent user control.
If you then apply an error template at the usercontrol level, it's on everything in that. Label, textbox, everything in your usercontrol.
The next thing to consider is how your error template ends up visible on top of everything. This happens because your error template generates UI in the adorner layer. That's top of everything ( in the window ).
Add these together and you got a big image on top of the content of your usercontrol.
At risk of over simplifying:
You put a top on your box and you can't now get at what's in that box.
There are several ways you could "fix" this but they all involve some design change or compromise.
Maybe a big X on top of your input control isn't a good idea.
You could kind of make it work.
You could make your image IsHitTestVisible="False".
It'll be visually in the way but you can then likely click on the textbox and type.
Just maybe not see everything.
Probably not ideal.
You could show your cross next to the textbox using a datatrigger rather than error template.
Add an image to your usercontrol so you have label, textbox, CrossImage.
Add a style to that with a setter makes it visible collapsed by default.
Use a trigger in that style to show the CrossImage when the control has errors.
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter ... />
</Trigger>
</Style.Triggers>
You may well find it simplest to use the tag on the usercontrol and set that to visible/collapsed. Bind visibility of the image to that.

WPF ScrollViewer breaks: ''{DependencyProperty.UnsetValue}' is not a valid value

I'm getting a visual studio exception at run time and I have no idea where it's coming from!
I am creating a recipe book and meal planner desktop app in WPF. I'm currently working on the interface that allows users to input ingredients into a recipe.
I have a user control for each of the individual Ingredients, which I dynamically add to a stack panel in a Recipe user control. The stack panel is wrapped in a ScrollViewer using the code below:
<ScrollViewer Grid.Row="3" VerticalScrollBarVisibility="Auto" Margin="20,10,0,0">
<StackPanel Name="IngredientsPanel">
<!--IngredientUserControl objects are dynamically added here-->
</StackPanel>
</ScrollViewer>
Adding ingredients works until the ingredients fill the visible space in the stack panel. At that point, instead of a scrollbar appearing, I get the following exception:
System.Windows.Markup.XamlParseException: ''{DependencyProperty.UnsetValue}' is not a valid value for
property 'Foreground'.'
This exception was originally thrown at this call stack:
[External Code]
I suspect this is an issue with some of the styles I've defined to format my text boxes and text blocks within the IngredientUserControl. I keep these styles in a Fonts.xaml resource dictionary which is included in my App.xaml file. My font styles all look roughly like this:
<!--Body1 text (used for static large body text)-->
<Style x:Key = "Body1Text" TargetType = "TextBlock">
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="TextAlignment" Value="Left"/>
<Setter Property="FontSize" Value="16"/>
<Setter Property="Foreground" Value="{DynamicResource PrimaryBrush}"/>
<Setter Property="FontFamily" Value="Comic Sans MS"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Margin" Value="5"/>
</Style>
PrimaryBrush is a SolidColorBrush which I define differently in the resources section of all my user controls (I do this because the color theming varies across different components of my app, but the font styles do not). For example, inside my IngredientUserControl I have these resources:
<UserControl.Resources>
<SolidColorBrush x:Key="PrimaryBrush" Color="{StaticResource OrangeTheme}"></SolidColorBrush>
<SolidColorBrush x:Key="AccentBrush" Color="{StaticResource LightTealTheme}"></SolidColorBrush>
</UserControl.Resources>
And this text block:
<TextBlock Name="Quantity" Margin="0,20,0,5"
Text="{Binding Path=Quantity, Mode=OneWay}"
Style="{StaticResource Body1Text}">
</TextBlock>
All of the styles work and display correctly until the Ingredients Panel runs out of space! When the ScrollViewer gets full, it breaks and can no longer find some foreground property somewhere. I don't understand why I'm only having this issue when the ScrollViewer needs to scroll. Any ideas?
Turns out it was an issue with the style on the ScrollViewer. I had not even attempted to style the ScrollViewer, but whatever default style it was trying to apply was missing a resource. Very weird. I built my own style for the ScrollViewer and now it's working correctly.

Enable a disabled textbox in wpf

Is it possible to have a TextBox that is disabled by default, but becomes enabled when a user double-clicks it?
you can place your TextBox inside StackPanel like this:
<StackPanel MouseLeftButtonDown="StackPanel_MouseDown">
<TextBox Name="textBox1"/>
</StackPanel>
Then in StackPanel event handler:
private void StackPanel_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount >= 2)
{
textBox1.IsEnabled = true; //only hit here on DoubleClick
}
}
you can also simulate StackPanel DoubleClick as described on this question:
WPF StackPanel with Click AND DoubleClick
That's very unusual, also when a control is disabled it is not expected to get input. Users seeing a disabled control normally would not even try to click/double click on it.
Maybe you can add a check box to enable it (or the function belonging to it), or show a message box when double clicking it when it is not allowed/meant to. In this case you also can clearly add a reason why it cannot be double clicked.
What I have seen before is a checkbox without text right before the control. When you click the checkbox it enables the control (text box in your case) after it. You can even use a tooltip for the check box to provide help information what the checkbox is doing.
I would try attaching to the PreviewMouseDown event and enable/disable there.
Otherwise you will have to do the old VB6 trick of having a transparent control above the textbox to receive the click event.
This question is old, but maybe I can help someone that finds a solution for this.
In a recent project I've needed to simulate two states: view and edit. I've do this using a textbox. In view state, value is displayed but you cannot got focus clicking on the control. To enable editing mode you need to double click the control. To avoid the control getting focus by clicking on it and also the disadvantages of disable it, I've used two preview events for control the behaviour of the textbox and adapt it's responses to application needs and state. One of the events is PreviewMouseDown:
private void tbxVariable_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if(!tbxVariable.IsFocused && e.ChangedButton == MouseButton.Left)
e.Handled = true;
}
In this event we will block the mouse down button if our textbox isn't focused yet. This prevents textbox to get focus. So it will behave like a label. When the control is focused, this event isn't blocked and is propagated towards the control. Note that maybe you need to change the cursor because editing cursor is used when the mouse is over the control. Note also, that we are blocking only the left button.
The second event looks like:
private void tbxVariable_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left && !tbxVariable.IsFocused)
tbxVariable.Focus();
}
In the second event we will bring the focus to the control on double left click if it isn't focused yet. If control has focus so we will let the event propagate and the control will behave normally.
In my case I've created a special style for the textbox leaving borders, backgrounds, and all style behaviours. This is the XAML code:
<Style x:Key="InlineEditorTextBox" TargetType="{x:Type TextBox}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="KeyboardNavigation.TabNavigation" Value="None" />
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="MinWidth" Value="5" />
<Setter Property="MaxHeight" Value="16" />
<Setter Property="AllowDrop" Value="false" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBoxBase}">
<ScrollViewer Margin="0" x:Name="PART_ContentHost" VerticalScrollBarVisibility="Disabled" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

ContentControl change ContentTemplate on GotFocus

I have a UserControl which contains a ContentControl. When the user clicks this ContentControl I want to change its ContentTemplate, to make it "editable" (instead of labels display textboxes for example).
What I have is this:
<StackPanel>
<ContentControl Style="{DynamicResource ContainerStyleEditable}" GotFocus="ContentControl_GotFocus"></ContentControl>
</StackPanel>
and in userControl resources i have
<Style TargetType="{x:Type ContentControl}" x:Key="ContainerStyleEditable">
<Setter Property="ContentTemplate" Value="{DynamicResource ItemTemplateReadOnly}" />
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="ContentTemplate" Value="{DynamicResource ItemTemplateEditable}" />
</Trigger>
</Style.Triggers>
</Style>
This doe not work, it seems the GotFocus event never fires. What is the way to to this?
I usually base my triggers of IsKeyboardFocusWithin instead of IsFocused because often the focused element usually isn't the actual ContentControl, but rather a control inside it's Content.
Also, be sure that at least one control inside the ContentControl can accept focus so the control can get focus. If nothing inside the control can accept focus, your trigger will never fire.

WPF LinkLabel implementation strategy

I am attempting to create a LinkLabel control for WPF. Essentially, I'm going to create the LinkLabel from a TextBlock and handle MouseEnter, MouseLeave, and MouseLeftButtonUp events. In the back end I have a base class that has properties that you would expect to see with a LinkLabel. With most other clickable controls in WPF, there is a default MouseEnter animation where the control becomes Ice Blue. I would like to duplicate this behavior when the mouse cursor enters over the TextBlock. I'm not sure if I'm needing to derive from ButtonBase or something along those lines. I have a I am able to change the cursor to a hand, and handle the event for when the "LinkLabel" is clicked. If accessing this seemingly default color changing animations, then I just may have to resort to a simple foreground color swap without the smooth transition. If anyone has created a custom WPF LinkLabel before or has any advice into the matter your input would be much appreciated.
You can create the equivalent of WinForms' LinkLabel right now using a combination of TextBlock and HyperLink:
<TextBlock>Here is a <Hyperlink NavigateUri="http://example.com">link</Hyperlink></TextBlock>
You won't get the "ice blue" mouse-over effect, but you will get the hand cursor. I'm sure you can introduce your mouse-over effects using a simple style trigger.
The "NavigateUri" property works in navigation-style applications where the hyperlink is inside a Frame control. In a standard WPF application you'll want to handle the Hyperlink's Click event instead and use Process.Start to kick off a web browser with the correct URL.
I just created a style for a button and apply a style to a button whenever you want LinkLabel look. Click event of button is used to perform a function when the text is clicked.
<Style x:Key="LinkLabelButtonStyle" TargetType="Button">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="CornflowerBlue"></Setter>
</Trigger>
</Style.Triggers>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="Transparent">
<ContentPresenter/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock TextDecorations="Underline" Text="{TemplateBinding Content}"></TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="DarkBlue"></Setter>
<Setter Property="MinWidth" Value="90"></Setter>
<Setter Property="HorizontalAlignment" Value="Left"></Setter>
<Setter Property="Padding" Value="5"></Setter>
<Setter Property="Margin" Value="5"></Setter>
<Setter Property="Cursor" Value="Hand"></Setter>
</Style>
You can place above style in the Window.Resources so you can use it in entire window.
Then apply the style to a button whenever you want LinkLabel look.
<Button Name="LinkLabelLookALikeButton" Content="Text goes here" Style="{StaticResource LinkLabelButtonStyle}" Click="Event_Goes_Here">
</Button>
Hope this helps!

Resources