Creating Button using GeometryDrawing WPF - wpf

Hi I am trying to create a Button with a semi arc form something like this:
alt text http://www.freeimagehosting.net/uploads/6f804323db.jpg
I am using xaml and Control templates, it works fine but, the button recives the click event even if i click in any part into the rectangle form by the geometry, it launch the click event, I want the event to be catch only inside the geomtry...
here is the xaml
<ControlTemplate x:Key="ButtonTemplate" TargetType="{x:Type Button}">
<Grid>
<Image>
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<GeometryDrawing x:Name="X"Geometry= "M 0,0
A .8,.8 180 1 1 0,4
L 0,3
A .6,.6 180 1 0 0,1
L 0,0">
<GeometryDrawing.Pen>
<Pen Brush="Black" Thickness=".1" />
</GeometryDrawing.Pen>
<GeometryDrawing.Brush>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0" Color="Blue"/>
<GradientStop Offset="1" Color="Red"/>
</LinearGradientBrush>
</GeometryDrawing.Brush>
</GeometryDrawing>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
<Viewbox>
<ContentControl Margin="20" Content="{TemplateBinding Content}"/>
</Viewbox>
</Grid>
</ControlTemplate>
...
<Button Template="{StaticResource ButtonTemplate}" Click="Button_Click" HorizontalAlignment="Right">OK</Button>
Thanks for any Comments

Instead of using an Image, which will have rectangular bounds that are used for hit testing, you can use a Path element with your Geometry data. The Path will only do hit testing on the area defined by the outline. Whatever text or other content is set will also be clickable unless you set IsHitTestVisible="false" on the ContentPresenter.
<Button Content="OK">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Path Data="M 0,0
A .8,.8 180 1 1 0,4
L 0,3
A .6,.6 180 1 0 0,1
L 0,0" Stroke="Black" StrokeThickness="1" Stretch="Uniform">
<Path.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0" Color="Blue"/>
<GradientStop Offset="1" Color="Red"/>
</LinearGradientBrush>
</Path.Fill>
</Path>
<Viewbox>
<ContentPresenter Margin="20" />
</Viewbox>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>

Related

xaml wpf - Gradient Brush resource doesn't work correctly

I'm already desperate to find the answer why this happens... here's my code:
ResourceDictionary
<Color x:Key="ControlStrokeColorDefault">#0F000000</Color>
<Color x:Key="ControlStrokeColorSecondary">#29000000</Color>
<LinearGradientBrush x:Key="ControlElevationBorderBrush" MappingMode="Absolute" StartPoint="0,0" EndPoint="0,3">
<LinearGradientBrush.RelativeTransform>
<ScaleTransform CenterY="0.5" ScaleY="-1" />
</LinearGradientBrush.RelativeTransform>
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0.33" Color="{DynamicResource ControlStrokeColorSecondary}" />
<GradientStop Offset="1.0" Color="{DynamicResource ControlStrokeColorDefault}" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
Panel
<Grid Background="#29000000">
<WrapPanel Background="#F7F7F7" VerticalAlignment="Center" HorizontalAlignment="Center">
<Border Margin="20" Width="100" Height="60" BorderThickness="2" BorderBrush="{DynamicResource ControlElevationBorderBrush}">
<TextBlock>Ok</TextBlock>
</Border>
<Border Margin="20" Width="100" Height="100" BorderThickness="2" BorderBrush="{DynamicResource ControlElevationBorderBrush}">
<TextBlock>???</TextBlock>
</Border>
</WrapPanel>
</Grid>
As a Result, I get that the gradient is calculated only on the first element... is this a wpf bug or am I missing something?
this is because of ScaleY="-1" in ScaleTransform
Scale creates different sizes at different heights

Why do I have to press tab twice to get to textbox with this control template?

I have created a ControlTemplate for a TextBox that includes a label for it. However, When I try to use Tab to navigate the controls I have to press tab twice to enter the textbox field, as if it's focusing on another element inside. I tried messing the labels focusability and what not but that doesn't seem to be the issue. Here is the code:
<ControlTemplate x:Key="custTextbox" TargetType="{x:Type TextBox}">
<Canvas x:Name="customTextbox">
<Border CornerRadius="3, 0, 0 ,3" BorderThickness="1, 1, 0, 1"
Height="30" x:Name="brdTextboxLabel" Width="98">
<Border.BorderBrush>
<LinearGradientBrush StartPoint=".5,0" EndPoint=".5,1">
<GradientStop Color="#3C3F48" Offset=".88"/>
<GradientStop Color="#9CA1A8" Offset=".96"/>
</LinearGradientBrush>
</Border.BorderBrush>
<Border.Background>
<LinearGradientBrush StartPoint=".5, 0" EndPoint=".5, 1">
<GradientStop Color="#414447" Offset="0"/>
<GradientStop Color="#4E525B" Offset=".08"/>
</LinearGradientBrush>
</Border.Background>
<Canvas>
<Rectangle Height="24" x:Name="rectangle3" Stroke="#636369"
Width="1" Canvas.Left="96" Canvas.Top="2" />
<Label Canvas.Left="0" Padding="9,6.5,0,0" Foreground="#BABBBF"
FontWeight="Bold" Canvas.Top="0" FontSize="11"
Content="{TemplateBinding Tag}" Height="28"
x:Name="lblTextboxHeader" Width="92" />
</Canvas>
</Border>
<!-- ========================================= -->
<Border CornerRadius="0,3,3,0" BorderThickness="0,1,1,1" Canvas.Left="98"
Height="30" x:Name="brdTextbox" Width="348">
<Border.BorderBrush>
<LinearGradientBrush StartPoint=".5,0" EndPoint=".5,1">
<GradientStop Color="#3C3F48" Offset=".88"/>
<GradientStop Color="#9CA1A8" Offset=".96"/>
</LinearGradientBrush>
</Border.BorderBrush>
<Border.Background>
<LinearGradientBrush StartPoint=".5, 0" EndPoint=".5, 1">
<GradientStop Color="#414447" Offset="0"/>
<GradientStop Color="#4E525B" Offset=".08"/>
</LinearGradientBrush>
</Border.Background>
<Canvas>
<TextBox TabIndex="0" Background="Transparent" CaretBrush="#8C8CA1"
FontSize="16" Padding="4, 3, 0 ,0" BorderBrush="Transparent"
Foreground="#D4D5DA" Canvas.Left="0" Canvas.Top="-1"
Height="30" x:Name="textBox1" Width="347"/>
</Canvas>
</Border>
</Canvas>
</ControlTemplate>
Sorry if it's a mess of a ControlTemplate, it was the first I had ever made when starting wpf/xaml.
Thank you for your time!
HA! Figured it out while solving an issue on another control.
The problem was I, essentially, had 2 TextBoxes each time I used the template.
a simple map would be:
<TextBox>
<ControlTemplate>
<Label/>
<Textbox/>
</ControlTemplate>
</TextBox>
So I just had to make the control I put in the window have KeyboardNavigation.IsTabStop="false" So it would pass that textbox and go to the one inside my ControlTemplate.

WPF binding issues with resource dictionaries

I have developed a custom data grid control. This control has two parts, one is the custom template used by the data grid itself and the other is my custom control which adds a title area to the data grid.
I have a class called CustomDataGrid, here is the XAML.
<UserControl x:Class="MDT_Designer.Presentation.ScreenControls.CustomDataGrid"
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"
Name="CDataGrid" >
<UserControl.Resources>
<ResourceDictionary x:Key="Dictionary" Source="CustomizedControls.xaml"/>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="{Binding ElementName=CDataGrid, Path=TitleAreaHeight}"/>
<RowDefinition Height="{Binding ElementName=CDataGrid, Path=GridAreaHeight}"/>
</Grid.RowDefinitions>
<Grid Name="TitleArea" DockPanel.Dock="Top" Background="{Binding ElementName=CDataGrid, Path=TitleBackColor}" >
<Viewbox HorizontalAlignment="{Binding ElementName=CDataGrid, Path=TitleAlignment}">
<TextBlock Name="TitleText" Text="{Binding ElementName=CDataGrid, Path=Title}" Margin="2,0,0,0" FontFamily="{Binding ElementName=CDataGrid, Path=TitleFontFamily}" Foreground="{Binding ElementName=CDataGrid, Path=TitleTextColor}"/>
</Viewbox>
</Grid>
<DataGrid Name="DataGridArea" Grid.Row="1" IsReadOnly="True" HorizontalGridLinesBrush="DarkGray" VerticalGridLinesBrush="DarkGray" VerticalScrollBarVisibility="Visible" Background="AliceBlue" >
</DataGrid>
</Grid>
</UserControl>
In my CustomDataGrid.xaml.cs file I have defined properties as such.
public static DependencyProperty TitleAlignmentProperty = DependencyProperty.Register("TitleAlignment", typeof(HorizontalAlignment), typeof(CustomDataGrid));
public HorizontalAlignment TitleAlignment
{
get { return (HorizontalAlignment)base.GetValue(TitleAlignmentProperty); }
set { base.SetValue(TitleAlignmentProperty, value); }
}
I am just showing one as a sample, and this works. I do wish that I could change the binding in the XAML to get rid of ElementName=CDataGrid, but any way I have as an alternative doesn't work.
Now to my problem. My custom template for the data grid control itself is stored in the dictionary CustomizedControls.xaml.
Here is one example of binding issues. I had to create this resource.
<LinearGradientBrush x:Key="HeaderBackgroundBrush" EndPoint="0.5,1" StartPoint="0.5,0" >
<GradientStop Color="{DynamicResource {x:Static SystemColors.ControlLightColorKey}}" Offset="0" />
<GradientStop Color="{DynamicResource {x:Static SystemColors.ControlDarkColorKey}}" Offset="1" />
</LinearGradientBrush>
Then I use it in Border definition for the column header.
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
<Grid>
<Border x:Name="columnHeaderBorder" BorderThickness="1" Padding="3,0,3,0"
Background="{DynamicResource HeaderBackgroundBrush}">
<Border.BorderBrush>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="{DynamicResource BorderLightColor}" Offset="0" />
<GradientStop Color="{DynamicResource BorderDarkColor}" Offset="1" />
</LinearGradientBrush>
</Border.BorderBrush>
<ContentPresenter x:Name="columnHeaderPresenter" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Style="{DynamicResource HeaderContentStyle}" />
</Border>
<Thumb x:Name="PART_LeftHeaderGripper" HorizontalAlignment="Left" Style="{StaticResource ColumnHeaderGripperStyle}" />
<Thumb x:Name="PART_RightHeaderGripper" HorizontalAlignment="Right" Style="{StaticResource ColumnHeaderGripperStyle}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then to change it in my code behind I have to do this.
LinearGradientBrush headerBrush = (LinearGradientBrush)TryFindResource("HeaderBackgroundBrush");
headerBrush.GradientStops[0] = new GradientStop(brush.Color, 0.0);
I cannot change the brush itself, I can just change a property of the brush. I have tried every binding technique I can think of, but nothing seems to work when the style is in a dictionary.
What I would like to be able to do is somehow bind something like BorderLightColor, which is BorderBrush setting, so that in my code behind I can change it's value.
Any help would make my day, it would make my whole weekend in fact. Thanks.
Why cant you change the brush? You should be able to just assign the brush reference to the control. Does this not work? FindResource should search the Parent Element of the FrameworkElement you are calling it on and up the tree, the application resources, then Themes ending in System Resources. Forgive my VB...
Private Sub Button_Click(ByVal sender as Object, ByVal e as System.Windows.RoutedEventArgs)
Dim MyBrush2 as Brush = Me.FindResource("Brush2")
Me.MyRect.Background = MyBrush2
End Sub
And the window contents:
<Window.Resources>
<LinearGradientBrush x:Key="Brush1" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="Brush2" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF0800FF" Offset="0"/>
<GradientStop Color="Red" Offset="1"/>
</LinearGradientBrush>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<Grid x:Name="MyRect" HorizontalAlignment="Left" Margin="63,66,0,174" Width="242" Background="{DynamicResource Brush1}" />
<Button Content="Button" HorizontalAlignment="Right" Height="60" Margin="0,117,95,0" VerticalAlignment="Top" Width="156" Click="Button_Click"/>
</Grid>

WPF Rectangle filled with horizontal or vertical lines

I am trying to create one rectangle filled with Horizontal or Vertical lines.
The width of the rectangle is dynamic so I can not use an image brush.
Please let me know if anybody knows any solution.
I figured out a straight-forward way of doing this; finally, I used following visual brush resources to fill rectangle with horizontal, vertical or dotted vertical lines respectively
<!--for horizontal lines-->
<VisualBrush
x:Key="HorizontalLines"
TileMode="Tile" Viewport="0,0,4,4"
ViewportUnits="Absolute" Viewbox="0,0,10,10"
ViewboxUnits="Absolute">
<VisualBrush.Visual>
<Canvas>
<Path Stroke="Black" Data="M 0 10 l 10 0" />
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
<!--For vertical lines-->
<VisualBrush
x:Key="VerticalLines"
TileMode="Tile" Viewport="0,0,4,4"
ViewportUnits="Absolute" Viewbox="0,0,10,10"
ViewboxUnits="Absolute">
<VisualBrush.Visual>
<Canvas >
<Path Stroke="Black" Data="M 0 0 l 0 10" />
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
<!--For dotted vertical lines-->
<VisualBrush
x:Key="DottedVerticalLinesWithFill"
TileMode="Tile" Viewport="0,0,10,10"
ViewportUnits="Absolute" Viewbox="0,0,10,10"
ViewboxUnits="Absolute">
<VisualBrush.Visual>
<Canvas>
<Path Stroke="Purple" Data="M 0 5l 0 -10" />
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
You can easily do this with a LinearGradientBrush:
<Rectangle Width="100" Height="100">
<Rectangle.Fill>
<LinearGradientBrush SpreadMethod="Reflect" StartPoint="0 0" EndPoint="0 0.05">
<GradientStop Offset="0.5" Color="Black"/>
<GradientStop Offset="0.5" Color="White"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
You control line thickness and orientation with the EndPoint property.
Have you tried using a TileBrush?
ImageBrush derives from TileBrush so you can use the Viewport property to repeat the image. See this MSDN page for an example.

How to modify Silverlight template at runtime?

I have a custom control containing a path that has a templated tooltip. I want to be able to get a reference to a grid in the template at runtime so that I can modify it's children depending on use.
I thought I could use GetTemplateChild to get a reference to the grid in the template from within the OnApplyTemplate method of the control but this method is not firing.
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_tooltipDetails = (Grid)GetTemplateChild("TooltipDetailsGrid");
}
How else might I be able to do this?
Here is the user control's XAML.
<UserControl
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" x:Class="MiX.AssetManagement.UI.Timeline.Silverlight.TimelineBarRibbonItem"
FontSize="9.333" Foreground="White" mc:Ignorable="d">
<UserControl.Resources>
<ControlTemplate x:Key="ToolTipControlTemplateTimelineView" TargetType="ToolTip">
<StackPanel Orientation="Horizontal" Margin="16,0,0,0">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="OpenStates">
<VisualState x:Name="Closed"/>
<VisualState x:Name="Open"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border CornerRadius="4">
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#7F000000" Offset="0"/>
<GradientStop Color="#B2000000" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
<Grid Margin="4">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Border x:Name="Info" Height="16" Width="16" BorderThickness="2" CornerRadius="8" HorizontalAlignment="Left" VerticalAlignment="Top">
<Border.BorderBrush>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="#7FFFFFFF" Offset="1"/>
</LinearGradientBrush>
</Border.BorderBrush>
<Path Fill="White" Stretch="Fill" Margin="5.229,2.089,5.035,2.82" RenderTransformOrigin="0.5,0.5" UseLayoutRounding="False" Data="M0.77471197,5.0623446 L2.4198356,5.0623446 L2.4198356,10.18 L0.77471197,10.18 z M0.72914064,3.0891075 L2.4654069,3.0891075 L2.4654069,4.3332038 L0.72914064,4.3332038 z">
<Path.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Path.RenderTransform>
</Path>
</Border>
<ContentPresenter d:LayoutOverrides="Width, Height" Margin="20,0,0,0"/>
<Grid Grid.Column="1" x:Name="TooltipDetailsGrid">
<TextBlock Text="Tooltip in a Grid"/>
</Grid>
</Grid>
</Border>
</StackPanel>
</ControlTemplate>
</UserControl.Resources>
<Path x:Name="RibbonItem" Cursor="Hand">
<Path.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF66CC33"/>
<GradientStop Color="#FF339900" Offset="1"/>
</LinearGradientBrush>
</Path.Fill>
<ToolTipService.ToolTip>
<ToolTip x:Name="RibbonItemTooltip" Content="" Foreground="#FFFFFFFF" FontSize="9.333" Placement="Mouse" Template="{StaticResource ToolTipControlTemplateTimelineView}"/>
</ToolTipService.ToolTip>
<Path.Data>
<GeometryGroup x:Name="RibbonItemGeometryGroup">
<RectangleGeometry x:Name="RibbonItemBackground" />
</GeometryGroup>
</Path.Data>
</Path>
</UserControl>
You need to be handling code in the ToolTip Classes OnApplyTemplate method. Here is my untested stab at what you need to do:-
Inherit from ToolTip and override the OnApplyTemplate method in this new class:-
public MyToolTip : ToolTip
{
public override void OnApplyTemplate()
{
//Perhaps to stuff
base.OnApplyTemplate();
//Perhaps to other stuff
}
}
In your resources you now set the TargetType to local:MyToolTip and ensure your assembly namespace is placed with the local alias in the user control element.
Now in your Xaml:-
<ToolTipService.ToolTip>
<local:MyToolTip x:Name="RibbonItemTooltip" Content="" Foreground="#FFFFFFFF" FontSize="9.333" Placement="Mouse" Template="{StaticResource ToolTipControlTemplateTimelineView}"/>
</ToolTipService.ToolTip>
I have resolved this in a slightly different way. Rather than modify the template at runtime I have created an instance of the template for each use case. I can then apply the correct template when I create the instance and elements in the template bind to control class to populate the tooltip with the appropriate values.
This is probably a better approach as the structure of the tooltips is now clearly visible in the XAML, rather than being hidden away in the code.

Resources