Can't get WPF TextBlock to bind to Property - wpf

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=" ↥"/>

Related

Custom TextBox not updating property when change

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}" />

How to define a TabItem Header Template in WPF

I'm learning WPF and I read an article about Templating. So I wanted to write some code, but i got stuck.
What do I want to do? My Application has A TabControl and I want that all the tabs has the same Layout. A stackpanel and in the stackpanel an Image and a Textblock.
Now i don't know how the content can be set afterwards. Do I need a ContentPresenter?
<ControlTemplate x:Key="TabTemplate">
<StackPanel Orientation="Horizontal">
<Image></Image>
<TextBlock></TextBlock>
</StackPanel>
</ControlTemplate>
In your resource dictionary add a Style with your desired template:
<Style x:Key="CustomTabItemStyle"
TargetType="{x:Type TabItem}">
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid x:Name="Root"
Width="180"
Height="45"
Margin="0,0,0,0"
SnapsToDevicePixels="true">
<StackPanel Orientation="Horizontal">
<Image Width="90"
Margin="10"
VerticalAlignment="Center"
Source="pack://Application:,,,/img/myTabImage.png"
Stretch="Uniform" />
<TextBlock x:Name="contentPresenter"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Focusable="False"
FontSize="16"
Foreground="White"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Text="{TemplateBinding Header}" />
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Don't forget to edit your Image. If all tabs has same image then just change a Source link, otherwise, you may need another binding, e.g Content.
And then simply use this style in your TabItems:
<TabControl Margin="0,5,0,0"
FocusVisualStyle="{x:Null}">
<TabItem Header="My First Tab"
IsSelected="{Binding FirstTabItemSelected}"
Style="{DynamicResource CustomTabItemStyle}">
...
</TabItem>
<TabItem Header="My Second Tab"
IsSelected="{Binding SecondTabItemSelected}"
Style="{DynamicResource CustomTabItemStyle}">
...
</TabItem>
</TabControl>

How can I access child element of any object by using style?

I'm faced with a problem about to find child element. I want to access TextBlock element inside Label. But I can't find it.
Here is my MainWindow.xaml code:
<Label x:Name="text" Style="{DynamicResource labelstyle}">
<TextBlock>asdasdasd</TextBlock>
</Label>
Here is my style code:
<Style x:Key="labelstyle" TargetType="Label">
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Label">
<Border BorderThickness="2" BorderBrush="Red">
<TextBox x:Name="textBox" Text="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type TextBlock},
AncestorLevel=2},Path=Text}">
</TextBox>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I want to bind TextBox's Text property to Label inside TextBlock's Text property. What should I do ?
I hope I made my self clear.
Thank you.
Below is a sample image:
Instead of using a TextBlock in your label, just leave it in your template and have it reference the Label Content for the text to be displayed.
Below is an example:
<Label x:Name="text" Content="asdasdasd" Style="{StaticResource labelstyle}"/>
and for the styling/template
<Style x:Key="labelstyle" TargetType="{x:Type Label}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Label}">
<Border BorderThickness="2" BorderBrush="Red">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style>
This should provide you with the centered text in the red border you seem to be trying to achieve.
Hopefully this helps you a bit.
This will let you display text through a binding and the user will be able to select it, but not type in the TextBox. If you want to be able to also type in the TextBox, remove the IsReadOnly="True"
<Label Height="30" Width="150">
<Label.Template>
<ControlTemplate TargetType="{x:Type Label}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0"
MinWidth="26"
Margin="2"
Source="{Binding myImageSource}"/>
<TextBox Grid.Column="1"
IsReadOnly="True"
Text="{Binding myTextValue}"
Margin="5,2"/>
</Grid>
</ControlTemplate>
</Label.Template>
</Label>

WPF ListBox items with template become invisible after grouping

I have ObservableCollection with items which I want to display in a ListBox.
Also I write a template for ListboxItem for correct display of my collection.
On this stage everything works fine.
in .cs
Sensors = new ObservableCollection<Sensor>();
...
lstBox.ItemsSource = Sensors;
in .xaml
...
<DataTemplate x:Key="SensorTileTemplate">
<Border>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" Grid.Row="0" Grid.ColumnSpan="3"></TextBlock>
<Image Source="{Binding ImageModel.ImgSource}" Style="{StaticResource ImageGlowStyle}" Height="72" Grid.Row="1" Grid.Column="0"></Image>
<StackPanel Grid.Row="1" Grid.Column="1" Margin="5">
<TextBlock Text="IP:"></TextBlock>
<TextBlock Text="Port:"></TextBlock>
<TextBlock Text="Command port:"></TextBlock>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="2" Margin="5">
<TextBlock Text="{Binding DeviceAddress}"></TextBlock>
<TextBlock Text="{Binding DeviceDataPort}"></TextBlock>
<TextBlock Text="{Binding DeviceControlPort}"></TextBlock>
</StackPanel>
</Grid>
</Border>
</DataTemplate>
<Style x:Key="ContainerStyle">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="ListBoxItem.Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
...
<ListBox Name="lstBox" Focusable="False"
SelectionChanged="lstBox_SelectionChanged"
HorizontalContentAlignment="Stretch"
ItemTemplate="{StaticResource SensorTileTemplate}"
ItemContainerStyle="{StaticResource ContainerStyle}">
</ListBox>
The problem appears when I need to group certain items using expander as a group container.
in .cs
...
ICollectionView view = CollectionViewSource.GetDefaultView(Sensors);
view.GroupDescriptions.Add(new PropertyGroupDescription("GroupNumber"));
lstBox.ItemsSource = view;
...
in .xaml
<!--Same Template and Style-->
...
...
<Style x:Key="GroupContainerStyle" TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Group #" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
...
<ListBox Name="lstBox" Focusable="False"
SelectionChanged="lstBox_SelectionChanged"
HorizontalContentAlignment="Stretch"
ItemTemplate="{StaticResource SensorTileTemplate}"
ItemContainerStyle="{StaticResource ContainerStyle}">
<ListBox.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource GroupContainerStyle}" />
</ListBox.GroupStyle>
</ListBox>
This code works and groups items but items become invisible.
So without grouping items display correctly but with grouping expanders show nothing in it.
I think there is something about ItemsPresenter in Expander but can't figure out what.
The problem was in one third party theme I use in my app. That theme has a ListBox template like:
<Style TargetType="{x:Type ListBox}">
...
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}">
<Grid>
<Border x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2" Background="{DynamicResource ControlBackgroundBrush}" />
<ScrollViewer Margin="1" Style="{DynamicResource NuclearScrollViewer}" Focusable="false" Background="{x:Null}">
<StackPanel Margin="1,1,1,1" IsItemsHost="true" />
</ScrollViewer>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" Value="{DynamicResource DisabledBackgroundBrush}" TargetName="Border" />
<Setter Property="BorderBrush" Value="{DynamicResource DisabledBorderBrush}" TargetName="Border" />
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
So I use an ItemsPresenter instead of the StackPanel in that template and everything works now.

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