How to apply StaticResource using Textbox.Style - wpf

I have a textbox using AddItemsTextBoxStyle (defined in resource dictionary), as below:
<TextBox x:Name="txtItems" Style="{StaticResource AddItemsTextBoxStyle}" />
However, if I want to apply DataTrigger to my textbox, then I can't use the self-closing tags format. Instead, I need to reformat my textbox to something like this:
<TextBox x:Name="txtItems">
<TextBox.Style>
<Style>
<Style.Triggers>
...
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
Sorry if this sounds silly. But how can I apply "static resource" for my textbox by using "TextBox.Style" tag?

You can use the Style.BasedOn property to combine your pre defined Style and your Trigger like this:
<TextBox x:Name="txtItems">
<TextBox.Style>
<Style BasedOn="{StaticResource AddItemsTextBoxStyle}">
<Style.Triggers>
...
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>

Related

How Textbox interacts with TextElement

I can write like this
<TextBox FontWeight="ExtraBold">
</TextBox>
And I can write like this
<TextBox>
<TextBox.Style>
<Style>
<Setter Property="TextElement.FontWeight" Value="ExtraBold"/>
</Style>
</TextBox.Style>
But i don't understand how TextBox interracts with TExtElement. For example TExtBlock has Inlines property. And MSDN says the following "PasswordBox, RichEditBox and TextBox don't support a text object model that's based on TextElement."
Who knows how it works?!
TextBox.Fontweight inherits from Control.Fontweight, whose Value is of Type System.Windows.Fontweight
TextElement.FontWeight is also of Type System.Windows.FontWeight.
You refer to this prop via a Style setter, so you can access the Property over another Class as well (as long the types match)
<TextBox>
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="ComboBox.FontWeight"
Value="Black" />
</Style>
</TextBox.Style>
</TextBox>
<TextBox>
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Control.FontWeight"
Value="Black" />
</Style>
</TextBox.Style>
</TextBox>
Are also valid.
Why this works, see below in Comment.

Conditional overwrite of style property

I'm trying to alter the background of a textbox if the value of the field contains a specified text. The problem that I encounter is that I already have a style applied to the field and I try to overwrite a property of the style like in the following example but with no success. Any ideas how could I achieve this?
<TextBox Grid.Column="1"
HorizontalAlignment="Right"
Text="{Binding CustomerType}" >
<TextBox.Style BasedOn="{DynamicResource SelectableTextStyle}">
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding CustomerType}" Value="Unknown">
<Setter Property="TextBox.Background" Value="Tomato"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
Your style definition, as written should probably not even compile because you are using an attribute on a property element (the TextBox.Style tag).
Update your code so that the inner style definition has the 'BasedOn' attribute like so:
<TextBox.Style>
<Style TargetType="TextBox" BasedOn="{DynamicResource SelectableTextStyle}">
<Style.Triggers>
....
And everything will be gravy.

DataTriggers : How it works

I want to implement a DataTrigger for say, textBox1. When Text inside textBox1 is "ABC" then I want to display "Data matched!" in another TextBox say, textBox2. I have written below xaml code for this but its not working. I am getting below error message.
'Text' member is not valid because it does not have a qualifying type name
XAML code for this is:
<Window x:Class="ControlTemplateDemo.Animation"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="Animation" Height="300" Width="607">
<Grid>
<Border Background="White">
<StackPanel Margin="30" HorizontalAlignment="Left" Width="500" Height="209">
<TextBox Name="textBox1">
<TextBox.Triggers>
<DataTrigger Binding="{Binding Path=Text}">
<DataTrigger.Value>
<sys:String>ABC</sys:String>
</DataTrigger.Value>
<Setter TargetName="textBox2" Property="Text" Value="Data matched!"/>
</DataTrigger>
</TextBox.Triggers>
</TextBox>
<TextBox Name="textBox2">
</TextBox>
</StackPanel>
</Border>
</Grid>
</Window>
Is there any problem in binding?
Thanks,
Hemant
You need to give the DataTrigger in a Style for the second TextBox
something like:
<StackPanel>
<TextBox x:Name="inputBox" />
<TextBox Margin="0 25 0 0">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Text"
Value="No Match Found" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=inputBox,
Path=Text}"
Value="ABC">
<Setter Property="Text"
Value="Match Found" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</StackPanel>
TextBox.Triggers does not support DataTrigger. I'd guess it's only for EventTriggers as the documentation states
on a side-note, I normally have my bindings in the element that ends up as the target(as much as I can). This way I find it easier to debug at-least personally. If the TextBox has wrong info I instantly check it's binding than every binding in my xaml file to see which element has a wrong binding that ends up updating my TextBox.

How do I enable text wrapping on all column headers?

How does one enable text wrapping on all column headers of a DataGrid, without disabling the other default header functionality? Such as column resizing, sort direction indicator, etc which are needed.
Is there a way to do this?
Or don't bother with the primitives in the app.xaml file and do the following (my objects):
<DataGrid Name="WBdataGrid" AutoGenerateColumns="False" ColumnHeaderHeight="50" >
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock TextWrapping="Wrap" Text="{Binding}"></TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.Columns> ...
I would like to enable text wrapping
on all column headers of my DataGrid,
without disabling the other default
header functionality
You need to add the following namespace to your app.xaml file:
xmlns:primitives="clr-namespace:Microsoft.Windows.Controls.Primitives;assembly=WPFToolkit"
and then add this tag to app.xaml:
<Style TargetType="{x:Type primitives:DataGridColumnHeader}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock TextWrapping="Wrap" Text="{Binding}"></TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
This will add text-wrapping to the headers of all of your DataGrids throughout your WPF application.
While we're on the subject, if you just wanted to center the text of each of your DataGrid headers, you could do this using the following style instead:
<Style TargetType="{x:Type primitives:DataGridColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Center"></Setter>
</Style>
Yet I've seen so many WPF articles suggesting that you can only do this by adding TextWrapping or HorizontalAlignment on each DataGrid column individually:
<toolkit:DataGridTextColumn Binding="{Binding Path=TaxAmount}">
<toolkit:DataGridTextColumn.HeaderTemplate >
<DataTemplate>
<TextBlock Text="Tax Amount" Width="90" TextWrapping="Wrap" HorizontalAlignment="Center"/>
</DataTemplate>
</toolkit:DataGridTextColumn.HeaderTemplate>
</toolkit:DataGridTextColumn>
I added
to the header text where I want the text to wrap. This is useful when you don't need to bind your header text to something
<DataGridTextColumn x:Name="cAccountNumber"
Header="Account
Number"
Width="80"
Binding="{Binding Path=AccountNumber}" />
Instead of assigning the column name directly to the DataGridColumn.Header property, I created a TextBlock containing the column name, set the TextWrapping property of the TextBlock to "Wrap" and assigned the TextBlock to the DataGridColumn.Header property. This preserves the default header functionality.
Example:
<toolkit:DataGridTextColumn Binding="{Binding Path=MyProperty}">
<toolkit:DataGridTextColumn.Header>
<TextBlock Text="Something Longer" TextWrapping="Wrap" />
</toolkit:DataGridTextColumn.Header>
</toolkit:DataGridTextColumn>
You could create a global Style for your column headers. Without any example mark-up I don't know the syntax, but it should look something like this:
<Style TargetType="{x:Type dg:ColumnHeader}">
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
Since the Style is key-less, it will automatically be applied to all of your column headers. And styles will not override any locally set properties, so it won't "disable" any existing header functionality.

How to specify a ToolTip for a control in a Style from XAML?

I'm using a the WPF datagrid from the Microsoft CodePlex project. I have a custom control that I want to databind to a field from the row of the datagrid. I can't for the life of me figure out how to specify a tooltip on a datagrid row.
The closest I've come is to use a RowStyle with a Setter to set the tooltip, but this only seems to work for text. When I try to put a ControlTempalte in as the Value for the ToolTip, it displays the result of calling ToString on the ControlTemplate type.
I think I need to set the "Template" property of the ToolTip, but I can't seem to figure out how to do that...
<dg:DataGrid Name="dgResults" AutoGenerateColumns="True">
<dg:DataGrid.RowStyle >
<Style TargetType="{x:Type dg:DataGridRow}">
<Setter Property="ToolTip" >
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToolTip}">
<StackPanel>
<TextBlock>txt1</TextBlock><TextBlock>txt2</TextBlock>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</dg:DataGrid.RowStyle>
</dg:DataGrid>
Figured it out... took me about 6 hours...
For some reason, I can't set the value directly using Value.Setter. If I define the content for the tooltip as a static resource though, and then set it in the Style property of the DataGrid.RowStyle it works.
So, the datagrid row style looks like:
<Style TargetType="{x:Type dg:DataGridRow}">
<Setter Property="ToolTip" Value="{StaticResource resKWIC}">
</Setter>
</Style>
</dg:DataGrid.RowStyle>
And the resource is
<Window.Resources>
<StackPanel x:Key="resKWIC">
<TextBlock>f1</TextBlock>
<TextBlock>f2></TextBlock>
</StackPanel>
</Window.Resources>
Thanks!
The key is to use the Property ToolTipService.ToolTip, instead of ToolTip - like this:
<Setter Property="ToolTipService.ToolTip" Value="My Tooltip"/>
I also got this working with a couple of changes; included incase it helps someone.
My Datadrid is bound to a list of custom objects, I wanted to display the string "Name" as a column and the string "text" in the tooltip. The trick for me (newbe) was that I had to include the Text Column and hide it for it to show up in the tooltip, i.e.:
<DataGrid AutoGenerateColumns="False" CanUserAddRows="False" EnableRowVirtualization="False" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" IsReadOnly="True" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding}" Name="dgrTextGroupText" VerticalContentAlignment="Stretch" Grid.Column="3" Grid.Row="1" Grid.RowSpan="6" CanUserReorderColumns="False" CanUserSortColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" Width="*" />
<DataGridTextColumn Binding="{Binding Text}" Width="0" Visibility="Hidden" />
</DataGrid.Columns>
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=DataContext.text}" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
</DataGrid>
Not sure you can do it through XAML.
A easier way might be to just handle the LoadingRow event. In xaml have something like:
<dg:DataGrid Name="dgResults" AutoGenerateColumns="True"
LoadingRow="dgResults_LoadingRow"
ItemsSource="{Binding ListOfStrings}" />
Then in code behind
void dgResults_LoadingRow(object sender, DataGridRowEventArgs e)
{
DataGridRow row = e.Row;
row.ToolTip = row.DataContext as string;
}
Obviously you will have to change the code depending on how you are populating the data in the datagrid. This is also untested =)
I needed to set the tooltip dynamically based on the cell content. I'm using the tooltip to display text overflow text from the cell. The binding below is from a c# class property named CellText. Thanks to the posts above for allowing me to avoid figuring out the entire thing myself.
<DataGridTextColumn Header="HeaderText" Binding="{Binding DisplayText, Mode=OneWay}" Width="33*">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="ToolTipService.ToolTip" Value="{Binding DisplayText, Mode=OneWay}"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
There's no need for the ControlTemplate. If you want the StackPanel in the ToolTip, just set it as:
<Setter Property="ToolTip">
<Setter.Value>
<StackPanel>
<TextBlock>txt1</TextBlock><TextBlock>txt2</TextBlock>
</StackPanel>
</Setter.Value>
</Setter>

Resources