DataBinding to RelativeSource Tag - wpf

I am trying to use the Tag property as the source of a Binding, but the value is null when it gets to the converter.
What am I doing wrong?
Consumer
<Button Style="{StaticResource AddNewItemButtonStyle}" Tag="blah" />
Binding
<Style x:Key="AddNewItemButtonStyle" BasedOn="{StaticResource blueButtonStyle}"
TargetType="{x:Type Button}">
...
<AccessText Text="{Binding RelativeSource={RelativeSource Self},
Path=Tag, Converter={StaticResource AddNewItemForLabel}}">
</Style>
UPDATE
I added a setter for the ToolTip using the same strategy, and that does work BUT only after the second call to the converter (triggered by mousing over).
Can you see why the binding wouldn't work on the first pass?
Is there some place else besides the Tag that I can use more reliably?
2nd UPDATE
Based on Phil's input I changed my style to the xaml below. Must I add a Template property to the style?
<Style x:Key="AddNewItemButtonStyle" BasedOn="{StaticResource blueButtonStyle}" TargetType="{x:Type Button}">
<Setter Property="resx:ResxExtension.DefaultResxName" Value="Smack.Core.Presentation.Resources.MasterDetail"/>
<Setter Property="Content" >
<Setter.Value>
<StackPanel Orientation="Horizontal">
<Image Source="{resx:Resx ResxName=Smack.Core.Presentation.Resources.MasterDetail, Key=bullet_add}" Stretch="Uniform" />
<AccessText VerticalAlignment="Center" Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Tag, Converter={StaticResource AddNewItemForLabel}}" />
<ContentPresenter/>
</StackPanel>
</Setter.Value>
</Setter>
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Tag, Converter={StaticResource AddNewItemForToolTip}}"/>
<Setter Property="Command" Value="{Binding AddNewItemCommand}" />
</Style>

If you change the xaml in the answer I gave to your other question to
<AccessText Grid.Column="1" VerticalAlignment="Center">
<AccessText.Text>
<MultiBinding StringFormat="{}_{0} {1}">
<Binding Source="{StaticResource Test}"/>
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Tag"/>
</MultiBinding>
</AccessText.Text>
</AccessText>
Then Tag will work.
Or you can use the short form of TemplateBinding
<AccessText Grid.Column="1" VerticalAlignment="Center" Text="{TemplateBinding Tag}"/>
or the long form
<AccessText Grid.Column="1" VerticalAlignment="Center"
Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Tag}"/>
or, your style will work like this (bits deleted for testing):
<Style x:Key="AddNewItemButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Content" >
<Setter.Value>
<StackPanel Orientation="Horizontal">
<AccessText VerticalAlignment="Center"
Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Button}}, Path=Tag}" />
<ContentPresenter/>
</StackPanel>
</Setter.Value>
</Setter>
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Tag}"/>
</Style>

Related

Why can't I use AlternationCount on a Border?

In the code below if I change
<Style TargetType="{x:Type Border}">
To
<Style TargetType="{x:Type ListBoxItem}">
It will color my entire listboxitem the correct color. But I only want to hit the border background
<ListBox x:Name="FilteredMessagesListBox" BorderThickness="0" ScrollViewer.HorizontalScrollBarVisibility="Disabled" HorizontalContentAlignment="Stretch" SelectionMode="Extended" Background="Transparent" AlternationCount="2">
<ListBox.Resources>
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="0">
<Setter Property="Background" Value="LightBlue"></Setter>
</Trigger>
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
<Setter Property="Background" Value="LightGreen"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate >
<DataTemplate>
<DockPanel Margin="0,0,0,3">
<Button x:Name="AttachmentImageButton" Click="AttachmentImageButton_Click" DockPanel.Dock="Bottom" MaxWidth="200" MaxHeight="200" HorizontalContentAlignment="Center" Visibility="{Binding ElementName=AttachmentImageButton, Converter={StaticResource cImageAttachmentToVisible}}" >
<Image Source="{Binding Path=Attachment}" x:Name="AttachmentImage" />
</Button>
<Button x:Name="AttachmentButton" Click="AttachmentButton_Click" DockPanel.Dock="Right" Visibility="{Binding ElementName=AttachmentButton, Converter={StaticResource cAttachmentToVisible}}" >
<Image Source="/MobilWPF;component/Resources/Images/PaperClip/PaperClip.jpg" Width="20" Height="20"/>
</Button>
<TextBlock Text="{Binding Converter={StaticResource cGetInstantMessageHeader}}" Width="120" TextWrapping="Wrap" HorizontalAlignment="Left" Background="Transparent" FontSize="10" DockPanel.Dock="Left"/>
<Border DockPanel.Dock="Left" HorizontalAlignment="Stretch" CornerRadius="5">
<DockPanel>
<TextBlock Margin="5,0,0,0" Text="{Binding Path=Message}" TextWrapping="Wrap" DockPanel.Dock="Left" HorizontalAlignment="Left" Background="Transparent" FontSize="12"/>
</DockPanel>
</Border>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I tried this:
<Border DockPanel.Dock="Left" HorizontalAlignment="Stretch" CornerRadius="5">
<Border.Triggers>
<DataTrigger Binding="{Binding Path=(ItemsControl.AlternationIndex), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}" Value="0">
<Setter Property="Background" Value="LightBlue"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=(ItemsControl.AlternationIndex), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}" Value="1">
<Setter Property="Background" Value="LightGreen"/>
</DataTrigger>
</Border.Triggers>
<DockPanel>
<TextBlock Margin="5,0,0,0" Text="{Binding Path=Message}" TextWrapping="Wrap" DockPanel.Dock="Left" HorizontalAlignment="Left" Background="Transparent" FontSize="12"/>
</DockPanel>
</Border>
Cannot find the static member 'BackgroundProperty' on the type 'ContentPresenter'
The problem is that AlternationIndex is set on the containers (ListBoxItems in this case), not on some child within them. You could solve this by using DataTriggers that bind to AlternationIndex on the parent ListBoxItem instead:
<DataTrigger Binding="{Binding Path=(ItemsControl.AlternationIndex), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}" Value="0">
<Setter Property="Background" Value="LightBlue"/>
</DataTrigger>

WPFdatagrid - how to use(give) different colors while grouping?

I am using WPF datagrid by codeplex.
I have a wpf grid with grouping features. I want the grouped region in different colors.
The screenshot is as follows:
Can different colors be assigned during grouping? If yes how do I achieve this in WPF datagrid?
Hope this helps...
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander>
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" />
<TextBlock Text=" ("/>
<TextBlock Text="{Binding Path=ItemCount}"/>
<TextBlock Text=" "/>
<TextBlock Text="Items"/>
<TextBlock Text=")"/>
</StackPanel>
</Expander.Header>
<ItemsPresenter>
<ItemsPresenter.Resources>
<Style TargetType="{x:Type toolkit:DataGridRow}">
<Style.Triggers>
<DataTrigger
Binding="{Binding RelativeSource=
{RelativeSource AncestorType={x:Type
GroupItem}}, Path=DataContext.Name}"
Value="1">
<Setter Property="Background"
Value="LightGreen"/>
</DataTrigger>
<DataTrigger
Binding="{Binding RelativeSource=
{RelativeSource AncestorType={x:Type
GroupItem}}, Path=DataContext.Name}"
Value="2">
<Setter Property="Background"
Value="LightPink"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ItemsPresenter.Resources>
</ItemsPresenter>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
The data triggers above check which value we have created the groups upon and accordingly assigns data grid row background colors.
So first group represents all values under text "1" (LightGreen) and next group is grouped under value 2 (LightPink).

WPF Style Triggers in DataTemplate

i hope you can help me. I got following Code in the Resources:
<UserControl.Resources>
<BitmapImage x:Key="img_src_lock" UriSource="/EEBase;component/Images/Lock_24x32.png" />
<BitmapImage x:Key="img_src_unlock" UriSource="/EEBase;component/Images/Unlock_24x32.png" />
<Style TargetType="{x:Type ToggleButton}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked}" Value="True">
<Setter Property="Content">
<Setter.Value>
<Image Source="{StaticResource img_src_lock}" />
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked}" Value="False">
<Setter Property="Content">
<Setter.Value>
<Image Source="{StaticResource img_src_unlock}" />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
<!-- TypeComboTemplateCollapsed -->
<DataTemplate x:Key="TypeComboTemplateCollapsed">
<TextBlock
Text="{Binding Path=Text, Mode=OneWay}"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="5,0,0,5"
/>
</DataTemplate>
<!-- TypeComboTemplateExpanded -->
<DataTemplate x:Key="TypeComboTemplateExpanded">
<TextBlock
Text="{Binding Path=Text, Mode=OneWay}"
VerticalAlignment="Center"
Margin="5,0,0,5"
/>
</DataTemplate>
<!-- EditCircleTemplate -->
<DataTemplate x:Key="EditCircleTemplate">
<!-- some content here, no ToggleButton -->
</DataTemplate>
<!-- EditRectangleTemplate -->
<DataTemplate x:Key="EditRectangleTemplate">
<!-- some other content here, including the ToggleButtons -->
<ToggleButton
IsChecked="{Binding Path=BaseLocked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
Margin="5"
/>
<ToggleButton
IsChecked="{Binding Path=HeightLocked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
Margin="5"
/>
</DataTemplate>
</UserControl.Resources>
To me that looks all correct.
Now, the problem is:
When i do the following, an exceptions occurs:
Specified element is already the logical child of another element. Disconnect it first.
1. load the control, selected type is CIRC
2. change the dropdown to select RECT (template triggers and togglebuttons are shown correctly)
3. change the dropdown back to CIRC
--> now the Exception occurs.
4. if i ignore the exception, the template "EditCircleTemplate" does not get loaded, and the normal ToString of the model object gets displayed.
Additional info:
originally there are 4 different types in the WPF, two of them with ToggleButtons (that's why i use templates). I cut them out, they dont differ really. But what i found out using all 4 templates is that the error does not occur when switching to a new Template, but when unloading a template with the toggle buttons. Which is kinda strange.
Also if i remove the DataTriggers for the ToggleButtons everything works like a charm.
The Exception comes from the XAML-Interpreter, so the Stacktrace is not useful at all.
Can anyone give me a hint what i am doing wrong?
Edit:
oops, i guess i forgot the content code:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition />
</Grid.RowDefinitions>
<ComboBox
Margin="5"
Grid.Row="0"
Grid.Column="0"
ItemsSource="{Binding Path=PossibleTypes, Mode=OneTime}"
SelectedItem="{Binding Path=SelectedType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<ContentControl x:Name="content" Content="{Binding}" ContentTemplate="{StaticResource TypeComboTemplateExpanded}"/>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ComboBoxItem}}" Value="{x:Null}">
<Setter TargetName="content" Property="ContentTemplate" Value="{StaticResource TypeComboTemplateCollapsed}"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ContentControl
Grid.Column="0"
Grid.Row="1"
Grid.ColumnSpan="2"
Content="{Binding Mode=OneWay}">
<ContentControl.ContentTemplate>
<DataTemplate >
<ContentControl
Name="inputContent"
Content="{Binding Mode=OneWay}"
ContentTemplate="{x:Null}"
/>
<DataTemplate.Triggers>
<DataTrigger
Binding="{Binding Path=SelectedType.Type, Mode=OneWay}"
Value="CIRC">
<Setter
TargetName="inputContent"
Property="ContentTemplate"
Value="{StaticResource EditCircleTemplate}"
/>
</DataTrigger>
<DataTrigger
Binding="{Binding Path=SelectedType.Type, Mode=OneWay}"
Value="RECT">
<Setter
TargetName="inputContent"
Property="ContentTemplate"
Value="{StaticResource EditRectangleTemplate}"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Grid>
Edit2/Solution:
I just found a workaround - it just does not satisfy me:
Instead of putting the style in the UserControl.Resources, which for me would be the more clean and intuitive solution, i have to set the style with the triggers on each ToggleButton separately.
So removing the and adding following code to each ToggleButton did the trick:
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked}" Value="True" >
<Setter Property="Content">
<Setter.Value>
<Image Source="{StaticResource img_src_lock}" />
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked}" Value="False">
<Setter Property="Content">
<Setter.Value>
<Image Source="{StaticResource img_src_unlock}" />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
The big question still persists: WHY?
The problem is that if you create an Image in a style there is only one instance, so as soon as multiple controls use the style there is going to be a fight over this one instance which can only be owned by one control.
The easiest solution to this is placing the Style in a resource and setting x:Shared to false, that way a copy of the style is used whereever referenced.
Why do you need to create BitmapImages and set them as Source to your Content Images in Triggers? Why arent you using the URI source to Images directly?
<Style TargetType="{x:Type ToggleButton}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked}" Value="True">
<Setter Property="Content">
<Setter.Value>
<Image Source="/EEBase;component/Images/Lock_24x32.png" />
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked}" Value="False">
<Setter Property="Content">
<Setter.Value>
<Image Source="/EEBase;component/Images/Unlock_24x32.png" />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
Let me know if this helps.

Custom validation of textbox in WPF

So I'm trying to do something along this example: http://www.codeproject.com/KB/WPF/wpfvalidation.aspx
My Textbox currently looks like this:
<TextBox Height="23" HorizontalAlignment="Left" Margin="118,60,0,0" Name="CreateUserCPRTextbox" VerticalAlignment="Top" Width="120" >
<TextBox.Text>
<Binding Path="Name" UpdateSourceTrigger="LostFocus">
<Binding.ValidationRules>
<validators:TextRangeValidator
MinimumLength="10"
MaximumLength="10"
ErrorMessage="ID has to be 10 letters" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
I've directly copied my TextRangeValidator from the example on that website. Nothing happens when I lose focus on the textbox. No matter what I type in it. Any Ideas? :)
Have you set the Validation.ErrorTemplate? It is defined as below in the Application.Resources in the example.You may have missed that
<Application.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Right"
Foreground="Orange"
Margin="5"
FontSize="12pt"
Text="{Binding ElementName=MyAdorner,
Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
</TextBlock>
<Border BorderBrush="Green" BorderThickness="3">
<AdornedElementPlaceholder Name="MyAdorner" />
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
</Application.Resources>
EDIT
Your default value is not triggering the validation routine.To force it to validate for default value you have to set
<validators:TextRangeValidator ValidatesOnTargetUpdated="True"
MinimumLength="10"
MaximumLength="10"
ErrorMessage="ID has to be 10 letters" />
I think you need to set ValidatesOnDataErrors=True in binding for Text to make it work
WPF TextBox Validation
<Style x:Key="TextBoxInError" TargetType="TextBox">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel>
<Grid>
<Polygon Points="20,10,20,0 0,0"
Stroke="Black"
StrokeThickness="1"
Fill="Red"
HorizontalAlignment="Right"
VerticalAlignment="Top"
ToolTip="{Binding ElementName=adorner,
Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"/>
<AdornedElementPlaceholder x:Name="adorner"/>
<AdornedElementPlaceholder Name="customAdorner" VerticalAlignment="Center" >
<Border BorderBrush="red" BorderThickness="1" />
</AdornedElementPlaceholder>
</Grid>
<Border Background="Red" DockPanel.Dock="right" Margin="5,0,0,0"
Width="150" Height="20" CornerRadius="5"
ToolTip="{Binding ElementName=customAdorner,
Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
<TextBlock Text= "{Binding ElementName=customAdorner,
Path=AdornedElement.(Validation.Errors)[0].ErrorContent}" VerticalAlignment="center" HorizontalAlignment="Left"
FontWeight="Bold" Foreground="white" Width="250" />
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}" />
</Trigger>
</Style.Triggers>
</Style>

What's wrong with this ContentTemplate?

I'm getting an error for this content template within a style: "Must specify both Property and Value for Setter." Aren't I doing that?
<Style x:Key="LinkButton" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<ContentPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Label x:Name="ContentRoot">
<StackPanel Orientation="Horizontal">
<Viewbox Width="24" Height="24" VerticalAlignment="Center">
<Image x:Name="ButtonImage" Source="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Button}}, Path=Tag}" />
</Viewbox>
<TextBlock VerticalAlignment="Center" x:Name="ButtonText" Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Button}}, Path=Content}"></TextBlock>
</StackPanel>
</Label>
<DataTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ButtonText" Property="TextBlock.TextDecorations" Value="Underline"/>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</Setter.Value>
</Setter>
And here is a button that will be using this style:
<Button Name="HelpButton" Style="{StaticResource LinkButton}" Height="30" Content="Help" Tag="Help.png"/>
Thanks!
I have loaded this up and have no such problems. The only problem I see is that your button will never have the style applied. This is because if you want the style applied you need to remove the x:Key from the style. Otherwise if you want the style applied only to the HelpButton, the definition should look like this:
<Button Name="HelpButton" Style="{StaticResource LinkButton}" Height="30" Content="Help" Tag="Help.png"/>
But I cannot see the error you are seeing.

Resources