Custom TextBox not updating property when change - wpf

I have a custom TextBox control where the defaul value is the Tag string. When the TextBox is empty it shows a TextBlock on top with the Tag string with other color and centered. The control works on almost every scenario but I am having issues with one specifically. I have a TexBox that need to update a property when I type so I thought that adding the UpdateSourceTrigger=PropertyChanged will update the peroperty, but it doesn't work when I use the custom style, it works only on default so I thought that the issue is on the custom Style, but I am too new on xaml to figure out. Could someone please enlight me on what I am doing wrong?
<TextBox Grid.Column="2" Margin="5" Style="{StaticResource DefaultTextBox}" Tag="Sesión" Text="{Binding Sesion, UpdateSourceTrigger=PropertyChanged}" Width="100"/>
<!--Directorio TextBox-->
<Style x:Key="DefaultTextBox" TargetType="TextBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border Background="#ffffff" BorderBrush="#ffacacac" BorderThickness="1" CornerRadius="0">
<Grid>
<TextBox x:Name="TextBoxPersonalizado" Background="Transparent" BorderThickness="0" VerticalContentAlignment="Center" Text="{TemplateBinding Text}"/>
<TextBlock Foreground="#828282" HorizontalAlignment="Center" IsHitTestVisible="False" Text="{TemplateBinding Tag}" VerticalAlignment="Center">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding Text, ElementName=TextBoxPersonalizado}" Value="">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
<Setter Property="Visibility" Value="Hidden"/>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

TemplateBinding is for one direction only.
That beeing said, you need to use RelativeSource inside your template.
Text="{Binding Text, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"
Then you can use your customized TextBox like this.
<TextBox
x:Name="InputTextBox"
Grid.Column="0"
Width="100"
Margin="5"
Style="{StaticResource DefaultTextBox}"
Tag="Sesión" />
<TextBox Grid.Column="1" Text="{Binding ElementName=InputTextBox, Path=Text}" />

Related

How to create a WPF ListBoxItem with check mark?

I was looking a while for how to build a list item with a check mark if selected.
Checking different Resources, it seems there are a lot of code solutions, bot no true XAML only one.
So this is what i tried to achive:
Any additions are welcomed.
In the spirit of Answer-your-own (Stackoverflow blog)
So here is what I actually got after hours of figuring it out.
As said, any additions are welcomed.
The check mark is:
U+2714 (10004) ✔ HEAVY CHECK MARK Dingbats (2700–27BF)
The style code:
basically creating a wrapper for any item, setting base properties, and changing them on item selection, made available as StaticResource as checkmarkItem
<Window.Resources>
<Style x:Key="checkmarkItem" TargetType="ListBoxItem">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Name="Border" Background="Transparent" BorderThickness="5" BorderBrush="Transparent" Margin="0,1,0,1">
<Grid>
<TextBlock VerticalAlignment="Top" HorizontalAlignment="Right" Name="Marker" Visibility="Hidden" Background="#0078D7" Padding="5,0,0,5" Foreground="White">✔</TextBlock>
<ContentPresenter />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="ListBoxItem.IsSelected" Value="true">
<Setter TargetName="Marker" Property="Visibility" Value="Visible" />
<Setter TargetName="Border" Property="BorderBrush" Value="#0078D7"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
The implementation:
it is just a regular stackpanel arrangement, all styles are added by referring in ItemContainerStyle="{StaticResource checkmarkItem}"
<Grid>
<Label Grid.Row="0" FontSize="26">Software</Label>
<ListView Grid.Row="1" SelectionMode="Multiple" BorderBrush="Transparent" ItemContainerStyle="{StaticResource checkmarkItem}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="64" AutomationProperties.Name="{Binding Title}">
<Image Source="{Binding Icon}" Height="48" Width="48" VerticalAlignment="Center" Margin="5,0,20,0"/>
<StackPanel Orientation="Vertical" VerticalAlignment="Center">
<TextBlock Text="{Binding Title}" FontSize="16" TextWrapping="Wrap"/>
<TextBlock Text="{Binding Description}" TextWrapping="Wrap" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>

Can't get WPF TextBlock to bind to Property

In my project I have a custom control which is an expander. The button's content that makes the control expand or collapse should change depending on the state. I got most of it to work but I fail at binding text to the content which I use for the button.
Here's my XAML-Code from Generic.xaml:
<ControlTemplate x:Key="PndExpanderControlVertical" TargetType="{x:Type local:PndExpanderControl}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<dx:DXExpander x:Name="expander" Grid.Column="0" FlowDirection="LeftToRight" VerticalExpand="None" HorizontalExpand="FromLeftToRight" IsExpanded="True">
<dxlc:GroupBox x:Name="group_box" Padding="0" Header="Header"/>
</dx:DXExpander>
<Button Grid.Column="1" Padding="1" x:Name="expand_button">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<!-- Button-Style, expanded -->
<DataTrigger Binding="{Binding IsExpanded, ElementName=expander}" Value="True">
<Setter Property="Content" Value="↧ ↧"/>
<Setter Property="LayoutTransform">
<Setter.Value>
<RotateTransform Angle="90"/>
</Setter.Value>
</Setter>
</DataTrigger>
<!-- Button-Style, collapsed -->
<DataTrigger Binding="{Binding IsExpanded, ElementName=expander}" Value="False">
<Setter Property="Content">
<Setter.Value>
<TextBlock>
<TextBlock Text="↥ "/>
<TextBlock Text="{Binding Header, ElementName=group_box}"/>
<TextBlock Text=" ↥"/>
</TextBlock>
</Setter.Value>
</Setter>
<Setter Property="LayoutTransform">
<Setter.Value>
<RotateTransform Angle="90"/>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</Grid>
</Border>
</ControlTemplate>
As you can see I use a nested text block to combine the group_box's header with some arrows. However the binding of the middle text block does not work.
To be honest, I work with WPF for a while now but data binding (to the right source) is still a mystery to me. Most of the time I get it to work somehow but if it fails I've no Idea what to do. I googled a few hours and tried various thing but nothing worked for me.
Any help is appreciated.
You could bind to the Header of the Content of the Expander:
<TextBlock Text="↥ "/>
<TextBlock Text="{Binding Content.Header, ElementName=expander}"/>
<TextBlock Text=" ↥"/>
You cannot use an ElementName to bind directly to the GroupBox since it is not in the same name scope as the Button.
Edit:
Ok, it only works if the expander is collapsed by default.
But you could use an x:Reference to bind to the GroupBox:
<TextBlock Text="↥ "/>
<TextBlock Text="{Binding Header, Source={x:Reference group_box}}"/>
<TextBlock Text=" ↥"/>

WPF TextBox text selection issue

I have an issue with selecting part of the text in TextBox. I cannot understand why, I can select just one character at a time and I can select as many as I want with the keyboard (shift+arrow). This is my code:
<Style TargetType="{x:Type local:EditableTextBlock}">
<Setter Property="FocusVisualStyle"
Value="{x:Null}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:EditableTextBlock}">
<Grid Background="{TemplateBinding Background}">
<Grid.Resources>
<BooleanToVisibilityConverter x:Key="bool2Vis" />
<local:BooleanToInverseVisibilityConverter x:Key="bool2InverseVis" />
</Grid.Resources>
<TextBox x:Name="textbox"
Visibility="{Binding IsInEditMode, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource bool2Vis}}"
TextWrapping="{TemplateBinding TextWrapping}"
Text="{Binding Text, Mode=TwoWay, UpdateSourceTrigger=LostFocus, RelativeSource={RelativeSource Mode=TemplatedParent}}" IsHitTestVisible="True" Focusable="True"/>
<ContentControl x:Name="textblockContainer"
Visibility="{Binding IsInEditMode, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource bool2InverseVis}}">
<TextBlock x:Name="textblock"
Text="{TemplateBinding Text}"
TextWrapping="{TemplateBinding TextWrapping}"
TextTrimming="CharacterEllipsis"/>
</ContentControl>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Any help will be appreciated.
Thanks in advance.

ListView Style Trigger affecting one control and not another

I have a ListView control with a list of system messages in it, and a corresponding "action" button that will help my user resolve the message:
If the message is an error message, I want the Foreground of the text and the action button (the [more...] part) to turn red. As you can see, it's not doing that.
I'm using a Style.Trigger bound to the message data's Severity to set the Foreground. This works fine for the TextBlock in the DataTemplate, but fails to affect the Button in that Template. My XAML looks like this:
<ListView x:Name="ImageCenterStatus" Background="{DynamicResource SC.ControlBrush}" Margin="10,22,10,0" FontSize="12" Grid.Column="1" ClipToBounds="True" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock TextWrapping="WrapWithOverflow" cal:Message.Attach="[Event MouseUp] = [Action DoStatusAction($dataContext)]" Text="{Binding Path=DisplayedMessage}"/>
<Button x:Name="ActionButton" Content="{Binding Path=DisplayedActionLabel}" FontSize="12" cal:Message.Attach="DoStatusAction($dataContext)" Style="{DynamicResource SC.ActionButtonHack}"></Button>
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Severity}" Value="Warning">
<Setter Property="Foreground" Value="#FFCE7A16" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Severity}" Value="Error">
<Setter Property="Foreground" Value="#FFD13636" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
</ListView>
The Button has its own Style, SC.ActionButtonHack, but I am very careful not to override the Foreground anywhere that style:
<Style x:Key="SC.ActionButtonHack" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<ContentPresenter x:Name="contentPresenter" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Why does the TextBlock in the DataTemplate respond to the Foreground change in the Trigger, but not the Button?
What do I need to do to get the button to respect the Foreground change?
Bind TextElement.Foreground of ContentPresenter to ListViewItem's foreground to get it work:
<Style x:Key="SC.ActionButtonHack" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<ContentPresenter x:Name="contentPresenter"
TextElement.Foreground="{Binding Foreground,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=ListViewItem}}"
HorizontalAlignment="Center"
VerticalAlignment="Center" Margin="0"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
OR
Either bind it on Button itself:
<Button x:Name="ActionButton"
Foreground="{Binding Foreground, RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType=ListViewItem}}"
Content="{Binding Path=Name}" FontSize="12"
Style="{DynamicResource SC.ActionButtonHack}"/>

Setting tooltip to equal content

I'm trying to set a data grid's cell's tooltip to be equal to the text inside of a TextBlock in that cell. What I have so far is this:
<Style x:Key="CellStyle" TargetType="{x:Type DataGridCell}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridCell">
<Grid>
<TextBlock Margin="2" VerticalAlignment="Center"
HorizontalAlignment="Left" TextWrapping="Wrap" >
<ContentPresenter Content="{TemplateBinding Property=ContentControl.Content}" />
<TextBlock.ToolTip>
<ContentPresenter Content="{TemplateBinding Property=ContentControl.Content}" />
</TextBlock.ToolTip>
</TextBlock>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
However, what this does is very briefly show the tooltip and then the content in the cell is removed, so nothing shows up at all. Also, setting the tooltip from outside the Template setter is an option, but I'm not sure what the corrent binding is to do that.
My example here is for a simple label but this can be applied to other controls.
<Label Name="lblFormName" Content="Form Elements:" FontWeight="Bold" HorizontalAlignment="Left" Width="295" >
<Label.ToolTip>
<Binding ElementName="lblFormName" Path="Content"/>
</Label.ToolTip>
</Label>
Check out this link: http://msdn.microsoft.com/en-us/library/ms742167.aspx
or this link for a bunch of binding "how-to"s from MS http://msdn.microsoft.com/en-us/library/ms752039.aspx
Have you tried using RelativeSource? I heard of some issues about TemplateBinding vs. RelativeSource (WPF TemplateBinding vs RelativeSource TemplatedParent).
<ContentPresenter Content="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type YourAncestorType}, AncestorLevel=1},Path=Content}" />
Where "YourAncestorType" is the type of the parent you want to find.
Or you could also try the same approach with
<ContentPresenter Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}" />
See also: http://www.wpfwiki.com/Default.aspx?Page=WPF%20Q5.3&AspxAutoDetectCookieSupport=1
Try removing the ToolTip from the ControlTemplate and defining a separate Setter in the Style for the Tooltip.
Here is the XAML using your sample:
<Style x:Key="CellStyle" TargetType="{x:Type WpfToolkit:DataGridCell}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="WpfToolkit:DataGridCell">
<Grid>
<TextBlock Margin="2" VerticalAlignment="Center"
HorizontalAlignment="Left" TextWrapping="Wrap" >
<ContentPresenter Content="{TemplateBinding Property=ContentControl.Content}" />
<!--<TextBlock.ToolTip>
<ContentPresenter Content="{TemplateBinding Property=ContentControl.Content}" />
</TextBlock.ToolTip>-->
</TextBlock>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=Content.Text}"/>
</Style>

Resources