Custom Button with a Viewbox inside - c#

I have this style:
<Style TargetType="{x:Type local:ImageButton}" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ImageButton}">
<Border>
<StackPanel Orientation="Horizontal">
<Viewbox x:Name="ViewBoxInternal" Child="{TemplateBinding Child}"/>
<TextBlock x:Name="TextBlockInternal" Text="{TemplateBinding Text}" />
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And I have two dependency properties.
static ImageButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton), new FrameworkPropertyMetadata(typeof(ImageButton)));
ChildProperty = DependencyProperty.Register("Child", typeof(UIElement), typeof(ImageButton), new FrameworkPropertyMetadata());
TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(ImageButton), new FrameworkPropertyMetadata("Button"));
}
I can set the Text property without a problem, but somehow I can't set the Child of my Viewbox.
The Viewbox should receive a Canvas as a Child.
Calling this way, gives me an error:
<custom:ImageButton Text="New" Width="41" Child="{StaticResource NewIcon}"/>
Unable to cast object of 'System.Windows.TemplateBindingExpression' to type 'System.Windows.UIElement'.
Now, working...
As #Xaml-Lover said, you need this:
<Viewbox x:Name="ViewBoxInternal">
<ContentPresenter ContentSource="{TemplateBinding Content}" Width="Auto" Height="Auto"/>
</Viewbox>
And you need to call like this:
<custom:ImageButton Text="New" Content="{StaticResource NewIcon}" />

This is because, the Child property of Viewbox is not a Dependency Property. A binding can only set to a Dependency property. I would suggest you to follow a different approach. Use ContentPresenter as a place holder for Child property and wrap it in a a Viewbox.
<Viewbox x:Name="ViewBoxInternal">
<ContentPresenter ContentSource="Child"/>
</Viewbox>

Related

strange behavior of TemplateBinding

I am trying to create a custom control. After setting the cornerRadius property to be bound it works only for the default radius which is "8". Here is the definition of property:
public CornerRadius cornerRadius
{
get { return (CornerRadius)GetValue(cornerRadiusProperty); }
set { SetValue(cornerRadiusProperty, value); }
}
public static readonly DependencyProperty cornerRadiusProperty =
DependencyProperty.Register("cornerRadius", typeof(CornerRadius), typeof(expandMenuA), new PropertyMetadata(new CornerRadius(8)));
In the main border, everything works fine, but for the second it works only for default "8" when I change a value in different project, which has a reference to this custom control, nothing happens, and it stays the default:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:expandMenuA">
<Style TargetType="{x:Type local:expandMenuA}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:expandMenuA}">
<Border CornerRadius="{TemplateBinding cornerRadius}" x:Name="mainBorder" Background="#232323">
<StackPanel>
<Button Height="{TemplateBinding expanderHeight}" Background="#2d2d2d">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border CornerRadius="{TemplateBinding local:expandMenuA.cornerRadius}" Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Left" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Button.Template>
<Button.Content>
<TextBlock Text="{TemplateBinding menuTitle}" Foreground="White" Margin="10, 0, 0, 0" Background="{TemplateBinding BorderBrush}"/>
</Button.Content>
</Button>
<ContentPresenter/>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
TemplateBinding in ControlTemplate for Button will seek Button's property, which will resolve to default DP value. use Binding with RelativeSource:
<ControlTemplate TargetType="Button">
<Border CornerRadius="{Binding Path=cornerRadius, RelativeSource={RelativeSource AncestorType={x:Type local:expandMenuA}}}" Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Left" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>

Binding to dependency property inside of a style

I need a button with custom style and i would like to have an image on it.
So i have made CustomButton style with ControlTemlate containing StackPanel of TextBlock for text and Rectangle for image, i want to use DrawingImage from my Icons.xaml, it is SVG converted to XAML.
I have also made CustomButton class, it is derived from the regular Button class and has DrawingImageProperty which is dependency property.
In my view i want to create this button with DrawingImage property and then bind to this property in my style, something like this:
<controls:CustomButton DrawingImage="{StaticResource Add}" Content="Add" Style="{StaticResource CustomButton}" />
But unfortunately the button image is missing. I am not sure about binding to the dependency property in the style and also i am not sure about the types, because Drawing property of the DrawingBrush is of type Drawing, but in my Icons.xaml resources i have DrawingImage, couldn't this be also problem?
CustomButton.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Torch.Controls">
<Style x:Key="CustomButton" TargetType="{x:Type controls:CustomButton}">
<Setter Property="Foreground" Value="{StaticResource IconicWhiteBrush}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:CustomButton}">
<Border
Name="Border"
Background="{StaticResource IconicBlueLittleDarkBrush}"
BorderBrush="{StaticResource IconicBlackBrush}"
BorderThickness="1">
<StackPanel Orientation="Horizontal">
<Rectangle Width="20" Height="20">
<Rectangle.Fill>
<DrawingBrush Drawing="{TemplateBinding DrawingImage}" />
</Rectangle.Fill>
</Rectangle>
<TextBlock
Margin="0,0,5,0"
VerticalAlignment="Center"
Text="{TemplateBinding Content}" />
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
CustomButton.cs
public class CustomButton : Button
{
public static readonly DependencyProperty DrawingImageProperty =
DependencyProperty.Register("DrawingImage", typeof(DrawingImage), typeof(CustomButton),
new FrameworkPropertyMetadata(OnDrawingImageChanged));
public DrawingImage DrawingImage
{
get => (DrawingImage)GetValue(DrawingImageProperty);
set => SetValue(DrawingImageProperty, value);
}
private static void OnDrawingImageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((CustomButton)d).DrawingImage = (DrawingImage)e.NewValue;
}
}
Icons.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DrawingImage x:Key="Add">
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V24 H24 V0 H0 Z">
<GeometryDrawing Brush="#FF00ACC1" Geometry="F1 M24,24z M0,0z M19,13L13,13 13,19 11,19 11,13 5,13 5,11 11,11 11,5 13,5 13,11 19,11 19,13z" />
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</ResourceDictionary>
Thank you.
A DrawingImage is not a Drawing, hence
Drawing="{TemplateBinding DrawingImage}"
can not work. Use an Image element instead, and bind its Source property.
<Image Source="{TemplateBinding DrawingImage}"/>
Besides that, the OnDrawingImageChanged callback is pointless. It assigns a value to the property which it already has. You can safely remove it.
I'd also use ImageSource instead of DrawingImage as type of the dependency property. You could use it more flexibly and also assign other image types like BitmapImage.
public static readonly DependencyProperty ImageProperty =
DependencyProperty.Register(nameof(Image), typeof(ImageSource), typeof(CustomButton));
public ImageSource Image
{
get => (ImageSource)GetValue(ImageProperty);
set => SetValue(ImageProperty, value);
}
In the ControlTemplate:
<Image Source="{TemplateBinding Image}"/>

Button font properties ignored when customizing button content

I got the following setup:
<Button x:Name="DeleteFilter" Margin="5" Grid.Column="1" Padding="2">
<StackPanel Orientation="Horizontal">
<Rectangle Height="9" Width="9" Stretch="Fill" Margin="5 3">
<Rectangle.Fill>
<VisualBrush Visual="{StaticResource appbar_delete}"/>
</Rectangle.Fill>
</Rectangle>
<TextBlock Text="{Resx DeleteFilter}"/>
</StackPanel>
</Button>
However, when launching my application I get the following result which is not what I want. All font properties seem to be ignored when I set the Content property of my button manually.
Live example:
I'd like to have the same fontstyle and fontsize as the right button. I tried to specify the style manually by using StaticResource and DynamicResource as follows:
<Button Style="{StaticResource MetroButton}"....
but nothing changed.
I guess that I need to implement a style which overrides the existing one and transfers the formatting to the TextBlock element but I have no clue how to do that.
The working "LOAD FILTERS" button in the right:
<Button x:Name="LoadFilter" Content="{Resx LoadFilters}" Margin="5" Grid.Column="2"/>
MahApps.Metro's standard button (MetroButton) is included in this file.
The style I applied to my icon button:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="MetroIconButton" BasedOn="{StaticResource MetroButton}" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<StackPanel Orientation="Horizontal">
<Rectangle Height="9" Width="9" Margin="5 3" Stretch="Fill">
<Rectangle.Fill>
<VisualBrush Visual="{Binding Tag, RelativeSource={RelativeSource TemplatedParent}}"/>
</Rectangle.Fill>
</Rectangle>
<ContentPresenter/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
In your setup the StackPanel is used as a content which is not so ideal, you may create a style containing the template and the required property setters for font so it remain consistent for the desired buttons across the application.
So if I try to create a button style for you that would be
<Style x:Key="MetroButton" TargetType="{x:Type Button}">
<Setter Property="FontSize" Value="13"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<StackPanel Orientation="Horizontal">
<Rectangle Height="9" Width="9" Margin="5 3" Stretch="Fill">
<Rectangle.Fill>
<VisualBrush Visual="{Binding Tag, RelativeSource={RelativeSource TemplatedParent}}"/>
</Rectangle.Fill>
</Rectangle>
<ContentPresenter/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
then I would use this style on button as
<Button Content="Delete" Style="{StaticResource MetroButton}" Tag="{StaticResource appbar_delete}"/>
Update
leveraging the ContentTemplate to achieve the same while utilizing the existing template.
<Style x:Key="MetroIconButton" BasedOn="{StaticResource MetroButton}" TargetType="{x:Type Button}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate >
<StackPanel Orientation="Horizontal">
<Rectangle Height="9" Width="9" Margin="5 3" Stretch="Fill">
<Rectangle.Fill>
<VisualBrush Visual="{Binding Tag, RelativeSource={RelativeSource FindAncestor, AncestorType=Button}}"/>
</Rectangle.Fill>
</Rectangle>
<ContentControl Content="{Binding}"/>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
usage remain quite same except the style MetroIconButton
<Button Content="Delete" Style="{StaticResource MetroIconButton}" Tag="{StaticResource appbar_delete}"/>
I am using Tag property to hold the icon so it is plug and play for you, but better is to use Attached properties for the same. l:ExtraProperties.Icon={StaticResource appbar_delete}", I can provide a sample if you need that too.
Actually in previous style we override the Template defined in the MetroButton style so it failed. After looking at the original implementation of MetroButton style I come up with the ContentTemplate way to actieve the same. So instead of setting Template we will set the content template which will be picked up by MetroButton style and applied to the content.
Using Attached Properties
declare a class inheriting DependencyObject or any of its derived class along with the desired property as mentioned below
class ExtraProperties: DependencyObject
{
public static Visual GetIcon(DependencyObject obj)
{
return (Visual)obj.GetValue(IconProperty);
}
public static void SetIcon(DependencyObject obj, Visual value)
{
obj.SetValue(IconProperty, value);
}
// Using a DependencyProperty as the backing store for Icon. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IconProperty = DependencyProperty.RegisterAttached("Icon", typeof(Visual), typeof(ExtraProperties), new PropertyMetadata(null));
}
add namespace to your xaml
<Window x:Class="Example.MainWindow"
...
xmlns:l="clr-namespace:Example">
then change the style as
<VisualBrush Visual="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Button}, Path=(l:ExtraProperties.Icon)}"/>
and usage as
<Button Content="Delete" Style="{StaticResource MetroIconButton}" l:ExtraProperties.Icon="{StaticResource appbar_delete}"/>
using Attached properties is more WPF approach, instead of hacking other properties which may not be guaranteed to behave as expected.

WPF content control styling

I have a custom control which is basically a contentcontrol
public class PromoAlarmBox : ContentControl
{
static PromoAlarmBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(PromoAlarmBox), new FrameworkPropertyMetadata(typeof(PromoAlarmBox)));
}
}
I add it to the containing user control
<controls:PromoAlarmBox Grid.Row="9" Grid.Column="1" />
If I add the style to the containing usercontrols resources everything works fine
<UserControl.Resources>
<Style TargetType="{x:Type controls:PromoAlarmBox}">
<Setter Property="ContentControl.ContentTemplate">
<Setter.Value>
<DataTemplate >
<Rectangle Fill="Blue" Stroke="Black" Height="20" Width="20"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
But if I add it to generic.xaml in the custom controls project , nothing is show
<Style TargetType="{x:Type local:PromoAlarmBox}">
<Setter Property="ContentControl.ContentTemplate">
<Setter.Value>
<DataTemplate >
<Rectangle Fill="Blue" Stroke="Black" Height="20" Width="20"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
I know the style is applied as I have other controls in same project whos styles are defined in generic.xaml, Anyone have any ideas?
A simple static should do the trick...
static PromoAlarmBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(PromoAlarmBox), new FrameworkPropertyMetadata(typeof(PromoAlarmBox)));
}
Although im not sure why there is a difference when you use a style as a local resource and when you use generic , this works for me
<Style TargetType="{x:Type local:PromoAlarmBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Rectangle VerticalAlignment="Stretch" Fill="Yellow" Stroke="Black" Height="20" Width="20"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

How to bind polygon points in specific style at runtime wpf ,windows?

I have this code for style
<Window.Resources>
<Style x:Key="Mystyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid >
<Polygon Name="poly" Points="0,0 0,100 50,200"
Fill=" TemplateBinding Background}"
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Button Name="b1" Style="{StaticResource Mystyle }"></Button>
</Grid>
I need to set points for polygon in style tag not static but dynamic based on some calculation how can i bind points to polygon.
example I have this
PointCollection pc=this.CalculatePolygonPoints(new Point(0,0), 100,Orientation.Flat);
How can i bind this pc to my polygon dynamic
Why dont you just create new dependency property(of a type PointCollection?) and bind Polygon's Points property against it? Then do your calculations when needed(#code-behind), and UI will automatically understand.
Here is an example(using ObservableCollection); http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/1293d4cb-87b3-4cdc-97e3-ae2f41caf2d4/

Categories

Resources