How to make Textbox style into a reusable Control Template? - c#

I want my textbox to have a watermark style.
I have the code below which I got here https://stackoverflow.com/a/21672408/9928363
<Grid>
<TextBox Width="250" VerticalAlignment="Center" HorizontalAlignment="Left" x:Name="SearchTermTextBox" Margin="5"/>
<TextBlock IsHitTestVisible="False" Text="Enter Search Term Here" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="10,0,0,0" Foreground="DarkGray">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Text, ElementName=SearchTermTextBox}" Value="">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
I have lots of Textbox and I only want some of it to use the style but not all.
How will I do that?

There are a number of ways to add a watermark to a textbox, including replacing the ControlTemplate of your TextBox (as your question implies). However, replacing the ControlTemplate of the TextBox may not be ideal because in so doing you become responsible for drawing the entire control, including the border, styling different states etc. This is not too difficult (you can use Visual Studio or Espression Blend to copy the template from your current theme), but unless you do a lot of work you will lose the WPF feature of adapting the styling of common controls to the current Windows theme.
If you want a simple, re-usable, pure XAML approach that doesn't require changing the control template, then declaring a style resource using a VisualBrush is one effective approach.
See below, where we have 3 text boxes, the watermark style is applied to two of them. This style goes a little further than your example by removing the watermark when the textbox has the input focus.
<Window ...>
<Window.Resources>
<Style TargetType="TextBox" x:Key="Watermark">
<Style.Resources>
<VisualBrush x:Key="WatermarkBrush" AlignmentX="Left" AlignmentY="Center" Stretch="None">
<VisualBrush.Visual>
<Label Content="Enter Search Term Here" Foreground="LightGray" />
</VisualBrush.Visual>
</VisualBrush>
</Style.Resources>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Text}" Value="">
<Setter Property="Background" Value="{StaticResource WatermarkBrush}" />
</DataTrigger>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter Property="Background" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel>
<TextBox Width="250" VerticalAlignment="Center" FontSize="20"
HorizontalAlignment="Left" Style="{StaticResource Watermark}"/>
<TextBox Width="250" VerticalAlignment="Center"
HorizontalAlignment="Left"/>
<TextBox Width="250" VerticalAlignment="Center"
HorizontalAlignment="Left" Style="{StaticResource Watermark}"/>
</StackPanel>
</Window>
If you need the watermark to react to different font sizes, you could make use of the Stretch property:-
<VisualBrush x:Key="WatermarkBrush" AlignmentX="Left" AlignmentY="Center" Stretch="Uniform">
<VisualBrush.Visual>
<Label Padding="2 1" Content="Enter Search Term Here" Foreground="LightGray" />
</VisualBrush.Visual>
</VisualBrush>

As stated in an earlier answer, sometimes it is preferable not to replace the ControlTemplate of the standard controls. So here's a method to create a watermark via an attached property, that keeps the existing control template intact. This has the advantage that you can set the watermark to any text, including setting it at runtime via data binding.
For demonstration the XAML will create 3 textboxes (see below). The bottom textbox has a watermark whose Text property is bound to the Text property of the top textbox, which demonstrates the dynamic nature of the watermark text. Typing a number into the second textbox will change the font size of the Text (and watermark) of the bottom textbox.
The above image is taken from Windows 10 standard theme. Here is is again after setting the Windows theme to High Contrast. As you can see the existing ControlTemplate (and color scheme) has been respected.
XAML
<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:behaviors="clr-namespace:WpfApp2.Behaviors"
mc:Ignorable="d"
Title="MainWindow" Height="250" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="Watermark" Grid.Row="0"/>
<TextBox Grid.Row="0" Grid.Column="1" Margin="5"
x:Name="watermark" Width="250" HorizontalAlignment="Left" Text="enter search term here"/>
<Label Content="Font Size" Grid.Row="1"/>
<TextBox Grid.Row="1" Grid.Column="1" Margin="5"
x:Name="fontSize" Width="250" VerticalAlignment="Center" HorizontalAlignment="Left"
behaviors:TextBoxExtensions.Watermark="Enter a font size"/>
<Label Content="Result" Grid.Row="2"/>
<TextBox Grid.Row="2" Grid.Column="2" Margin="5"
Width="200" VerticalAlignment="Center" HorizontalAlignment="Left"
FontSize="{Binding Text, ElementName=fontSize}"
behaviors:TextBoxExtensions.Watermark="{Binding Path=Text, ElementName=watermark}" />
</Grid>
</Window>
TextBoxExtensions.cs
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace WpfApp2.Behaviors
{
public class TextBoxExtensions
{
public static readonly DependencyProperty WatermarkProperty = DependencyProperty.RegisterAttached(
"Watermark",
typeof(string),
typeof(TextBoxExtensions),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, OnWatermarkTextChanged)
);
private static void OnWatermarkTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var tb = d as TextBox;
if (tb != null)
{
var textChangedHandler = new TextChangedEventHandler((s, ea) => ShowOrHideWatermark(s as TextBox));
var focusChangedHandler = new DependencyPropertyChangedEventHandler((s, ea) => ShowOrHideWatermark(s as TextBox));
var sizeChangedHandler = new SizeChangedEventHandler((s, ea) => ShowOrHideWatermark(s as TextBox));
if (string.IsNullOrEmpty(e.OldValue as string))
{
tb.TextChanged += textChangedHandler;
tb.IsKeyboardFocusedChanged += focusChangedHandler;
// We need SizeChanged events because the Background brush is sized according to the control size
tb.SizeChanged += sizeChangedHandler;
}
if (string.IsNullOrEmpty(e.NewValue as string))
{
tb.TextChanged -= textChangedHandler;
tb.IsKeyboardFocusedChanged -= focusChangedHandler;
tb.SizeChanged -= sizeChangedHandler;
}
ShowOrHideWatermark(tb);
}
}
public static string GetWatermark(DependencyObject element)
{
return (string)element.GetValue(WatermarkProperty);
}
public static void SetWatermark(DependencyObject element, string value)
{
element.SetValue(WatermarkProperty, value);
}
private static void ShowOrHideWatermark(TextBox tb)
{
// Restore TextBox background to style/theme value
tb.ClearValue(TextBox.BackgroundProperty);
if (string.IsNullOrEmpty(tb.Text) && !tb.IsKeyboardFocused)
{
var wm = GetWatermark(tb);
if (!string.IsNullOrEmpty(wm))
{
tb.Background = CreateTextBrush(wm, tb);
}
}
}
private static Brush CreateTextBrush(string text, TextBox tb)
{
Grid g = new Grid
{
Background = tb.Background,
Width = tb.ActualWidth,
Height = tb.ActualHeight
};
g.Children.Add(new Label
{
Padding = new Thickness(2,1,1,1),
FontSize = tb.FontSize,
FontFamily = tb.FontFamily,
Foreground = Brushes.LightGray,
Content = text
});
VisualBrush vb = new VisualBrush
{
Visual = g,
Stretch = Stretch.None,
AlignmentX = AlignmentX.Left,
AlignmentY = AlignmentY.Center,
};
return vb;
}
}
}
None of the watermark mechanisms is perfect for every scenario. This particular solution has the benefit of preserving the default theme, will use the font size and family of the TextBox, and will respect the background color set on the TextBox control via a style or theme. However it will not work correctly if you attempt to set the Background property of the TextBox directly in XAML as this value will be wiped out by the watermark.

Related

Using in code-behind a custom `Style`, declared in xaml, throws run-time error

I declare a style in xaml that I need to use and apply to a user control in code behind and when I use the same style twice the following error throws:
Element already has a logical parent. It must be detached from the old
parent before it is attached to a new one.
What am I doing wrong? I need to create multiple controls of the same user-control-type in code behind and apply one and the same Style to it.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dxdo="http://schemas.devexpress.com/winfx/2008/xaml/docking"
xmlns:s="http://schemas.abtsoftware.co.uk/scichart"
x:Class="MyChartControl.MainWindow"
Title="MainWindow" Height="655" Width="1020">
<Window.Resources>
<Style x:Key="SciChartSurfaceStyle" TargetType="{x:Type s:SciChartSurface}">
<Setter Property="XAxis">
<Setter.Value>
<s:DateTimeAxis Visibility="Visible"
TextFormatting="dd/MM/yyyy"
SubDayTextFormatting="dd/MM/yyyy HH:mm:ss.fff"
GrowBy="0.02, 0.02"/>
</Setter.Value>
</Setter>
<Setter Property="YAxis">
<Setter.Value>
<s:NumericAxis AxisAlignment="Right"
Visibility="Visible"
TextFormatting="{Binding YAxisFormatting}"
GrowBy="0.02, 0.02"
AutoRange="Always"/>
</Setter.Value>
</Setter>
<Setter Property="ChartModifier">
<Setter.Value>
<s:ModifierGroup>
<s:RubberBandXyZoomModifier IsAnimated = "False" IsXAxisOnly = "True" ExecuteOn = "MouseRightButton"/>
<s:ZoomPanModifier XyDirection="XYDirection" ClipModeX = "ClipAtExtents" ExecuteOn ="MouseLeftButton" />
<s:MouseWheelZoomModifier XyDirection = "XYDirection"/>
<s:ZoomExtentsModifier IsAnimated = "False" ExecuteOn = "MouseDoubleClick" />
<s:XAxisDragModifier DragMode = "Scale"/>
<s:CursorModifier SourceMode="AllSeries" UseInterpolation="True"/>
<s:LegendModifier ShowLegend="True" LegendPlacement ="Inside" GetLegendDataFor="AllSeries" Margin="10"/>
<!--<s:SeriesSelectionModifier ReceiveHandledEvents="True">
<s:SeriesSelectionModifier.SelectedSeriesStyle>
<Style TargetType="s:BaseRenderableSeries">
<Setter Property="SeriesColor" Value="White"/>
<Setter Property="PointMarkerTemplate">
<Setter.Value>
<ControlTemplate>
<s:EllipsePointMarker Fill="#FF00DC" Stroke="White" Width="7" Height="7"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</s:SeriesSelectionModifier.SelectedSeriesStyle>
</s:SeriesSelectionModifier>-->
</s:ModifierGroup>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="32" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal" Background="Black">
<TextBlock Text="Dataseries Type:" Margin="5,0" VerticalAlignment="Center" FontSize="12" Foreground="White"/>
<ComboBox x:Name="ComboBox_ChooseSeriesType" MinWidth="140" Margin="5,3" VerticalContentAlignment="Center"/>
<TextBlock Text="Theme:" Margin="5,0" VerticalAlignment="Center" FontSize="12" Foreground="White"/>
<ComboBox x:Name="ComboBox_ChooseTheme" MinWidth="140" Margin="5,3" VerticalContentAlignment="Center"/>
</StackPanel>
<dxdo:LayoutGroup Grid.Row="1" x:Name="LayoutGroup" Orientation="Vertical">
<!--<dxdo:LayoutPanel Name="Panel1">
<s:SciChartSurface Name="Surface1" Style="{StaticResource SciChartSurfaceStyle}"></s:SciChartSurface>
</dxdo:LayoutPanel>-->
</dxdo:LayoutGroup>
</Grid>
And the code-behind method that retrieves the style and applies it:
private void TestSomeStuff()
{
var style = this.TryFindResource("SciChartSurfaceStyle") as Style;
var sciChartSurface1 = new SciChartSurface() {Style = style};
var panel1 = new LayoutPanel(){Content=sciChartSurface1};
var style2 = this.TryFindResource("SciChartSurfaceStyle") as Style;
var sciChartSurface2 = new SciChartSurface() {Style = style2};
var panel2 = new LayoutPanel() {Content = sciChartSurface2};
LayoutGroup.Add(panel1);
LayoutGroup.Add(panel2);
}
EDIT
Adding panel1 to LayoutGroup works just fine but the run-time error occurs as soon as I attempt to add panel2. Also, as long as do not inject style into a new instance of SciChartSurface it works just fine. The error pops up as soon as I inject the style into both new surfaces.
Do not set Style in code behind directly:
var style = this.TryFindResource("SciChartSurfaceStyle") as Style;
var sciChartSurface1 = new SciChartSurface() {Style = style};
but with SetValue method:
var style = this.TryFindResource("SciChartSurfaceStyle") as Style;
var sciChartSurface1 = new SciChartSurface();
sciChartSurface1.SetValue(StyleProperty, style);

Show a custom tooltip for a group of rectangles (points on a graph)

I have a Canvas on which I have drawn a number of Rectangles which represent a number of user selected positions on the canvas.
I want to create a ToolTip for each of the rectangles that shows the x and y coords of the rectangle and also the distance to another point: the "stylus point".
The x and y coords are known when a rectangle is created of course but the distance to the stylus point is not therefore the tooltip needs to update its text each time it is shown.
I've tried using a binding as below but this just puts the text "System.Windows.Control.ToolTip" in the tool tip.
...
Rectangle rectangle = new Rectangle
{
Width = _rectWidth,
Height = _rectWidth,
Fill = new SolidColorBrush(Colors.Red)
};
rectangle.ToolTip = new ToolTip();
Binding binding = new Binding()
{
Source = this,
Path = new PropertyPath("ToolTipBinding"),
Mode = BindingMode.OneWay,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
};
BindingOperations.SetBinding( rectangle.ToolTip as ToolTip ,ToolTipService.ToolTipProperty, binding);
}
public string ToolTipBinding
{
get
{
return "How would i get the data context here (even if it bound correctly)";
}
}
Any help greatly appreciated.
This is the solution I've come up with. this will show a list of points as squares on a canvas.TargetListbelow is a list of Target objects that contain the required data for each point. Hope this helps someone.
Resource created for the ToolTips:
<UserControl.Resources>
<Style TargetType="{x:Type ToolTip}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToolTip}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBox Grid.Row="0" Grid.Column="0" Text="X:" />
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Path=PositionX}" />
<TextBox Grid.Row="1" Grid.Column="0" Text="Z:" />
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Path=PositionZ}" />
<TextBox Grid.Row="2" Grid.Column="0" Text="ΔX:" />
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Path=DeltaX}" />
<TextBox Grid.Row="3" Grid.Column="0" Text="ΔZ:" />
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding Path=DeltaZ}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<UserControl.Resources>
Items control
<ItemsControl Grid.Row="0" Grid.Column="0" ClipToBounds="True" ItemsSource="{Binding TargetList}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Path=PositionScreen.X}" />
<Setter Property="Canvas.Top" Value="{Binding Path=PositionScreen.Z}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle Width="{Binding RectangleSize}"
Height="{Binding RectangleSize}"
Fill="{Binding RectangleBrush}" >
<Rectangle.ToolTip>
<ToolTip />
</Rectangle.ToolTip>
</Rectangle>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Target class, showing the property names only. These are updated as required.
public class Target : ObservableObject
{
public Point3D PositionX...
public Point3D PositionZ...
public double DeltaX...
public double DeltaZ...
public Point3D PositionScreen...
public double RectangleSize...
public Brush RectangleBrush...
}

How to Trigger Click Event in WPF User control (Textbox with Button)?

I have Created one User control for WPF Like Windows 8 Password box Model.
My UserControl XAML Code -
<Grid.Resources>
<Style x:Key="ButtonWithoutHover" TargetType="Button">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Name="border"
BorderThickness="0"
Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="BorderBrush" Value="Black" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<Border BorderBrush="Black" BorderThickness="2" >
<DockPanel Canvas.Right="2" Canvas.Top="2">
<Button Style="{StaticResource ButtonWithoutHover}" Height="25" Width="20" BorderThickness="3" BorderBrush="White" DockPanel.Dock="Right" Background="CadetBlue" >
<Button.Content>
<Label Content="->" Foreground="White" />
</Button.Content>
</Button>
<TextBox Height="30" Width="180" FontSize="18" BorderThickness="0" Name="txtNumber" DockPanel.Dock="Left" >
</TextBox>
</DockPanel>
</Border>
Edit 1:
I have include this Usercontrol in My project. But at the time of Implementing UserControl in Wpf Application There is No Click Event for my UserControl. So I add Click="UserControl1_Click" to Xaml. But It through an Error
The property 'Click' does not exist in XML namespace
'clr-namespace:NumericTextbox;assembly=NumericTextbox'.
My Application Xaml Code-
<Window x:Class="NumericAppication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:NumText="clr-namespace:NumericTextbox;assembly=NumericTextbox"
Title="MainWindow" Height="350" Width="525">
<Grid>
<NumText:UserControl1 Width="120" Height="30" Click="UserControl1_Click" />
</Grid>
</Window>
You could have a Dependency Property on the UserControl that you bind to the Click event of the button.
EDIT:
In your UserControl you can have
public EventHandler MyProperty
{
get { return (EventHandler)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register("MyProperty", typeof(EventHandler), typeof(ownerclass));
then in the XAML you have:
<button Click="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=MyProperty}" />
Although, I think that it is best practice to use ICommand instead of the event handler but the principle is the same.
may be this will help you
<Button Style="{StaticResource ButtonWithoutHover}" Height="25"
Width="20" BorderThickness="3" BorderBrush="White"
DockPanel.Dock="Right" Background="CadetBlue" Click="Button_Click">
<Button.Content>
<Label Content="->" Foreground="White" />
</Button.Content>
</Button>
You can create a command an make a command binding like this:
Create RoutedUiCommand command:
Class Command
{
Static RoutedUICommand ButtonClick = New RoutedUICommand("Buttonclick", "ButtonClick", TypeOf(Window))
}
Then in xaml
<Window.CommandBindings>
<CommandBinding Command="local:Command.ButtonClick" Executed="CommandBinding_ButtonClick" />
</Window.CommandBindings>
inside the method CommandBinding_ButtonClick you make the logic.
and finally add the command to the button:
<Button Style="{StaticResource ButtonWithoutHover}" Height="25" Width="20" BorderThickness="3" BorderBrush="White" DockPanel.Dock="Right" Background="CadetBlue" Command=local:Command.ButtonClick>
<Button.Content>
<Label Content="->" Foreground="White" />
</Button.Content>
</Button>
When you design your user control have you tried this on your button?
(remember to choose that edit template thing)
https://i-msdn.sec.s-msft.com/dynimg/IC616903.png
p.s. when I design user controls I always prefer to use blend.
As we have created the UserControl1.then after adding the Click event the UserControl1.Desginer.cs file will look like this.
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(180, 38);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.TabIndex = 0;
this.button1.Text = "button1";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// UserControl1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.button1);
this.Name = "UserControl1";
this.Size = new System.Drawing.Size(286, 95);
this.ResumeLayout(false);
}
and in the UserControl1.cs file will have the body of the Button click event
private void button1_Click(object sender, EventArgs e)
{
}
this may help you.Plz let me know

WPF ElementHost in WinForms not receiving mouse clicks

I have a winforms application that I'd like to add a WPF user control to. The issue is when I run the program the WPF user control does not respond to mouse events. I can tab through the control and use the keyboard just fine, but the control does not respond to mouse clicks (the buttons don't even recognize when you hover over them). I have attempted the solution in this SO question but it did not help: WPF WinForms Interop issue with Enable / Disable . Please see the code below.
User Control XAML
<UserControl x:Class="WinformsWPF.ucNotes"
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="400" d:DesignWidth="460" Background="#FFD7DFEC" IsHitTestVisible="False" Loaded="Window_Loaded">
<UserControl.Resources>
<LinearGradientBrush x:Key="DataGridHeaderBackgroundBrush" StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#b3dbcc" Offset="0" />
<GradientStop Color="#61a99b" Offset="1" />
</LinearGradientBrush>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<Label Content="Notes" HorizontalAlignment="Left" Margin="8,8,0,0" VerticalAlignment="Top" FontSize="16" FontWeight="Bold"/>
<DataGrid x:Name="dgNotes" Margin="8,50,8,0" VerticalAlignment="Top" Height="121.28" d:LayoutOverrides="HorizontalAlignment" Background="#FFE4EEF3" AutoGenerateColumns="False" HorizontalGridLinesBrush="{x:Null}" VerticalGridLinesBrush="{x:Null}" ColumnWidth="Auto" CanUserDeleteRows="False" CanUserAddRows="False" CanUserReorderColumns="False" CanUserResizeRows="False" IsReadOnly="True" SelectionMode="Single" SelectionChanged="dgNotes_SelectionChanged" RowHeaderWidth="0" BorderBrush="{x:Null}" SelectedIndex="0">
<DataGrid.Columns>
<DataGridTextColumn x:Name="nDate" Header="Date"/>
<DataGridTextColumn x:Name="nEmployee" Header="Employee"/>
<DataGridTextColumn x:Name="nText" Header="Note" Width="*"/>
</DataGrid.Columns>
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Padding" Value="6" />
<Setter Property="BorderBrush" Value="#489283" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Background" Value="{StaticResource DataGridHeaderBackgroundBrush}" />
</Style>
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="Background" Value="Transparent" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#66D7DFEC" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#DDD7DFEC" />
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Margin" Value="3" />
<Setter Property="Padding" Value="3" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="Black" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
</DataGrid>
<Grid Margin="8,175.28,8,8" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto" MinHeight="25.96"/>
<RowDefinition Height="Auto" MinHeight="25.96"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="61.757"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="Date" d:LayoutOverrides="Height" HorizontalAlignment="Left"/>
<Label Content="Employee" Grid.Row="1" d:LayoutOverrides="Width"/>
<Label Content="Notes" Grid.Row="2" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Label x:Name="lblDate" Content="lblDate" Grid.Column="1" HorizontalAlignment="Left" d:LayoutOverrides="Height"/>
<Label Content="lblEmployee" Grid.Column="1" HorizontalAlignment="Left" Grid.Row="1" d:LayoutOverrides="Height" Name="lblEmployee" />
<TextBox x:Name="txtNotes" Grid.Column="1" Grid.Row="2" TextWrapping="Wrap" d:LayoutOverrides="Width" LostFocus="txtNotes_LostFocus" />
</Grid>
<Button x:Name="btnDelete" HorizontalAlignment="Right" Margin="0,8,8,0" VerticalAlignment="Top">
</Button>
<Button x:Name="btnAdd" HorizontalAlignment="Right" Margin="0,8,50,0" VerticalAlignment="Top" Click="btnAdd_Copy_Click">
</Button>
</Grid>
</UserControl>
User Control CS
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
namespace WinformsWPF
{
/// <summary>
/// Interaction logic for ucNotes.xaml
/// </summary>
public partial class ucNotes : UserControl
{
List<noteItem> notes;
List<string> noteColumns;
public ucNotes()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
notes = new List<noteItem>
{
new noteItem{nID = 0, nDate = Convert.ToDateTime("9/5/2012 2:48 PM"), nEmployee = "Gardner, John", nText = "This is a test note"},
new noteItem{nID = 1, nDate = Convert.ToDateTime("9/5/2012 2:51 PM"), nEmployee = "Gardner, John", nText = "This is another test note. This test note is very long. It should cause overlap."}
};
noteColumns = new List<string> { "nDate", "nEmployee", "nText" };
dgNotes.GenerateColumns(noteColumns);
dgNotes.ItemsSource = notes;
}
private void dgNotes_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
noteItem noteInfo = (noteItem)dgNotes.SelectedItem;
lblDate.Content = noteInfo.nDate;
lblEmployee.Content = noteInfo.nEmployee;
txtNotes.Text = noteInfo.nText;
}
private void txtNotes_LostFocus(object sender, RoutedEventArgs e)
{
((noteItem)dgNotes.SelectedItem).nText = txtNotes.Text;
dgNotes.Items.Refresh();
}
private void btnAdd_Copy_Click(object sender, RoutedEventArgs e)
{
notes.Add(new noteItem { nDate = DateTime.Now, nEmployee = "Gardner, John" });
dgNotes.SelectedIndex = dgNotes.Items.Count - 1;
dgNotes.Items.Refresh();
txtNotes.Focus();
}
}
public static class dataGridExtension
{
public static void GenerateColumns(this DataGrid dataGrid, List<string> columns)
{
List<DataGridColumn> oldColumns = dataGrid.Columns.ToList();
dataGrid.Columns.Clear();
int index = 0;
foreach (var column in oldColumns)
{
var newCol = (DataGridTextColumn)column;
newCol.Binding = new Binding(columns[index++]);
dataGrid.Columns.Add(newCol);
}
}
}
public class noteItem
{
public int nID { set; get; }
public DateTime nDate { set; get; }
public string nEmployee { set; get; }
public string nText { set; get; }
}
}
The windows form is empty except for the ElementHost (ie, it's a fresh project with nothing changed). Let me know if you would like that code as well.
Any help would be appreciated.
You inadvertently had IsHitTestVisible="False" set on the UserControl....take that off and it works.
That will stop the control getting mouse messages when hit.
http://msdn.microsoft.com/en-us/library/system.windows.uielement.ishittestvisible.aspx
I have encountered this issue too, however the accepted solution did not work. For posterity I will add what I found out here.
TLDR
Disable XAML Hot Reload in Visual Studio.
Debug -> Options -> Debugging -> Hot Reload -> Uncheck Enable XAML Hot Reload
Problem
I have a WPF UserControl hosted in a WinForms project in an ElementHost control. The WPF UserControl contains a number of labels and textboxes along with two buttons, Save and Cancel.
The problem is fairly similar to OPs: You can navigate the UserControl with the tab key and can fire the Click event on the buttons with the space bar. Hovering over the buttons with the mouse causes the focus color to flash almost imperceptibly and the click event will only be raised if you happen to click at the exact moment the Button has focus.
It looks like something on the UserControl is "stealing" back focus after mouse movement ends, and apparently buttons can only be clicked if they have focus in WPF. That something turns out to be the debugger and Hot Reload.
Another hint that this problem can also be caused by the debugger is that disabling Hot Reload also solved another issue I had: the WinForms components of the project will loose their DPI-awareness and cause all sorts of wonky issues with presentation.
Edit: not caused by Hot Reload. Everything else is as described.
For reference I am using Visual Studio Professional 2019 (Version 16.8.2)

WPF Label to TextBox

what is the best practice to show text label (e.g. "Name") with the TextBox in WPF?
I want a label "Name" above the TextBox and many similar Labels/TextBoxes.
Should I put the pairs Label/TextBox into the vertical StackPanel?
Is there a simpler solution?
Here is a control that does it:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
public class KeyValueControl : Control
{
public static readonly DependencyProperty KeyProperty = DependencyProperty.Register(
"Key",
typeof(string),
typeof(KeyValueControl),
new PropertyMetadata(default(string)));
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
"Value",
typeof(object),
typeof(KeyValueControl),
new FrameworkPropertyMetadata
{
DefaultValue = null,
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
});
static KeyValueControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(KeyValueControl), new FrameworkPropertyMetadata(typeof(KeyValueControl)));
}
public string Key
{
get
{
return (string)GetValue(KeyProperty);
}
set
{
SetValue(KeyProperty, value);
}
}
public object Value
{
get
{
return GetValue(ValueProperty);
}
set
{
SetValue(ValueProperty, value);
}
}
}
Style:
<Style TargetType="{x:Type local:KeyValueControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:KeyValueControl}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Key, RelativeSource={RelativeSource TemplatedParent}}"/>
<TextBox Grid.Column="1" Text="{Binding Value, RelativeSource={RelativeSource TemplatedParent}, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Usage (create a property grid):
<ItemsControl>
<customControls:KeyValueControl Key="First" Value="{Binding Value1}" />
<customControls:KeyValueControl Key="Second" Value="{Binding Value2}" />
<customControls:KeyValueControl Key="Last" Value="{Binding Value3}" />
<customControls:KeyValueControl Key="Bool1" Value="{Binding Bool1}" Style="{StaticResource CheckBoxStyle}"/>
<customControls:KeyValueControl Key="Bool2" Value="{Binding Bool2}" Style="{StaticResource CheckBoxStyle}"/>
</ItemsControl>
It really depends on what you want to do with these controls in the future. If you want to reuse this kind of control multiple times (and maybe create it on the fly), it would be the best to create UserControl and program it. You can then easily reuse it in a very simple manner (like putting in on StackPanel).
Code for LabelTextBox.xaml
<UserControl x:Class="YourProject.LabelTextBox"
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="49" d:DesignWidth="314" MinHeight="49" MaxHeight="49">
<Grid>
<Label Content="Label" Height="28" HorizontalAlignment="Left" Name="BaseLabel" VerticalAlignment="Top" />
<TextBox Height="23" Margin="0,26,0,0" Name="BaseTextBox" VerticalAlignment="Top" />
</Grid>
</UserControl>
Code for LabelTextBox.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace YourProject
{
/// <summary>
/// Interaction logic for LabelTextBox.xaml
/// </summary>
public partial class LabelTextBox : UserControl
{
public LabelTextBox()
{
InitializeComponent();
}
string LocalLabel = "";
string LocalTextBox = "";
public string Label
{
get { return LocalLabel; }
set
{
LocalLabel = value;
BaseLabel.Content = value;
}
}
public string TextBox
{
get { return LocalTextBox; }
set
{
LocalTextBox = value;
BaseTextBox.Text = value;
}
}
}
}
You can change Label text and TextBox content with Label and TextBox property of new control (hidden in "Other" part of Properties in designer. You can also program additional functions for the UserControl.
If you don't need to reuse these controls so much, other solutions will suffice.
If you want the flexibility to manipulate this text label structure I suggest to wrap each TextBox and Label in a dock panel, and set the docking in a style that will apply on all labels and text boxes.
so it'll be like
<StackPanel>
<StackPanel.Resources>
<Style TargetType={x:Type Label}>
<Setter Property="DockPanel.Dock" Value="Top"/>
</Style>
</StackPanel.Resources>
<DockPanel>
<Label></Label>
<TextBox></TextBox>
</DockPanel>
<DockPanel>
<Label></Label>
<TextBox></TextBox>
</DockPanel>
</StackPanel>
I usually do something like this:
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Margin="5">Repository URL:</Label>
<TextBox Grid.Column="1" Margin="5"></TextBox>
</Grid>
</StackPanel>
If you did this often enough, you could create a UserControl or Datatemplate. But WPF is just a verbose markup language...
I use this
<Style TargetType="{x:Type TextBox}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Top" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Grid HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="ControlLabel" Grid.Column="0"
VerticalAlignment="Top" HorizontalAlignment="Left"
Text="{TemplateBinding Tag}" />
<Line x:Name="LineColumnLabel" Grid.Column="1" X1="0" X2="0" Y1="0" Y2="{TemplateBinding Height}"
Stroke="#B31C1C1C" StrokeThickness="0.5" Margin="5,0,5,0" />
<ScrollViewer Grid.Column="2" x:Name="PART_ContentHost"
IsTabStop="{TemplateBinding ScrollViewer.IsTabStop}"
TextElement.Foreground="{TemplateBinding Foreground}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Tag" Value="{x:Null}">
<Setter TargetName="ControlLabel" Property="Visibility" Value="Collapsed" />
<Setter TargetName="ControlLabel" Property="Margin" Value="0" />
<Setter TargetName="LineColumnLabel" Property="Visibility" Value="Collapsed" />
<Setter TargetName="LineColumnLabel" Property="Margin" Value="0" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And
<Grid>
<TextBox Tag="The Name"/>
</Grid>
Create a class X that holds label and text that implements INotifyPropertyChanged. Make an ObservableCollection. This will be the ItemsSource for ListBox, ComboBox, StackPanel.. whatever you choose. Create a DataTemplate that displays X the way you want it to.

Categories

Resources