Set Datacontext of a control to a property - wpf

I am trying to create a style for a textblock to create a highlight effect. This style will be used on a number of TextBlocks, each bound to a different property. The datacontext for my main control is set in code behind:
this.DataContext = dataobject;
I can create one textblock with a working binding like this:
<TextBlock Text="Field1">
<TextBlock.Style>
<Style x:Key="HighlightStyle" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=.}" Value="True">
<Setter Property="Background" Value="Yellow"/>
<Setter Property="Foreground" Value="Black"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
<TextBlock>
But I need to change the binding so that the style can be used on different TextBlocks. Something like:
<Style x:Key="HighlightStyle" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=.}" Value="True">
<Setter Property="Background" Value="Yellow"/>
<Setter Property="Foreground" Value="Black"/>
</DataTrigger>
</Style.Triggers>
</Style>
<TextBlock Text="Field1" DataContext="{Binding Path=BooleanProperty1}" Style="{StaticResource HighlightStyle}"/>
<TextBlock Text="Field2" DataContext="{Binding Path=BooleanProperty2}" Style="{StaticResource HighlightStyle}"/>
When I try this, changing the properties does nothing to highlight the textblock. Is there a way to accomplish this?

Could always abuse the tag property.
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="Tag" Value="True">
<Setter Property="Background" Value="Yellow"/>
<Setter Property="Foreground" Value="Black"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="Field1" Grid.Row="0" Tag="{Binding Field1}"/>
<TextBlock Text="Field2" Grid.Row="1" Tag="{Binding Field2}"/>
</Grid>
</Window>
or if you're feeling adventurous you could create an attached dependency property and pass that over.
public class TextBlockExtender : DependencyObject
{
public static string GetMyDataField(DependencyObject obj)
{
return (string)obj.GetValue(MyDataFieldProperty);
}
public static void SetMyDataField(DependencyObject obj, string value)
{
obj.SetValue(MyDataFieldProperty, value);
}
public static readonly DependencyProperty MyDataFieldProperty =
DependencyProperty.RegisterAttached("MyDataField", typeof(string), typeof(TextBlockExtender), new PropertyMetadata(null));
}
used with XAML
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="local:TextBlockExtender.MyDataField" Value="True">
<Setter Property="Background" Value="Yellow"/>
<Setter Property="Foreground" Value="Black"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="Field1" Grid.Row="0" local:TextBlockExtender.MyDataField="{Binding Field1}"/>
<TextBlock Text="Field2" Grid.Row="1" local:TextBlockExtender.MyDataField="{Binding Field2}"/>
</Grid>
</Window>

Related

Cannot set trigger within TabControl template in WPF

I wrote a style for my TabControl. Within the TabControl I have a TextBlock and a Button. I wish to set trigger for TabItem.IsSelected such that the font colour of the text within TextBlock changed. My code below doesn't work:
<Style x:Key="_tabItemButtonStyle" BasedOn="{StaticResource MetroTabItem}" TargetType="{x:Type TabItem}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Name="_TabHeaderStackPanel" >
<TextBlock Text="{ Binding TabName }" Name="_TabHeaderText" Background="{ Binding TabBackColour }" FontSize="{ Binding TabFontSize }" >
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected,
RelativeSource={RelativeSource AncestorType=TabItem}}"
Value="True">
<Setter Property="Foreground" Value="SteelBlue"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
...
The problem I suspect is with this code:
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected,
RelativeSource={RelativeSource AncestorType=TabItem}}"
Value="True">
<Setter Property="Foreground" Value="SteelBlue"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
EDIT:
I bind my tab items to a Collection of ViewModels. So my style binding looks as following:
<TabControl x:Name="_MainTabControl" Grid.Column="1" Grid.Row="1"
SelectedIndex="0"
ItemsSource="{Binding OpenTabs}"
ItemContainerStyle="{StaticResource _tabItemButtonStyle}" />
I don't know what MetroTabItem is. But the other Code looks fine.
I tried it out with sample Code. And it does exactly what you describe you want to achieve.
Please check whether your Styles are correctly applied with Style={StaticResource YourStyleName} on the TabItem
Here my XAML:
<Window x:Class="WpfApplication1.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:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
</Window.Resources>
<Grid>
<Grid.Resources>
<ResourceDictionary>
<Style TargetType="{x:Type TabItem}" x:Key="TestStyle">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Name="_TabHeaderStackPanel" >
<TextBlock Text="test" Name="_TabHeaderText" Background="{ Binding TabBackColour }" FontSize="{ Binding TabFontSize }" >
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected,
RelativeSource={RelativeSource AncestorType=TabItem}}"
Value="True">
<Setter Property="Foreground" Value="SteelBlue"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<Button Content="Test"></Button>
</StackPanel></DataTemplate></Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</Grid.Resources>
<TabControl>
<TabItem Style="{StaticResource TestStyle}">
</TabItem>
<TabItem Style="{StaticResource TestStyle}">
</TabItem>
</TabControl>
</Grid>

On button hover, enable ResizeMode

I am trying to make window frame appear when user hovers "mybutton". This should work but for some reason it is not. What am I missing?
<Window x:Class="test2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" WindowStyle="None" Loaded="ShellWindow_SourceInitialized" x:Name="mywindow">
<Window.Resources>
<Style TargetType="{x:Type Window}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsMouseOver, ElementName=mybutton}" Value="True">
<Setter Property="ResizeMode" Value="CanResize" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsMouseOver, ElementName=mybutton}" Value="False">
<Setter Property="ResizeMode" Value="NoResize" />
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid Name="mygrid" Loaded="Grid_Loaded">
<Button Name="mybutton" Content="Button" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75"/>
</Grid>
(In case you are wondering what those "Loaded" functions are, they remove chrome while keeping the shadow and allowtransparency=false
http://marcin.floryan.pl/blog/2010/08/wpf-drop-shadow-with-windows-dwm-api)
At the end it will not be button but will react on whole window border (I can not find better way to enable resizing while removing all chrome)
In the style TargetType should be MainWindow
<Style TargetType="{x:Type local:MainWindow}">
where local has to be mapped to your namespace:
xmlns:local="clr-namespace:test2"
myButton is defined after style. XAML parser is not that smart. User the following XAML to get your intended behavior:
<Window x:Class="test2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" WindowStyle="None" Loaded="ShellWindow_SourceInitialized" x:Name="mywindow">
<Grid Name="mygrid" Loaded="Grid_Loaded" >
<Button Name="mybutton" Content="Button" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75"/>
</Grid>
<Window.Style>
<Style TargetType="{x:Type Window}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsMouseOver, ElementName=mybutton}" Value="True">
<Setter Property="ResizeMode" Value="CanResize" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsMouseOver, ElementName=mybutton}" Value="False">
<Setter Property="ResizeMode" Value="NoResize" />
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Style>
</Window>

Fire trigger in UserControl based on DependencyProperty

have a very easy question, but it seems I could not find the answer on the Internet for it. Possibly because I am not looking in the right places.
I have a user control with a DependencyProperty of a custom enum type.
In XAML I would like to Show/Hide elements based on the value of the enum type. I tried to do this with DataTriggers but I fail to get it working.
<UserControl x:Class="WpfApplication1.DisplayIcon"
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="50" d:DesignWidth="50"
x:Name="control">
<UserControl.Resources>
<Style TargetType="Ellipse">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Value="Ellipse" Binding="{Binding MyIconType, ElementName=control}">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="Rectangle">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Value="Rectangle" Binding="{Binding MyIconType, ElementName=control}">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Grid>
<Ellipse x:Name="el1" Fill="Red" Width="30" Height="30" />
<Rectangle x:Name="el2" Fill="Green" Width="20" Height="20" />
<TextBlock Text="{Binding MyIconType, ElementName=control}" Margin="0,40,0,0"/>
</Grid></UserControl>
And my code behind looks like this:
public enum IconType
{
Ellipse,
Rectangle
}
public partial class DisplayIcon : UserControl
{
public DisplayIcon()
{
InitializeComponent();
}
public IconType MyIconType
{
get { return (IconType)GetValue(MyIconTypeProperty); }
set { SetValue(MyIconTypeProperty, value); }
}
// Using a DependencyProperty as the backing store for MyIconType. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyIconTypeProperty =
DependencyProperty.Register("MyIconType", typeof(IconType), typeof(DisplayIcon), new PropertyMetadata(IconType.Ellipse));
}
Can someone help me?
Thanks,
Jim
You can create a Style for each element and define the Triggers there:
<UserControl x:Class="WpfApplication1.DisplayIcon"
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="50" d:DesignWidth="50"
x:Name="control">
<UserControl.Resources>
<Style TargetType="Ellipse">
<Style.Triggers>
<DataTrigger Value="Rectangle" Binding="{Binding MyIconType, ElementName=control}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="Rectangle">
<Style.Triggers>
<DataTrigger Value="Ellipse" Binding="{Binding MyIconType, ElementName=control}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Grid>
<Ellipse x:Name="el1" Fill="Red" Width="20" Height="20"/>
<Rectangle Grid.Row="1" x:Name="el2" Fill="Green" Width="20" Height="20"/>
</Grid>
EDIT:
Infact it would probably make more sense to invert the Visibility logic. That way you can add shapes without needing to modify the code:
<UserControl x:Class="WpfApplication1.DisplayIcon"
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="50" d:DesignWidth="50"
x:Name="control">
<UserControl.Resources>
<Style TargetType="Ellipse">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Value="Ellipse" Binding="{Binding MyIconType, ElementName=control}">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="Rectangle">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Value="Rectangle" Binding="{Binding MyIconType, ElementName=control}">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Grid>
<Ellipse x:Name="el1" Fill="Red" Width="20" Height="20"/>
<Rectangle x:Name="el2" Fill="Green" Width="20" Height="20"/>
</Grid>

xaml how to create a trigger isMouseHover on TextBlock

How to create a trigger, which if mouse hover that textblock, the text will change color.
just try this with background or foreground
<TextBlock Text="Hello" Height="20">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="TextBlock.Background" Value="red" />
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style x:Key="TextBlockMouseOverStyle" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<TextBlock Background="Blue" Style="{StaticResource TextBlockMouseOverStyle}" Text="Foo" />
</Grid>
</Window>

How can I bind another DependencyProperty to the IsChecked Property of a CheckBox?

Here's an example of what I'm trying to accomplish:
<Window x:Class="CheckBoxBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<CheckBox Name="myCheckBox">this</CheckBox>
<Grid>
<Grid.Resources>
<Style TargetType="ListBox">
<Style.Triggers>
<Trigger Property="{Binding ElementName=myCheckBox, Path=IsChecked}" Value="True">
<Setter Property="Background" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<ListBox>
<ListBoxItem>item</ListBoxItem>
<ListBoxItem>another</ListBoxItem>
</ListBox>
</Grid>
</StackPanel>
</Window>
When I try to run it, I get this XamlParseException:
A 'Binding' cannot be set on the 'Property' property of type 'Trigger'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
So how can I bind a property on the ListBox to the IsChecked property of a CheckBox?
Try using a DataTrigger. Replace your Grid.Resources with the following and it works:
<Grid.Resources>
<Style TargetType="ListBox">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=myCheckBox, Path=IsChecked}" Value="True">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>

Resources