WPF variable custom button shape - wpf

I'm trying to desinge a Template for a Button, which addapts the shape of the Button. I want to give the shape as Attribute to the control. Like this:
<Button Style="{StaticResource MyStyle}">
<Button.Shape>
<ImageBrush ImageSource="MyImage"/>
</Button.Shape>
</Button>
I tried to use the OpacityMask-Attribute of the Button, but somehow I coulden't specify in the template on which element I want to apply the mask.
<ControlTemplate x:Key="Test" TargetType="Button">
<Grid Background="{TemplateBinding Background}"
SnapsToDevicePixels="True">
<Rectangle Fill="{TemplateBinding Foreground}" OpacityMask"{TemplateBinding OpacityMask}">
</Rectangle>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="#aaff00ff"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
I already tried something like this:
<Button Style="{StaticResource MyStyle}">
<Button.OpacityMask>
<ImageBrush ImageSource="MyImage"/>
</Button.OpacityMask>
</Button>
But then i can't change the background anymore. I know, I could put every button in a Grid with the background I want, but I try to learn the concept of Templates and I wonder if there is a more elegant solution. :)

Related

Make ErrorTemplate in Style on ComboBox allow tooltip but not mouse click

I use telerik, but that should not mean much for this question. My application is WPF (.Net 4.5).
I have a style, that I use for all comboboxes, which has an errortemplate. The style looks like this:
<Style TargetType="{x:Type telerik:RadComboBox}" x:Key="RadComboBoxStyle" >
<Setter Property="FontFamily" Value="Calibri"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Background" Value="{StaticResource InputBrush}" />
<Setter Property="BorderBrush" Value="{StaticResource InputBorderBrush}" />
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource RadComboBoxValidationErrorTemplate}" />
</Style>
My ErrorTemplate looks like this:
<ControlTemplate TargetType="{x:Type Control}" x:Key="RadComboBoxValidationErrorTemplate">
<Grid ToolTipService.IsEnabled="True" ToolTipService.ShowOnDisabled="True"
ToolTip="{Binding ElementName=customAdorner, Path=AdornedElement.(Validation.Errors), Converter={StaticResource ValidationErrorsConverter}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border BorderBrush="{StaticResource ErrorBrush}" BorderThickness="3" Panel.ZIndex="999" HorizontalAlignment="Right" Margin="0,0,10,0"
Background="Transparent" DockPanel.Dock="right" Width="16" Height="16" CornerRadius="10">
<Rectangle Fill="{StaticResource ErrorBrush}" HorizontalAlignment="Stretch" VerticalAlignment="Center" Height="3" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform Angle="315" />
</Rectangle.RenderTransform>
</Rectangle>
</Border>
<AdornedElementPlaceholder Name="customAdorner" VerticalAlignment="Center" >
<Border BorderBrush="{StaticResource ErrorBrush}" BorderThickness="1" />
</AdornedElementPlaceholder>
</Grid>
</ControlTemplate>
The entire thing is defined in a global ResourceDictionary.
What this code does, it to put a "forbidden sign" on top of the combobox (Panel.ZIndex="999"), just before the dropdown button (using margins). The Border and the Rectangle makes a sign much like this: Picture.
The combobox itself must be able to hold a tooltip, set locally. So the error-message cannot be shown in the tooltip of the combobox - unless I find a way to combine the two without having to resolve it locally (I want that code in my resourcedictionary).
I also do not want the "forbidden sign" to handle mouse clicks (it gobbles up the click and prevent the combobox from dropping down, if the user clicks on the "forbidden sign".
I tried setting IsHitTestVisible to false on the grid and on the border inside the ErrorTemplate, but that caused the Tooltip to never show.
I also tried setting IsEnabled to false on the same two constrols, but that would not send the mouseclick on to the combobox itself (the list in the combobox does not drop down).
Is there any way to do this directly in the combobox-style or errortemplate? I do not even mind having a code behind - but I really do not want to add code locally where the combobox-style is used.

Stop button color change on Mouseover

ok..this is very strange....i have defined a button, actually 5 buttons and each have a different color but on mouse over, they just change the color to that icy blue color....i tried to override that by using the below code :
<Button Name="btn1" Content="Button" Width="65" Height="45" Background="Green" Margin="1,1,0,1" FontWeight="Bold">
<Button.Style>
<Style>
<Style.Triggers>
<Trigger Property="Button.IsMouseOver" Value="True">
<Setter Property="Button.Background" Value="Yellow" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
<Button.LayoutTransform>
<RotateTransform Angle="270"/>
</Button.LayoutTransform>
</Button>
but it still does not work...what i want is that it should maintain the background color it has been given(different for each button), so the question is : do i have to define it again and again for each button (the trigger did not work, color becomes ice blue) OR can i define it in the resource file with a generic value that it either stops the color change or just sets background to existing property
EDIT : Just to be clear, i want the button to stop changing its color on mouseover and just retain whatever color i have assigned it.....
Based on the comments and the SO post I linked: that article applied the style to anything of type Button, that's why it applies to all buttons (which is not necessarily a bad thing). But the salient point to take away from that article is that what you need to modify is the ControlTemplate.
So you would want to do something like this:
<Style x:Key="ButtonStyle1" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="Chrome" BorderBrush="Black" BorderThickness="2" CornerRadius="2" Background="{TemplateBinding Property=Background}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
This isn't perfect, because it loses all of it's interactivity (ie. you can't tell you've pushed it, hovered over it or anything). But it gives you a good starting point.
One of the key points here is that the Background property of the Border is bound to the TemplateParent Property=Background, that means it's going to read the background property of the button the that it is templating and use it's background color. This makes it easier for you to use a single style to cover all 5 of your buttons. You could (and may want to) do similar things with the BorderBrush property.
Also notice my style has a x:Key value, so in order to use this, you would do:
<Button x:Name="btn1" Content="Button" Width="65" Height="45" Background="Green" Margin="1,1,0,1" FontWeight="Bold" Style="{DynamicResource ButtonStyle1}">
<Button.LayoutTransform>
<RotateTransform Angle="270"/>
</Button.LayoutTransform>
</Button>
You can remove the x:Key attribute and the style will indeed apply to all buttons inside of whichever container you declare the resource.

Canvas with Children as a Style

Sorry if this is a dense question, but I'm looking to see if there is a way to have a style resource of Canvas in App.xaml and also have children on that Canvas and just refer to it in the Style of another canvas. The resource I envision would be something like:
<Style x:Key="Background1" TargetType="Canvas">
<Setter Property="Width" Value="500"/>
<Setter Property="Height" Value="600" />
<Setter Property="Background" Value="Red"/>
<Setter Property="Children">
<Setter.Value>
<Rectangle Canvas.Top="20" Canvas.Left="20" Width="100" Height="100" Fill="Yellow"></Rectangle>
</Setter.Value>
</Setter>
</Style>
And then calling it would be as simple as:
<Canvas Style="{StaticResource Background1}" x:Name="CanvasRoot" >
<Rectangle x:Name="PageRectangle" Canvas.Left="114" Canvas.Top="84" Height="378" Width="210" Stroke="#92D050" Fill="#C0504D" />
</Canvas>
Any thoughts on if something like this can be done. What I've done doesn't work because there is no "Children" property on Canvas.
So you want to add a child to a Canvas via a Style? I'm afraid this is simply not possible. Styles allows you to set the values of an elements dependency properties, such as Height, Background, Stroke etc ... You can also use them to set attached properties. However, the Children property you are trying to set is not a dependency property, it is the collection of children elements that describe the children of your canvas in the visual tree.
The only way to add new elements t the visual tree using styles is to add them to some controls template. Unfortunately you canot template panels (Canvas, Grid, StackPanel). You could use a ContentControl and add your rectangle as part of its template.
A ContentControl is a lookless container of a single child. See the template described here:
http://msdn.microsoft.com/en-us/library/dd334411%28VS.95%29.aspx
Here it is, templates to add a rectangle. I am not sure what layout you are trying to achieve, but it should give you the general idea
<Style TargetType="ContentControl" x:Key="myContentControl">
<Setter Property="Foreground" Value="#FF000000"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="VerticalContentAlignment" Value="Top"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Canvas>
<Rectangle Canvas.Top="20" Canvas.Left="20" Width="100" Height="100" Fill="Yellow"></Rectangle>
<ContentPresenter
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Cursor="{TemplateBinding Cursor}"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
<ContentControl Style={StaticResource myContentControl}>
<Canvas x:Name="CanvasRoot" >
<Rectangle x:Name="PageRectangle" Canvas.Left="114" Canvas.Top="84" Height="378" Width="210" Stroke="#92D050" Fill="#C0504D" />
</Canvas>
</ContentControl>

WPF Image Command Binding

I'm putting a WPF application together in which I have an image control which I want to bind a custom command object to from my view model that will execute when the image is clicked. I have exposed the command object from my view model and just need to bind it to the image control.
Is it possible to bind this command object to an image control? If so any advice would be appreciated.
Here's yet another solution I personally love to use most of the time if I want an image with command without enclosing it in another control.
<Image Source="Images/tick.png" Cursor="Hand" Tooltip="Applies filter">
<Image.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{Binding ApplyFilter, Mode=OneTime}" />
</Image.InputBindings>
</Image>
I set its properties Hand and Tooltip so that it's more clear that it's an action and not a static image.
You need to put the image in a button, and bind the button to the command:
<Button Command="{Binding MyCommand}">
<Image Source="myImage.png" />
</Button>
If you don't want the standard button chrome, just change the template of the button with something like that:
<ControlTemplate x:Key="tplFlatButton" TargetType="{x:Type Button}">
<Border Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
TextElement.Foreground="{TemplateBinding Foreground}"
TextElement.FontFamily="{TemplateBinding FontFamily}"
TextElement.FontSize="{TemplateBinding FontSize}"
TextElement.FontStretch="{TemplateBinding FontStretch}"
TextElement.FontWeight="{TemplateBinding FontWeight}"/>
</Border>
</ControlTemplate>
Note that you will also need to change other properties to override the default button style, otherwise the template above will use the default button background and border:
<Style x:Key="stlFlatButton" TargetType="{x:Type Button}">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Template" Value="{StaticResource tplFlatButton}" />
</Style>
It can be simpler to avoid using a button and use a Hyperlink instead:
<TextBlock DockPanel.Dock="Top">
<Hyperlink Command="{Binding SomeCommand}">
<Image Source="image.png" />
</Hyperlink>
</TextBlock>
Note that this will render the hyperlink with the default text decoration, so you'll want to add a style that removes that - putting this in the resource dictionary of the containing element will do the trick:
<Style x:Key={x:Type Hyperlink}" TargetType="Hyperlink">
<Setter Property="TextDecorations"
Value="{x:Null}" />
</Style>
Simplified version of answer from #Robert Rossney:
<TextBlock>
<Hyperlink Command="{Binding SomeCommand}" TextDecorations="{x:Null}">
<Image Source="{StaticResource image.png}" Width="16" />
</Hyperlink>
</TextBlock>
The best way to include an image is to use a {StaticResource x}, see WPF image resources
reset control template of the button and use image in control template..
<Button Width="100" Height="100" Command="{Binding SampleCommand}">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Image Stretch="Uniform" Source="Images/tick.png"></Image>
</ControlTemplate>
</Button.Template>
</Button>

How to make a simple hyperlink in XAML?

All I want to do is make a little hyperlink in XAML. I've tried everything. I give up.
What is the syntax for this?
<StackPanel Width="70" HorizontalAlignment="Center">
<Hyperlink Click="buttonClose_Click" Cursor="Hand"
Foreground="#555" Width="31" Margin="0 0 0 15"
HorizontalAlignment="Right">Close</Hyperlink>
<Button Width="60" Margin="0 0 0 3">Test 1</Button>
<Button Width="60" Margin="0 0 0 3">Test 2</Button>
<Button Width="60" Margin="0 0 0 3">Test 3</Button>
<Button Width="60" Margin="0 0 0 3">Test 4</Button>
</StackPanel>
Visual Studio Team: In Visual Studio 2010 I want Clippy to pop up and say "It seems you are trying to make a hyperlink" and tell me how to do it. Can't you do that with MEF? It would be retro cool, and these little "how do I do what I already know how to do in HTML" issues burn up so much time during the learning process with XAML.
You can't add a Hyperlink to a StackPanel -- you'll get a runtime error. (Actually, I'm kinda surprised it's not a compile-time error.) That's because Hyperlink doesn't live in the "controls" side of WPF with <Button> and <StackPanel> and other things that are laid out on rectangular chunks of screen and descend from UIElement. Instead, it lives in the "text" side of things, with <Bold> and <Run> and <Paragraph> and other generally texty things that word-wrap and flow in lines and paragraphs and descend from TextElement.
Once you realize that there are two separate class hierarchies with different layout behaviors, it makes sense that Hyperlink would be on the "text" side of things (makes it easy to e.g. have a paragraph with a hyperlink in the middle, and even for that hyperlink to wrap across a line break).
But no, it's not so discoverable when you're starting out.
To mix the two worlds, and use a hyperlink as a control, all you need to do is put it in a TextBlock. TextBlock is a control-ish thing (i.e., can go in a StackPanel) that contains text-ish things (i.e., can contain a Hyperlink):
<TextBlock><Hyperlink Click="buttonClose_Click">Close</Hyperlink></TextBlock>
You can use a Button with a custom control template, the code below is a limited hyperlink style button (for example it only support textual hyperlinks) but maybe it'll point you in the right direction.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<Style x:Key="Link" TargetType="Button">
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Foreground" Value="Blue"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<TextBlock TextDecorations="Underline"
Text="{TemplateBinding Content}"
Background="{TemplateBinding Background}"/>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Foreground" Value="Red"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<Button Content="Click Me!" Style="{StaticResource Link}"/>
</Page>
Try this:
<TextBlock>
<Hyperlink RequestNavigate="Hyperlink_RequestNavigate"
NavigateUri="http://www.msn.com">MSN</Hyperlink>
</TextBlock>
private void Hyperlink_RequestNavigate(object sender,
System.Windows.Navigation.RequestNavigateEventArgs e)
{
System.Diagnostics.Process.Start(e.Uri.AbsoluteUri);
}
<TextBlock>
<Hyperlink NavigateUri="{Binding YourUri}" RequestNavigate="YourRequestNavigate">
<TextBlock Text="{Binding YourText}" />
</Hyperlink>
</TextBlock>
This will linkify any binded text in the nested textblock, i have not found a better way yet, i would like the first textblock to not be there if possible.
This will work for DataTemplates aswell.
You may find that if you're binding to anything other than simple text values you will need to use ContentPresenter otherwise nothing will appear, this could be true if you're binding to an XML data source.
A Property Trigger for IsMouseOver gives the text an underline.
An example where I"m binding to XML is presented below.
<Style x:Key="JobNumberStyleButton" TargetType="{x:Type Button}">
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<TextBlock>
<ContentPresenter
Margin="0,0,0,0"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
ContentStringFormat="{TemplateBinding ContentStringFormat}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
RecognizesAccessKey="False"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<TextBlock Padding="0,0,0,0" Margin="0,0,0,0">
<Underline>
<ContentPresenter
Margin="0,0,0,0"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
ContentStringFormat="{TemplateBinding ContentStringFormat}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
RecognizesAccessKey="False"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Underline>
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
You can simply use HyperlinkButton.
When it is clicked on, the URL will be displayed in your web browser:
<HyperlinkButton
NavigateUri="https://dev.windowsphone.com"
TargetName="_blank"
Content="Windows Phone Dev Center" />
Usually, the meaning of an Hyperlink is to give an anchor to send the user to another Page or generally speaking to another resource, so it's implemented in such a way and you have to specify the location for that resource like this:
<HyperLink NavigateUri="http://www.site.com">
Web Site
</HyperLink>
However, i've found this blog post with a custom TextBlock that is used as an HyperLink and supports click events.
in UWP with mvvmcross i'm using this
<HyperlinkButton Content="{Binding TextSource, ConverterParameter=MyUrl, Converter={StaticResource Language},
FallbackValue=_MyUrl}" NavigateUri="http://www.google.com" />

Resources