WPF Button icon gets mirrored, why? - wpf

This Button looks fine (see screenshot) when defining the image as done below. Note that the shield shaped icon with the letter 'T' is visualized correctly.
<Button Command="w:MainWindow.BrowseTorrentSite">
<StackPanel>
<Image Source="../icons/kickasstorrent.png" />
</StackPanel>
</Button>
When I want to rely on the buttons enabled state, the icon gets mirrored.
<StackPanel
Orientation="Horizontal"
FlowDirection="RightToLeft">
<Button
x:Name="KatButton"
Command="w:MainWindow.BrowseTorrentSite">
<StackPanel>
<Image>
<Image.Style>
<Style TargetType="Image">
<Style.Triggers>
<DataTrigger
Binding="{Binding Path=IsEnabled, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Button}}"
Value="True">
<Setter Property="Source" Value="../icons/kickasstorrent.png" />
</DataTrigger>
<DataTrigger
Binding="{Binding Path=IsEnabled, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Button}}"
Value="False">
<Setter Property="Source" Value="../icons/kickasstorrent_disabled.png" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</StackPanel>
</Button>
</StackPanel>
Note the shield shaped icon with the letter 'T' is now mirrored.
What is responsible for mirroring the icon?
If somebody has tips to debug this in whatever way possible, feel free to point me in the right direction!

The problem is in the parent StackPanel. If the StackPanel is defining the FlowDirection from right to left, the Image definition inherits this property which results in the flipped icon.
To solve this problem redefine left to right on the image itself.
<StackPanel
Orientation="Horizontal"
FlowDirection="RightToLeft">
<Button>
<Image FlowDirection="LeftToRight">
<Image.Style>
<!-- etc etc -->
</Image.Style>
</Image>
</Button>
</StackPanel>

Related

WPF How to attach Popup to simple UIElement like rectangle

It took me hours to figure out the answer to this question, so I thought I would write an FAQ or answer for what I found. (it is based on the following thread Binding Textbox IsFocused to Popup IsOpen plus additional conditions)
I found lots of examples of binding popups to things like toggle buttons and other things that are based on windows chrome and have built in triggers. But in my application I wanted to bind a popup to a simple rectangle with a custom brush fill. I could not find an example on how to have a popup open ans stay open when a user mouses over the rectangle.
So I am posting this question and I will immediately post the answer I found so that hopefully someone else can benefit from it. I will also mark an answer for anyone who can help me understand if stackoverflow allows posts like this, or a better way I could have gone about it.
EDIT 1)
I can't self answer for 8 hours so here is the working code:
the following is a simple example of how to use the popup on a basic UIElement like a rectangle/ellipse/etc...
<Grid HorizontalAlignment="Stretch" Height="Auto">
<Rectangle x:Name="PopupRec"
Grid.Row="0"
Width="20" Height="20"
HorizontalAlignment="Right"
Fill="Gray" Margin="0,0,0,10" />
<Popup x:Name="SortPopup"
PlacementTarget="{Binding ElementName=PopupRec}"
StaysOpen="False"
PopupAnimation="Slide"
AllowsTransparency="True">
<Border Background="White" Padding="15">
<StackPanel Orientation="Vertical">
<Button Command="{Binding MyCommand}" CommandParameter="5">5</Button>
<Button Command="{Binding MyCommand}" CommandParameter="10">10</Button>
<Button Command="{Binding MyCommand}" CommandParameter="15">15</Button>
<Button Command="{Binding MyCommand}" CommandParameter="20">20</Button>
</StackPanel>
</Border>
<Popup.Style>
<Style TargetType="{x:Type Popup}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=PopupRec, Path=IsMouseOver}" Value="True">
<Setter Property="IsOpen" Value="True" />
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=SortPopup, Path=IsMouseOver}" Value="True">
<Setter Property="IsOpen" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</Popup.Style>
</Popup>
</Grid>
Just paste this inside window/usercontrol/etc...
I'd suggest the following improvement. It would make the Popup Style independent of any element names, and would thus enable you to use it as a default Style by putting it into the Window's or UserControl's Resources.
<Style TargetType="{x:Type Popup}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsMouseOver,
RelativeSource={RelativeSource Self}}"
Value="True">
<Setter Property="IsOpen" Value="True" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=PlacementTarget.IsMouseOver,
RelativeSource={RelativeSource Self}}"
Value="True">
<Setter Property="IsOpen" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
And please note that a Rectangle is not a "basic UIElement". It's a Shape, which itself is a FrameworkElement.

Trigger on an Inner/Attached property

Trigger on an Inner property
<Button BorderBrush="Black" BorderThickness="2" x:Name="TimeButton" ClickMode="Press" Click="SetTime_Click" Height="26" HorizontalAlignment="Left" Margin="15, 0, 0, 0" Style="{StaticResource ImageButtonStyle}" ToolTip="Set Time" Width="26">
<Button.Background>
<ImageBrush x:Name="TimeImageBrush" ImageSource="/YCS;component/Images/Clock.png" Stretch="Uniform" TileMode="None" />
</Button.Background>
</Button>
I need to make a trigger to set the ImageBrush in the Button.Background property to something different according to a boolean named HasHours which I can bind easily from my itemssource, any one knows how I can achieve this, I could not find any examples linking to this property....
I tried something like this
<Button.Triggers>
<DataTrigger Binding="{Binding HasHours}" Value="false">
<Setter TargetName="TimeImageBrush" Property="ImageSource" Value="/YCS;component/Images/ClockRed.png"/>
</DataTrigger>
</Button.Triggers>
but it gives me this error:
Cannot find the static member 'ImageSourceProperty' on the type 'ContentPresenter'.
Any help is much appreciated
This is perhaps not exactly an answer to your question.
First, i guess you won't be able to add a DataTrigger to the Triggers collection, since that only supports EventTriggers.
But, you could define the DataTrigger in the Button's Style. Here, instead of setting the ImageBrush's ImageSource property, simply set a new ImageBrush as Background.
<Button ...>
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding HasHours}" Value="False">
<Setter Property="Background">
<Setter.Value>
<ImageBrush ImageSource="/YCS;component/Images/ClockRed.png"/>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Put the image as Content, not as Background, since you have no content.
Put the DataTrigger in the Triggers of the Image, not of the Button.
You will have to seek for the DataContext of the Trigger :
So something like :
<Button ... >
<Image ... >
<Image.Triggers>
<DataTrigger
Binding="{Binding Path= HasHours, RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Button}}}"
Value="false" >
<Setter Property="ImageSource" Value="/YCS;component/Images/ClockRed.png"/>
</DataTrigger>
</Image.Triggers>
</Image>
</Button>

WPF Xceed datagrid - datatrigger on cell's content makes me lose data on load...however re

I am using the Xceed datagrid for WPF. Today I was trying to change the background of the whole row if one of its column "SA" has the some value or not null. I wrote the following piece of code in XAML with a converter function in code behind:
<xcdg:DataGridControl.Resources>
<Style TargetType="{x:Type xcdg:DataRow}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource newConverter}, Path=Cells[SA].Content}" Value="True">
<Setter Property="Background" Value="LightGreen" />
</DataTrigger>
</Style.Triggers>
</Style>
</xcdg:DataGridControl.Resources>
To my surprise, as soon as I load the grid for the first time, the data in the SA column is nowhere to be seen. However, once I scroll down a bit, till the point that row which is supposed to have data for the column is not visible, and then when I scroll up again to see that row, I can see the value in that column as well as the background changed.
What am I doing wrong?
Use simple binding and avoid converter/template
<TextBlock Text="{Binding}"></TextBlock>
To fill color in your column use this or the following code:
<xcdg:Column Title="Unit Price" FieldName="UnitPrice" ReadOnly="True">
<xcdg:Column.CellContentTemplate>
<DataTemplate>
<DockPanel LastChildFill="false" x:Name="UnitPrice">
<TextBlock Text="{Binding}"></TextBlock>
<Image x:Name="img" Width="16" Height="16" DockPanel.Dock="Right"
Margin="2,0,0,0" Visibility="Collapsed"
ToolTip="Unit Price is Undefined." VerticalAlignment="Center"
HorizontalAlignment="Left" />
</DockPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding}" Value="0.00">
<Setter TargetName="img" Property="Visibility" Value="Visible" />
<Setter TargetName="UnitPrice" Property="Background" Value="Pink" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</xcdg:Column.CellContentTemplate>
</xcdg:Column>

How do I change the image when the button is disabled?

I'm trying to show a different image when the button is disabled; I thought it would be easy with triggers.
However, I have not been able to get the image source to switch to the disabled image when the button is disabled. I've tried setting triggers on both the image and button. What is wrong with what I have below? How can I change the image source when the button is enabled/disabled?
<Button
x:Name="btnName"
Command="{Binding Path=Operation}"
CommandParameter="{x:Static vm:Ops.OpA}">
<Button.Content>
<StackPanel>
<Image
Width="24"
Height="24"
RenderOptions.BitmapScalingMode="NearestNeighbor"
SnapsToDevicePixels="True"
Source="/MyAssembly;component/images/enabled.png">
<Image.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=btnName, Path=Button.IsEnabled}" Value="False">
<Setter Property="Image.Source" Value="/MyAssembly;component/images/disabled.png" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</StackPanel>
</Button.Content>
</Button>
Yeah this one pops up quite a bit.
Any property that's explicitly set in the object's declaration can't be changed in a style. So because you've set the image's Source property in the declaration of the image, the style's Setter won't touch it.
Try this instead:
<Image
Width="24"
Height="24"
RenderOptions.BitmapScalingMode="NearestNeighbor"
SnapsToDevicePixels="True"
>
<Image.Style>
<Style TargetType="Image">
<Setter Property="Source"
Value="/MyAssembly;component/images/enabled.png" />
<Style.Triggers>
... your trigger and setter ...
</Style.Triggers>
</Style>
</Image.Style>
</Image>

How can I indicate in an Expander header that collapsed contents have an error

I have expanders that contain text boxes, the text boxes use the wpf validation stuff to draw a red box around them ( text boxes are wrapped in Adorner Decorators to make sure I don't get empty red boxes everywhere when the expanders are collapsed)
I want to indicate in the header of the expander that it has contents that have errors (in case it is in a collapsed state) - an icon or red exclamation mark or something. I think I see a way to do this in code from my validation function (not ideal) but is there a way to do it in xaml? Can I use a style for the expander with a trigger somehow pointing to the Validation.HasError of all children?
thanks for any thoughts..
Trev
If you know the contents of your expander, you can use a MultiDataTrigger to do this:
<Expander>
<Expander.Header>
<TextBlock>
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Text" Value="ERROR"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=txtWidth, Path=(Validation.HasError)}" Value="False"/>
<Condition Binding="{Binding ElementName=txtHeight, Path=(Validation.HasError)}" Value="False"/>
</MultiDataTrigger.Conditions>
<Setter Property="Text" Value="NO ERROR"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Expander.Header>
<StackPanel>
<TextBox x:Name="txtWidth" Text="{Binding Width, ElementName=rect, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True}"/>
<TextBox x:Name="txtHeight" Text="{Binding Height, ElementName=rect, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True}"/>
<Rectangle x:Name="rect" Width="100" Height="100" Margin="10" Fill="Green"/>
</StackPanel>
</Expander>
If the contents of the expander aren't known, then you'll probably have to set Binding.NotifyOnValidationError on the TextBoxes and handle the Error attached event.

Resources