WPF: Making the entire "block" of a path clickable - c#

I have a special ControlTemplate for some of my Buttons.
<ControlTemplate TargetType="{x:Type Button}">
<Path Name="ThePath" Fill="White" Stretch="UniformToFill"
Width="12" Height="12" Stroke="White"
StrokeThickness="4"
Data="M1.5,1.5 L10.5,10.5 M1.5,10.5 L10.5,1.5"/>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Fill" Value="#afa" TargetName="ThePath"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
This works fine, but since I'm using a Path (in this case, it's just shaped like a fat X), exactly the path is clickable, not the small space between the corners of the X. Is there any automagic thing I can use to make the entire "block" of the X clickable?
I've considered wrapping the path in a rectangular object, but I'd just like to make sure I'm not missing something trivial.

Aviad P. is correct. This is what I do:
<Border Background="Transparent">
<Path ... />
</Border>
This works because when "hit testing" to determine where a mouse click should be routed, the "Transparent" brush is considered as if it was a regular color.

Related

WPF - Diamond-shaped button style with text inside

How to make diamond-shaped button style with text that is displayed horizontally like this
I did this, but don't know what to do next:
<Style x:Key="Button">
<Setter Property="Button.Height" Value="40"/>
<Setter Property="Button.Width" Value="40"/>
<Setter Property="Button.RenderTransform">
<Setter.Value>
<RotateTransform Angle="45"/>
</Setter.Value>
</Setter>
</Style>
The easiest option is to not define your text content utilizing the Button control Content dependency property, but instead define your Text in a separate control such as the obvious TextBlock, since the content is also going to be rotated 45 degrees due to the render transform.
In your style, if you want your button to look exactly as the one in the link that you provided, you should also set the following dependency properties: BorderThickness, BorderBrush (Black) and Background (White).
<Grid.Resources>
<Style TargetType="Button">
<Setter Property="Height" Value="100"/>
<Setter Property="Width" Value="100"/>
<Setter Property="BorderThickness" Value="4"/>
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="Background" Value="White"/>
<Setter Property="RenderTransform">
<Setter.Value>
<RotateTransform Angle="45"/>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
Place the pair Button/TextBlock inside the same layout area, and overlap the Button control with the TextBlock. Depending on the dimensions to which you set your Button control element, you can easily play with the Margin dependency property on the TextBlock control and put it exactly on your desired place.
This would be my option if I were to choose a Grid as the layout container for these 2 control definitions.
If instead I choose to use a Canvas, I would utilize the attached properties Canvas.Left and Canvas.Top, like this
<Canvas Margin="100,0,0,0">
<Button x:Name="myButton"/>
<TextBlock Text="TextTextText" Canvas.Left="-40" Canvas.Top="60"/>
</Canvas>
You could actually have an CLR property calculating these two distances, based on your Button dimensions and perform binding to these two attached properties. For this situation, I set those values for the offsets for a Button dimension of (Width: 100, Height: 100).
PS. The code provided is working for UWP (I am more used to it :p), so I am not particularly 100% sure that the solution works right of the bat for WPF.
You could try rotating the button by 45 degrees like you did and then adding a TextBlock which is rotated -45 degrees to keep it horizontal. I'm sure there are better ways but this should work.
<Button>
<TextBlock Text="Testing">
<TextBlock.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="-45"/>
<TranslateTransform/>
</TransformGroup>
</TextBlock.RenderTransform>
</TextBlock>
<Button.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="45"/>
<TranslateTransform/>
</TransformGroup>
</Button.RenderTransform>
</Button>

Bind a custom property to a template in WPF

I want to create a template for a button, that will show an image (icon) instead of text.
I want to be able to assign different images to different instances of that button, for example:
<Button Style="{StaticResource iconButton}" ImageSource="Sources/icon.png">
or alternatively:
<Button Style="{StaticResource iconButton}">
<Image Source="...">
</Button>
(but I still want to be able to style the image in the template, so I wouldn't need to worry about color and size when I use it - so simply rendering the image as content won't do).
Example code:
Here I am using the image as a mask to render it in whatever color I want:
<Style TargetType="Button" x:Key="iconButton">
<Style.Resources>
<!-- add the "content to image source" converter-->
<converters:ContentToImageSourceConverter x:Key="ContentToImageSourceConverter"/>
</Style.Resources>
<Setter Property="Foreground" Value="{DynamicResource foreground_color}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid Margin="5">
<Ellipse x:Name="circle" Width="45" Height="45" Fill="{DynamicResource foreground_color}" Opacity="0"/>
<Ellipse Width="45" Height="45" Fill="{DynamicResource foreground_color}">
<Ellipse.OpacityMask>
<!--How to accomplish this?-->
<ImageBrush ImageSource="{TemplateBinding Property=Content "/>
</Ellipse.OpacityMask>
</Ellipse>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="circle" Property="Opacity" Value="0.15"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="circle" Property="Opacity" Value="0.3"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Have a look at the line where it says "how to accomplish this". I am using an image as a source but I want the image to be defined whenever you use the button, for example:
<Button Style="{StaticResource iconButton}" DockPanel.Dock="Right">
<Image Source="/Sources/settings_icon.png"/>
</Button>
I do not want to simply render the image as a content of the button because I'm using it as a mask for something else, thus I can change the foreground color and have the icons render in a different color.
I would do a custom control, with one dependency property of the image source. Have you tried this?
And then you will use it like this
<MyCustomButton DockPanel.Dock="Right" ImageSource = Sources/settings_icon.png">
</MyCustomButton>
The simplest way to do it:
Add UserControl to your project
In xaml just add one button
Add style to this control and set it to the button
Add dependency property, and link it to the image source (in style) by binding. Here you need also to have specified a source of binding.
{Binding RelativeSource={RelativeSource Self}}
Second way:
Make an class which is deriving form the Button class.
Add one dependency property, with the image source object.
In Style Specify a binding for the source property of the image control, it has to have the same name as he dependency property. I think that you will also need to specify the source of the binding as a Ancestor, because it could not be able to find the binding source.
Check here to see the syntax.
Binding to an ancestor in WPF
In both ways the usage will look same.

How to style a TextBox ScrollBar with my own images?

I was wondering if it is possible to insert my own bitmaps for scrollbar styling. I would like to to use an image for up/down arrows, background and scroller. This is what I have to start with, but I just don't know how to access these mentioned properties:
<TextBox Name="SlideNotes" Foreground="White" FontSize="20" FontWeight="Bold" Background="Transparent" BorderThickness="0" TextWrapping="Wrap" AcceptsReturn="True" ScrollViewer.VerticalScrollBarVisibility="Visible" Grid.Row="27" Grid.Column="11" Grid.ColumnSpan="46" Grid.RowSpan="5">
<TextBox.Resources>
<Style TargetType="{x:Type ScrollBar}">
<Setter Property="Background" Value="Red"/>
</Style>
</TextBox.Resources>
I would appreciate any help.
You can use ImageBrush to set an image to any property that uses Brushes (Background, Foreground, BorderBrush, Fill, etc.).
In your case, you'd use it like this:
<TextBox Name="SlideNotes" Foreground="White" FontSize="20" FontWeight="Bold" Background="Transparent" BorderThickness="0" TextWrapping="Wrap" AcceptsReturn="True" ScrollViewer.VerticalScrollBarVisibility="Visible" Grid.Row="27" Grid.Column="11" Grid.ColumnSpan="46" Grid.RowSpan="5">
<TextBox.Resources>
<Style TargetType="{x:Type ScrollBar}">
<Setter Property="Background">
<Setter.Value>
<ImageBrush ImageSource="yourimage.jpg" Stretch="Fill" />
</Setter.Value>
</Setter>
</Style>
</TextBox.Resources>
</TextBox>
ImageBrush (and all other Brushes that inherit from TileBrush) has a set of properties that control how the image is shown. I've used Stretch="Fill" in my sample, which will make the image stretch to fill all the space available, but you could want it to behave differently.
For instance, this...
<ImageBrush ImageSource="yourimage.jpg" Stretch="None" TileMode="FlipXY" />
Will make your image repeat, with tiles alternatively flipped horizontally and/or vertically.
Toy around with Stretch and TileMode (or even ViewPort, ViewPortUnit, Viewbox and ViewboxUnit, if you feel brave enough) to get the effect you want.
EDIT - As with RepeatButton, the default ScrollBar template seems to pretty much ignore the Background property, most of the time, as well as other properties you could use for customization... This means you'll probably have to override the whole template to customize it to your liking.
Here's one ScrollBar template sample: ScrollBar Styles and Templates
And here's another for the RepeatButton: RepeatButton Styles and Templates
Looks like you're after a ControlTemplate. See here for a nice tutorial on how to style one.
To avoid having to style the entire ScrollViewer, you can instead style only the RepeatButton element, this will make things much easier to work with.
<Style TargetType="RepeatButton">
<Setter Property="Background" Value="Red"/>
<Setter Property="Template">
<Setter.Value>
...
</Setter.Value>
</Setter>
</Style>
You may need to define this style in the Window Resources, instead of the TextBox resources due to the scope.
See the documentation for RepeatButton styles and templates.

WPF: Custom tooltip arrow placement

I have a custom tooltip style that basically creates a nice black tooltip with an arrow pointing to the location of the item you hovered over.
The problem is that sometimes the tooltip will not always be placed in the correct location (i.e. near window edges) which means the tooltip arrow no longer points at the correct place... Is there anyway around this problem? Or can I create specific styles for each location placement?
<Style x:Key="{x:Type ToolTip}" TargetType="ToolTip">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="HasDropShadow" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<StackPanel>
<Border CornerRadius="3" HorizontalAlignment="Center" VerticalAlignment="Top" Padding="10,7" BorderThickness="0" Background="#e5323232">
<StackPanel>
<TextBlock FontFamily="Arial" FontSize="12" Text="{TemplateBinding Content}" Foreground="#f0f0f0" />
</StackPanel>
</Border>
<Path Margin="10,0,0,0" Fill="#e5323232" Data="M 0 0 L 6 6 L 12 0 Z"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
Maybe you could try this, I just set the Placement to Center and added a HorizontalOffset to match the arrow you created in the template.
However that wont center it vertically on the control, so you could make an IValueConverter and calculate the size of the control and divide by 2, or you could add a dummy element to your StackPanel that is the same size as the Border, and that should center the ToolTip without needing any code behind
<Style TargetType="{x:Type ToolTip}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="HasDropShadow" Value="True"/>
<Setter Property="Placement" Value="Center" />
<!--Offset to the arrow path-->
<Setter Property="HorizontalOffset" Value="15"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToolTip}">
<StackPanel>
<Border x:Name="border" CornerRadius="3" HorizontalAlignment="Center" VerticalAlignment="Top" Padding="10,7" BorderThickness="0" Background="#e5323232">
<StackPanel>
<TextBlock FontFamily="Arial" FontSize="12" Text="{TemplateBinding Content}" Foreground="#f0f0f0" />
</StackPanel>
</Border>
<Path Margin="10,0,0,0" Fill="#e5323232" Data="M 0 0 L 6 6 L 12 0 Z"/>
<!--Dummy rectangle same height as tool tip, so it centers on the control-->
<Rectangle Height="{Binding ActualHeight, ElementName=border}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The simplest way of doing it is to use a UIElement that exists in the Control Tree as the PlacementTarget of the Tooltip. This will avoid the Silverlight automated positioning when you get near the window edges:
<StackPanel ToolTipService.ToolTip="{Binding Title, Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"
ToolTipService.Placement="Bottom"
ToolTipService.PlacementTarget="{Binding ElementName=LayoutRoot}">
<TextBlock Text="{Binding Title,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
In this case the tooltip will be positioned always at the Origin of the LayoutRoot element. If you have a fixed path size and the PlacementTarget is always at the same position relative to the control for which you want to show the tooltip then this works fine.
If you need to position the Tooltip relatively to the control that triggers the Tooltip than you have to make the Path Data dynamic and calculate the distance to create a new Path Data in the Tooltip control every time the Tooltip is opened.
For this case you have to handle the Tooltip.IsOpened event and implement this logic. If you're using the PlacementTarget than you always know the direction relatively to your control so this makes it easier to calculate the Path vertices.
Another way which works but it's way more complex is to implement your own Popup that shows up when you move the mouse over your control. You would need to implement a few calculations to get the position of the popup relative to the Control, which is exactly what the Tooltip control does for you. The advantage of this is that you have complete control over the positioning of the tooltip and its appearance.

How to use xaml vector images as image source in Windows 8 app

I created some assets in inkscape and would like to use them as icons in a windows 8 application. I have done some reading and it seams that while .Net 4.5 supports SVG, the modern ui profile does not. I converted the svg to xaml using this tool.
I get the following xaml.
<Canvas xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Name="svg2997" Width="744.09448" Height="1052.3622" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Canvas x:Name="layer1">
<Path Fill="#FFCCCCCC" Stroke="#FF000000" StrokeThickness="1.34377062" StrokeMiterLimit="4" x:Name="path3007" Data="M372.58272,134.72445C167.96301,134.72445 2.06820310000001,300.58818 2.06820310000001,505.20789 2.06820310000001,709.8276 167.96301,875.72241 372.58272,875.72241 577.20243,875.72241 743.06616,709.8276 743.06616,505.20789 743.06616,300.58818 577.20243,134.72445 372.58272,134.72445z M280.73888,251.77484L455.94149,251.77484 455.94149,413.70594 628.16035,413.70594 628.16035,588.97071 455.94149,588.97071 455.94149,773.71514 280.73888,773.71514 280.73888,588.97071 106.22005,588.97071 106.22005,413.70594 280.73888,413.70594 280.73888,251.77484z" />
</Canvas>
</Canvas>
If I add this directly to my apps xaml it will render however the scale is way off.
I would like to use this as an image source for an image object if possible.
<Image HorizontalAlignment="Left" Height="100" Margin="127,37,0,0" VerticalAlignment="Top" Width="100" Source="Assets/plus_circle.xaml"/>
Can this be done?
Most AppBar buttons are based on a style included in StandardStyles called AppBarButtonStyle.
To customize the text of the button you set the AutomationProperties.Name attached property, to customize the icon in the button you set the Content property, and it's also a good idea to set the AutomationProperties.AutomationId attached property for accessibility reasons.
Here's an example of a button customized using this approach:
<Style x:Key="FolderButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
<Setter Property="AutomationProperties.AutomationId" Value="FolderAppBarButton"/>
<Setter Property="AutomationProperties.Name" Value="Folder"/>
<Setter Property="Content" Value=""/>
</Style>
As mentioned above, to customize the icon you set the Content property. The challenge is how you set the content so it displays your custom vector art.
It turns out you can place any path Xaml, even yours, into a Viewbox to change its scale. That was my first approach, but it doesn't work. In fact, it seems any time you use Xaml expanded notation to set the Content property for a button it doesn't work.
<Style x:Key="SquareButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
<Setter Property="AutomationProperties.AutomationId" Value="SquareAppBarButton"/>
<Setter Property="AutomationProperties.Name" Value="Square"/>
<Setter Property="Content">
<Setter.Value>
<!-- This square will never show -->
<Rectangle Fill="Blue" Width="20" Height="20" />
</Setter.Value>
</Setter>
I actually think this is a bug, but luckily there is a workaround.
Tim Heuer wrote an excellent article on the simplest way to use a Xaml Path as the artwork for a button. That article is here:
http://timheuer.com/blog/archive/2012/09/03/using-vectors-as-appbar-button-icons.aspx
In short, you need to define a style that sets up all the bindings correctly:
<Style x:Key="PathAppBarButtonStyle" BasedOn="{StaticResource AppBarButtonStyle}" TargetType="ButtonBase">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Path Width="20" Height="20"
Stretch="Uniform"
Fill="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Data="{Binding Path=Content, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
</DataTemplate>
</Setter.Value>
</Setter>
Then you create a style that inherits from that style and you paste in your path. Here is the style for your artwork you listed above:
<Style x:Key="CrossButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource PathAppBarButtonStyle}">
<Setter Property="AutomationProperties.AutomationId" Value="CrossAppBarButton"/>
<Setter Property="AutomationProperties.Name" Value="Cross"/>
<Setter Property="Content" Value="M372.58272,134.72445C167.96301,134.72445 2.06820310000001,300.58818 2.06820310000001,505.20789 2.06820310000001,709.8276 167.96301,875.72241 372.58272,875.72241 577.20243,875.72241 743.06616,709.8276 743.06616,505.20789 743.06616,300.58818 577.20243,134.72445 372.58272,134.72445z M280.73888,251.77484L455.94149,251.77484 455.94149,413.70594 628.16035,413.70594 628.16035,588.97071 455.94149,588.97071 455.94149,773.71514 280.73888,773.71514 280.73888,588.97071 106.22005,588.97071 106.22005,413.70594 280.73888,413.70594 280.73888,251.77484z"/>
</Style>
And finally, you use it in your AppBar like this:
<Button Style="{StaticResource CrossButtonStyle}" />
Dev support, design support and more awesome goodness on the way:
http://bit.ly/winappsupport
I'm pretty positive you can't just inject Path Data into an Image Source and expect it to magically work unless it's through a Drawing Object as Source. What you can however do is adopt your Path into a ContentControl for re-use in the same way without having to go through the trouble of Drawing objects for every instance.
So instead of;
<Image Source="..."/>
Just do something like this and plop it in your Object.Resources or ResourceDictionary;
<Style x:Key="YourThingy" TargetType="ContentControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Path Fill="#FFCCCCCC" Stroke="#FF000000" StrokeThickness="1.34377062" StrokeMiterLimit="4" x:Name="path3007" Data="M372.58272,134.72445C167.96301,134.72445 2.06820310000001,300.58818 2.06820310000001,505.20789 2.06820310000001,709.8276 167.96301,875.72241 372.58272,875.72241 577.20243,875.72241 743.06616,709.8276 743.06616,505.20789 743.06616,300.58818 577.20243,134.72445 372.58272,134.72445z M280.73888,251.77484L455.94149,251.77484 455.94149,413.70594 628.16035,413.70594 628.16035,588.97071 455.94149,588.97071 455.94149,773.71514 280.73888,773.71514 280.73888,588.97071 106.22005,588.97071 106.22005,413.70594 280.73888,413.70594 280.73888,251.77484z" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then just plop it on your view wherever and as many times as you like;
<ContentControl Style="{StaticResource YourThingy}"/>
You will however want to play with that Path of yours. It seems set a large size, but hopefully this provides a good alternative for your circumstance. Cheers!

Categories