How to handle events in Custom Controls - c#

I am trying to create a simple controller, here is my Generic.xaml;
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TopluNotEkle">
<Style TargetType="{x:Type local:FileSelector}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:FileSelector}">
<Button BorderThickness="8,8,8,8" Background="Gray" x:Name="MainButton">
<Button.BorderBrush>
<DrawingBrush TileMode="Tile" Viewport="0,0,64,64" ViewportUnits="Absolute">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="LightGray">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,100,100" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing Brush="DarkGray">
<GeometryDrawing.Geometry>
<GeometryGroup>
<RectangleGeometry Rect="0,0,50,50"/>
<RectangleGeometry Rect="50,50,50,50"/>
</GeometryGroup>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Button.BorderBrush>
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="BlueViolet" FontWeight="Bold" FontSize="20">Drag & Drop</TextBlock>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
and this is my code behind
public class FileSelector : Control
{
static FileSelector()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(FileSelector), new FrameworkPropertyMetadata(typeof(FileSelector)));
MainButton.Drop += myDrop;
}
static void myDrop(object sender, DragEventArgs e)
{
Debug.Fail("This is called");
}
}
I am getting The name MainButton does not exist in current context error. I also tried setting Drop = "myDrop", however, that also didn't work.
How can I listen to events on my component?

Buttons have been assigned a name by the x:Name attribute for us to be able to find them in code and hook up their click event handlers. This is done by overriding the OnApplyTemplate in our Custom Control class.
Retrieve the Button from the template
public override void OnApplyTemplate()
{
Button mainButton = GetTemplateChild("MainButton") as Button;
if (mainButton != null)
mainButton.Click += MainBtnClick;
}
Follow a complete tutorial like for example (but not necessarily) this: http://blog.magnusmontin.net/2013/03/16/how-to-create-a-custom-window-in-wpf/

So your problem is not what you are setting as a 'Drop' value, it is the reference to the MainButton that is not available.
I think you are trying to create a custom control, which typically defined as UserControl, not as a part of ResourceDictionary. I might be wrong as I haven't worked with WPF for several years. Here is how to create user control:
<UserControl x:class= "YourNamespace.FileSelector" ... other xmlns garbage>
<Button BorderThickness="8,8,8,8" Background="Gray" x:Name="MainButton">
<Button.BorderBrush>
<DrawingBrush TileMode="Tile" Viewport="0,0,64,64" ViewportUnits="Absolute">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="LightGray">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,100,100" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing Brush="DarkGray">
<GeometryDrawing.Geometry>
<GeometryGroup>
<RectangleGeometry Rect="0,0,50,50"/>
<RectangleGeometry Rect="50,50,50,50"/>
</GeometryGroup>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Button.BorderBrush>
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="BlueViolet" FontWeight="Bold" FontSize="20">Drag & Drop</TextBlock>
</Button>
Also check out this tutorial: https://www.tutorialspoint.com/wpf/wpf_custom_controls.htm

Related

Is it possible to use WPF ResourceDictionary with parameters?

I have stored some icons as XAML-Code in the resourcedictionary, e.g.
<DataTemplate x:Key="Question">
<Viewbox Width="16" Height="16" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Rectangle Width="16" Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M10,14L6,14 6,9C6,8.232,6.29,7.531,6.765,7L4,7 4,5C4,3.262 5.682,2 8,2 10.316,2 12,3.262 12,5L12,7C12,8.304,11.164,9.416,10,9.828z" />
<GeometryDrawing Brush="#FF414141" Geometry="F1M9,11L7,11 7,13 9,13z M9,10L7,10 7,9C7,7.897,7.897,7,9,7L9,5.203C8.84,5.115 8.495,5 8,5 7.504,5 7.159,5.115 7,5.203L7,6 5,6 5,5C5,3.841 6.261,3 8,3 9.738,3 11,3.841 11,5L11,7C11,8.103,10.102,9,9,9z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
</Viewbox>
</DataTemplate>
The problem: width and height are fixed values. So i changed the fixed values to "Auto" without any error in the resourcedictionary.
<DataTemplate x:Key="QuestionAuto">
<Viewbox Width="Auto" Height="Auto" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Rectangle Width="Auto" Height="Auto">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M10,14L6,14 6,9C6,8.232,6.29,7.531,6.765,7L4,7 4,5C4,3.262 5.682,2 8,2 10.316,2 12,3.262 12,5L12,7C12,8.304,11.164,9.416,10,9.828z" />
<GeometryDrawing Brush="#FF414141" Geometry="F1M9,11L7,11 7,13 9,13z M9,10L7,10 7,9C7,7.897,7.897,7,9,7L9,5.203C8.84,5.115 8.495,5 8,5 7.504,5 7.159,5.115 7,5.203L7,6 5,6 5,5C5,3.841 6.261,3 8,3 9.738,3 11,3.841 11,5L11,7C11,8.103,10.102,9,9,9z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
</Viewbox>
</DataTemplate>
Then i used the icon in my view with setting the width and height values, but nothing is shown. the "fixed" icon works, the "auto" icon not.
<ContentPresenter Grid.RowSpan="2"
ContentTemplate="{StaticResource Question32}"
Margin="20,0">
</ContentPresenter>
vs.
<ContentPresenter Grid.RowSpan="2"
ContentTemplate="{StaticResource QuestionAuto}"
Margin="20,0"
Width="64"
Height="64">
</ContentPresenter>
Is it possible to set width and height parameter in the xaml-view and use them in the resourcedictionary? because the xaml-icon is basically "dynamic" and a variable size would be better than guessing the width and height to fit perfectly.
Better define the icons as DrawingImage resources like
<DrawingImage x:Key="Question">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M10,14L6,14 6,9C6,8.232,6.29,7.531,6.765,7L4,7 4,5C4,3.262 5.682,2 8,2 10.316,2 12,3.262 12,5L12,7C12,8.304,11.164,9.416,10,9.828z" />
<GeometryDrawing Brush="#FF414141" Geometry="F1M9,11L7,11 7,13 9,13z M9,10L7,10 7,9C7,7.897,7.897,7,9,7L9,5.203C8.84,5.115 8.495,5 8,5 7.504,5 7.159,5.115 7,5.203L7,6 5,6 5,5C5,3.841 6.261,3 8,3 9.738,3 11,3.841 11,5L11,7C11,8.103,10.102,9,9,9z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
and show them in an Image element like
<Image Source="{StaticResource Question}" Width="64" Height="64"/>
The drawing would stretch to the desired size according to the Stretch property of the Image, which defaults to Uniform.

Set InkCanvas Background DrawingBrush via C#

I want to set the following XAML attributes via C#:
<InkCanvas.Background>
<DrawingBrush Stretch="None" TileMode="Tile" Viewport="0,0,2,2" ViewportUnits="Absolute">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="White">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,2,2" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing Brush="#FFE3E3E3">
<GeometryDrawing.Geometry>
<GeometryGroup>
<RectangleGeometry Rect="0,0,1,1" />
<RectangleGeometry Rect="1,1,1,1" />
</GeometryGroup>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</InkCanvas.Background>
How should I go about this?
I have found the solution myself and leave it here for anyone else, facing the same issue.
http://www.c-sharpcorner.com/uploadfile/mahesh/wpf-drawing-brush/ pretty much is what i was looking for.

How to specify the default value if the binding fails?

I have a DrawingBrush as follow :-
<DrawingBrush x:Key="MY_ICON" Viewbox="0,0,39.125,39.125" ViewboxUnits="Absolute">
<DrawingBrush.Drawing>
<GeometryDrawing Brush="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=uiEntityViews:BaseView}, Path=Color}" Geometry="F1M19.5625,0.999954C29.8144,0.999954 38.125,9.31053 38.125,19.5625 38.125,29.8142 29.8143,38.1249 19.5625,38.1249 9.31073,38.1249 1,29.8142 1,19.5625 1,9.31053 9.31064,0.999954 19.5625,0.999954z">
<GeometryDrawing.Pen>
<Pen DashCap="Square" EndLineCap="Flat" LineJoin="Round" MiterLimit="10" StartLineCap="Flat" Thickness="2">
<Pen.Brush>
<LinearGradientBrush EndPoint="0.849422,0.849423" StartPoint="0.150577,0.150578">
<GradientStop Color="#FF657783" Offset="0"/>
<GradientStop Color="White" Offset="0.146"/>
<GradientStop Color="#FF2C4758" Offset="1"/>
</LinearGradientBrush>
</Pen.Brush>
<Pen.DashStyle>
<DashStyle/>
</Pen.DashStyle>
</Pen>
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
I used the above brush inside the style as the following:-
<Style x:Key="NODE_ICON" TargetType="Rectangle">
<Setter Property="Fill">
<Setter.Value>
<DrawingBrush TileMode="None">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="{StaticResource MY_ICON}">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,1,1" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
<DrawingGroup>
<DrawingGroup.Transform>
<TranslateTransform X="0.2" Y="0.2" />
</DrawingGroup.Transform>
<GeometryDrawing Brush="{StaticResource NODE_ICON}">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,1,1" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Setter.Value>
</Setter>
Right now, I am getting this error - System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='Views.UIEntityViews.BaseView', AncestorLevel='1''. BindingExpression:Path=Color; DataItem=null; target element is 'GeometryDrawing' (HashCode=3377278); target property is 'Brush' (type 'Brush').
It seems that the binding is not working. Is there a way to specify the default value for color, lets say <GeometryDrawing Brush="{StaticResource ROADM_ICON, color=Gray}"> if the binding fails?
Use FallbackValue.....<GeometryDrawing Brush="{StaticResource NODE_ICON, FallbackValue=Gray}">
As #Carmine suggested using FallbackValue, I was able to make it work making following changes:-
<GeometryDrawing Brush="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=uiEntityViews:CanvasLinkConnectionPointBaseView}, Path=MeSiteColor, FallbackValue=Gray}" Geometry="F1M19.5625,0.999954C29.8144,0.999954 38.125,9.31053 38.125,19.5625 38.125,29.8142 29.8143,38.1249 19.5625,38.1249 9.31073,38.1249 1,29.8142 1,19.5625 1,9.31053 9.31064,0.999954 19.5625,0.999954z">

How to use DrawingBrush as Window.Icon?

I have a DLL that has a ResourceDictionary containing a XAML image:
<DrawingBrush x:Key="imgFoo" ViewboxUnits="Absolute" Viewbox="0,0,128,128">
<DrawingBrush.Drawing>
<GeometryDrawing Brush="#FF111111">
<GeometryDrawing.Pen>
<Pen LineJoin="Miter" StartLineCap="Square" EndLineCap="Square"/>
</GeometryDrawing.Pen>
<GeometryDrawing.Geometry>
<PathGeometry Figures="M 56.5625 ... 64.03125 45.46875 z"
FillRule="NonZero"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
Is it possible to use this DrawingBrush as Window.Icon?
using StaticResource or DynamicResource doesn't work:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Icon="{DynamicResource imgFoo}">
The only thing I found is using Window.Resources like this:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Icon="{DynamicResource imgFoo}">
<Window.Resources>
<DrawingImage x:Key="imgFoo">
<DrawingImage.Drawing>
<GeometryDrawing Brush="#FF111111">
<GeometryDrawing.Pen>
<Pen LineJoin="Miter" StartLineCap="Square"
EndLineCap="Square"/>
</GeometryDrawing.Pen>
<GeometryDrawing.Geometry>
<PathGeometry Figures="M 56.5625 ... 64.03125 45.46875 z"
FillRule="NonZero"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingImage.Drawing>
</DrawingImage>
</Window.Resources>
But this uses only a part of the DrawingBrush from the DLL file and creates duplicate XAML code.
Any suggestions on how I could use that DrawingBrush directly?
You could split the resource declaration into two parts and declare the GeometryDrawing and the DrawingBrush separately:
<GeometryDrawing x:Key="imgFooDrawing" Brush="#FF111111">
<GeometryDrawing.Pen>
<Pen LineJoin="Miter" StartLineCap="Square" EndLineCap="Square"/>
</GeometryDrawing.Pen>
<GeometryDrawing.Geometry>
<PathGeometry Figures="M10,64 L64,10 118,64 64,118Z" FillRule="NonZero"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
<DrawingBrush x:Key="imgFoo" ViewboxUnits="Absolute" Viewbox="0,0,128,128"
Drawing="{StaticResource imgFooDrawing}">
</DrawingBrush>
Now you could directly reuse the GeometryDrawing in a DrawingImage that is used as the Window's Icon:
<Window.Icon>
<DrawingImage Drawing="{DynamicResource imgFooDrawing}"/>
</Window.Icon>
In case you can't change the Resource DLL, you could bind the DrawingImage's Drawing property to the Drawing property of the DrawingBrush.
This would however require an ugly workaround because you can't use a DynamicResource as binding source. You may set the Tag property of the Window to the DrawingBrush, and then create a RelativeSource/FindAncestor binding:
<Window ... Tag="{DynamicResource imgFoo}">
<Window.Icon>
<DrawingImage Drawing="{Binding Tag.Drawing,
RelativeSource={RelativeSource AncestorType=Window}}"/>
</Window.Icon>
</Window>

WPF rectangle anchor

I have a canvas and I set a background image to it. I have some rectangles within the canvas. When the form gets resized, the background image gets strechted but I also want the rectangles to get to the new positions. Any help?
<Window x:Class="abc.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:jas="clr-namespace:foo"
Title="foo" Width="1200" Height="800" >
<jas:DragCanvas x:Name="jasCanvas" >
<jas:DragCanvas.Background>
<DrawingBrush Stretch="None" TileMode="Tile" Viewport="0,0,20,20" ViewportUnits="Absolute">
<!-- a drawing of 4 checkerboard tiles -->
<DrawingBrush.Drawing>
<DrawingGroup>
<!-- checkerboard background -->
<!--<GeometryDrawing Brush="White">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,20,20" />
</GeometryDrawing.Geometry>
</GeometryDrawing>-->
<!-- two checkerboard foreground tiles -->
<!--<GeometryDrawing Brush="LightGray">
<GeometryDrawing.Geometry>
<GeometryGroup>
<RectangleGeometry Rect="0,0,10,10" />
<RectangleGeometry Rect="10,10,10,10" />
</GeometryGroup>
</GeometryDrawing.Geometry>
</GeometryDrawing>-->
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</jas:DragCanvas.Background>
<TextBlock x:Name="m_resultText" FontSize="16" Canvas.Left="10" Canvas.Top="10"
jas:DragCanvas.CanBeDragged="False"
FontWeight="Bold"
Background="Black"/>
<Rectangle x:Name="m_redRect" Width="40" Height="120" Canvas.Left="100" Canvas.Top="50" Stroke="Gray" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
StrokeThickness="1" >
<Rectangle.Fill>
<!--<SolidColorBrush Color="#99FF0000"/>-->
<ImageBrush ImageSource="Media/yacht.png" />
</Rectangle.Fill>
<Rectangle.RenderTransform>
<RotateTransform x:Name="m_redRectRotate" Angle="0" CenterX="20" CenterY="60"/>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="m_greenRect" Width="40" Height="120" Canvas.Left="247" Canvas.Top="113" Stroke="Gray"
StrokeThickness="1"
>
<Rectangle.Fill>
<!--<SolidColorBrush Color="#9900FF00" />-->
<ImageBrush ImageSource="Media/yacht.png"/>
</Rectangle.Fill>
<Rectangle.RenderTransform>
<RotateTransform x:Name="m_greenRectRotate" Angle="0" CenterX="20" CenterY="60"/>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="m_greenRect2" Width="40" Height="120" Canvas.Left="338" Canvas.Top="113" Stroke="Gray"
StrokeThickness="1"
>
<Rectangle.Fill>
<!--<SolidColorBrush Color="#9900FF00" />-->
<ImageBrush ImageSource="Media/yacht.png"/>
</Rectangle.Fill>
<Rectangle.RenderTransform>
<RotateTransform x:Name="m_greenRectRotate2" Angle="0" CenterX="20" CenterY="60"/>
</Rectangle.RenderTransform>
</Rectangle>
<Button Content="Button" Canvas.Left="464" Canvas.Top="10" Width="75" Click="Button_Click" x:Name="buton" jas:DragCanvas.CanBeDragged="false"/>
</jas:DragCanvas>
</Window>
here is the background image set :
ImageBrush ib = new ImageBrush();
ib.ImageSource = new BitmapImage(new Uri(#"Media\foo.jpg", UriKind.Relative));
jasCanvas.Background = ib;
There is a very simple solution that you can use... just put your Canvas inside a ViewBox control. From the linked page on MSDN, the ViewBox Defines a content decorator that can stretch and scale a single child to fill the available space:
<ViewBox Stretch="Fill">
<jas:DragCanvas x:Name="jasCanvas">
...
</jas:DragCanvas>
</ViewBox>
If it doesn't work straight out of the box, then just experiment with the Stretch and StretchDirection properties.
I'm not sure about your jas namespace, but there is no layout system support in standard Canvas panel. So if you want something placed inside canvas to move and resize automatically due to parent canvas position change you have to create some code for this behavior manually.
Modify your XAML code like this:
<jas:DragCanvas x:Name="jasCanvas" SizeChanged="jasCanvas_SizeChanged">
And then in your code behind file:
private void Canvas_SizeChanged(object sender, SizeChangedEventArgs e)
{
var deltaWidth = (e.NewSize.Width - e.PreviousSize.Width);
m_redRect.Width += deltaWidth;
// and so on
}

Categories