I am working on a product in WPF that requires an end-user toolbox, exactly like Visual Studio, where the user can drag and drop specific tools (such as a Button) onto another Canvas object.
So far, I have it looking OK, and am using the official Microsoft vector icons next to a label describing the tool.
For example, the code below displays a vector button icon and a label next to it.
My question is, how do I group both the image and label together so that they are "one item", similar to the Visual Studio toolbox.
I need the two grouped items to be draggable/droppable onto a separate Canvas object.
I don't have much experience with this in WPF so any advice would be appreciated.
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Width="16"
Height="16"
Grid.Row="1"
Grid.Column="0">
<Rectangle
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing
Brush="#00FFFFFF"
Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing
Brush="#FFF6F6F6"
Geometry="F1M2.5857,-0.000199999999999534L-0.000299999999999745,2.5858 -0.000299999999999745,9.4138 2.5857,12.0008 7.9997,12.0008 7.9997,14.2418 9.4317,14.2418 10.3357,13.2478 11.2607,15.3318 14.2687,13.9638 13.4017,11.9978 15.3537,11.9978 15.3537,10.5318 15.1097,10.3048 16.0007,9.4138 16.0007,2.5858 13.4147,-0.000199999999999534z" />
<GeometryDrawing
Brush="#FF424242"
Geometry="F1M9,6L9,13.23 10.629,11.441 11.766,14.004 12.955,13.463 11.869,10.998 14.387,10.998z M8,11L3,11 1,9 1,3 3,1 13,1 15,3 15,9 14.376,9.624 13.642,8.943 14,8.586 14,3.414 12.586,2 3.414,2 2,3.414 2,8.586 3.414,10 8,10z" />
<GeometryDrawing
Brush="#FFF0EFF1"
Geometry="F1M8,10L3.414,10 2,8.586 2,3.414 3.414,2 12.586,2 14,3.414 14,8.586 13.642,8.943 8,3.708z" />
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
</Viewbox>
<Label
Content="Button"
Grid.Row="1"
VerticalContentAlignment="Center"
Grid.ColumnSpan="2"
Margin="29,0,2.6,0.4" />
Related
This Question might be Very Similar to Questions such as Error when using x:Shared="False" resources in external assembly in WPF but I have not been able to find a Solution that I can Relate to or rather get an Idea of How to Solve this.
Why I have mentioned Mandatory Use in the title is I have not been able to find an alternative way to solve my problem other than the with the use of Setting X:shared to false.
My problem is that in a particular view that has Icons , for Elements of Similar Type Icons seem to be sharing hence even if there are two or more types the Icon will only be rendered with one Item
This Question is justified in these Stack oveerflow Questions as well
Content Only being shown in a Single element at a given time
WPF: Can use StaticResource only once
I would Really appreciate any help/suggestions to overcome this
A complication is the numerous colours you have there. The trend nowadays is to simpler icons with just a background and foreground. Which you can "just" use one path and geometry for.
Seeing as how this is several shapes and several colours, you could use a DrawingImage.
I did this example in a usercontrol, it's roughly like one of your icons.
xmlns:PresentationOptions="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
mc:Ignorable="PresentationOptions"
>
<UserControl.Resources>
<DrawingImage x:Key='icon' PresentationOptions:Freeze="True">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Geometry="M17,17A5,5,0,0,1,12,22A5,5,0,0,1,7,17C7,15.36,7.79,13.91,9,13L9,5A3,3,0,0,1,12,2A3,3,0,0,1,15,5L15,13C16.21,13.91,17,15.36,17,17 M11,8L11,14.17C9.83,14.58,9,15.69,9,17A3,3,0,0,0,12,20A3,3,0,0,0,15,17C15,15.69,14.17,14.58,13,14.17L13,8 11,8z"
Brush="Wheat"
>
<GeometryDrawing.Pen>
<Pen Thickness="1" Brush="Green"/>
</GeometryDrawing.Pen>
</GeometryDrawing>
<GeometryDrawing Geometry="M17,18L12,15.82 7,18 7,5 17,5 M17,3L7,3A2,2,0,0,0,5,5L5,21 12,18 19,21 19,5C19,3.89,18.1,3,17,3z"
Brush="Lavender"
>
<GeometryDrawing.Pen>
<Pen Thickness="1" Brush="Red"/>
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</UserControl.Resources>
<Grid>
<StackPanel>
<Image Width="12" Height="12" Source="{StaticResource icon}"/>
<Image Width="12" Height="12" Source="{StaticResource icon}"/>
</StackPanel>
</Grid>
</UserControl>
I might look at using DataTemplates to create the icons, if for any reason x:Shared is a problem. A DataTemplate instantiates a copy of the content when it's applied, so sharing is a non-issue.
Resource:
<DataTemplate x:Key="FileSystemIcon">
<Canvas Width="12" Height="12">
<Path
Stroke="Black"
Fill="White"
Data="M20,4L4,4A2,2,0,0,0,2,6L2,18A2,2,0,0,0,4,20L20,20A2,2,0,0,0,22,18L22,6A2,2,0,0,0,20,4 M20,18L4,18 4,8 12,13 20,8 20,18 M20,6L12,11 4,6 4,6 20,6 20,6z"
/>
</Canvas>
</DataTemplate>
Usage:
<UserControl ContentTemplate="{StaticResource FileSystemIcon}" />
I have a DrawingImage that I use as a Vector image placeholder.
Further on, there are styles that take that respective DrawingImage and use it as an image in my UserControl custom button.
So far everything was fine, but I just came to realize that my current approach has caused my DrawingImages to be non-reusable, since they have their Brush property hard-bound to the control, they are used in, as follows:
<DrawingImage x:Key="addIcon">
<DrawingImage.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="{Binding Path=ImageBrush, ElementName=addButton}" Geometry="M438.2,0H51.6C23.1,0,0,23.2,0,51.6v386.6c0,28.5,23.2,51.6,51.6,51.6h386.6c28.5,0,51.6-23.2,51.6-51.6V51.6
C489.8,23.2,466.6,0,438.2,0z M465.3,438.2c0,14.9-12.2,27.1-27.1,27.1H51.6c-14.9,0-27.1-12.2-27.1-27.1V51.6
c0-14.9,12.2-27.1,27.1-27.1h386.6c14.9,0,27.1,12.2,27.1,27.1V438.2z" />
<GeometryDrawing Brush="{Binding Path=ImageBrush, ElementName=addButton}" Geometry="M337.4,232.7h-80.3v-80.3c0-6.8-5.5-12.3-12.3-12.3s-12.3,5.5-12.3,12.3v80.3h-80.3c-6.8,0-12.3,5.5-12.3,12.2
c0,6.8,5.5,12.3,12.3,12.3h80.3v80.3c0,6.8,5.5,12.3,12.3,12.3s12.3-5.5,12.3-12.3v-80.3h80.3c6.8,0,12.3-5.5,12.3-12.3
C349.7,238.1,344.2,232.7,337.4,232.7z" />
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
Now, I was trying to come up with a way to have them bound not directly to the Element, but to some kind of a generic Element (similairly as interfaces work) so that the code would be sure that anything that is bound to it, has got the Brush Dependency property.
So far, i failed to find it.
I have also tried by searching through an ancestor, still no luck.
Is there a more or less common practice to binding to unknown Elements, that have certain dependency properties, without exposing them directly?
In fact, there is no way to do directly what you ask. The reason is that when you define DrawingImage as a resource and then use it as a source for image, then no copies of the resource are created, but instead of this each image looks at the same resource. Therefore, the DrawingImage can not have a parent in the visual tree at all, so there is just no control to bind to.
There are two options here. One is to use a Geometry as a resource, not a DrawingImage. Then you can create some DrawingImage resources referencing to this Geometry and using different colors. Or do not use DrawingImage at all, but use the Geometry directly (for example, through the Path). Actually, there are so many ways how to use and combine Geometry resources. I give just few examples here:
<Window x:Class="FlipControlApp.MainWindow"
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"
Title="MainWindow" Height="300" Width="300">
<Window.Resources>
<PathGeometry x:Key="addIconGeometry" Figures="M337.4,232.7h-80.3v-80.3c0-6.8-5.5-12.3-12.3-12.3s-12.3,5.5-12.3,12.3v80.3h-80.3c-6.8,0-12.3,5.5-12.3,12.2
c0,6.8,5.5,12.3,12.3,12.3h80.3v80.3c0,6.8,5.5,12.3,12.3,12.3s12.3-5.5,12.3-12.3v-80.3h80.3c6.8,0,12.3-5.5,12.3-12.3
C349.7,238.1,344.2,232.7,337.4,232.7z"/>
<PathGeometry x:Key="iconBorderGeometry" Figures="M438.2,0H51.6C23.1,0,0,23.2,0,51.6v386.6c0,28.5,23.2,51.6,51.6,51.6h386.6c28.5,0,51.6-23.2,51.6-51.6V51.6
C489.8,23.2,466.6,0,438.2,0z M465.3,438.2c0,14.9-12.2,27.1-27.1,27.1H51.6c-14.9,0-27.1-12.2-27.1-27.1V51.6
c0-14.9,12.2-27.1,27.1-27.1h386.6c14.9,0,27.1,12.2,27.1,27.1V438.2z"/>
<GeometryGroup x:Key="addIconWithBorderGeometry">
<StaticResource ResourceKey="iconBorderGeometry"/>
<StaticResource ResourceKey="addIconGeometry"/>
</GeometryGroup>
<DrawingImage x:Key="addIconBlack">
<DrawingImage.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="Black" Geometry="{StaticResource addIconWithBorderGeometry}" />
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Window.Resources>
<UniformGrid Columns="2">
<Button Name="addButton0" Width="100" Height="100">
<Image Source="{StaticResource addIconBlack}"/>
</Button>
<Button Width="100" Height="100" Foreground="Blue">
<Image>
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="{Binding Foreground, RelativeSource={RelativeSource AncestorType=Control}}"
Geometry="{StaticResource addIconGeometry}" />
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</Button>
<Button Width="100" Height="100" Foreground="Green">
<Grid>
<Path Data="{StaticResource iconBorderGeometry}" Fill="Pink" Stretch="Uniform"/>
<Path Data="{StaticResource addIconGeometry}" Fill="Purple" Stretch="Uniform" Margin="15"/>
</Grid>
</Button>
<Button x:Name="addBtn" Width="100" Height="100" Foreground="Green">
<Path Fill="{Binding Foreground, ElementName=addBtn}" Stretch="Uniform">
<Path.Data>
<CombinedGeometry Geometry1="{StaticResource iconBorderGeometry}" Geometry2="{StaticResource addIconGeometry}"/>
</Path.Data>
</Path>
</Button>
</UniformGrid>
</Window>
Another option is to use whole Image as resource with a Brush binding to the some ancestors property. But in this case, it is necessary to use the attribute x:Shared and hence the resource must be declared in the compiled resource dictionary (for details see x:Shared).
Create resource:
<ResourceDictionary>
<Image x:Key="addIconImage2" x:Shared="False">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="{Binding Foreground, RelativeSource={RelativeSource AncestorType=Control}}"
Geometry="M438.2,0H51.6C23.1,0,0,23.2,0,51.6v386.6c0,28.5,23.2,51.6,51.6,51.6h386.6c28.5,0,51.6-23.2,51.6-51.6V51.6
C489.8,23.2,466.6,0,438.2,0z M465.3,438.2c0,14.9-12.2,27.1-27.1,27.1H51.6c-14.9,0-27.1-12.2-27.1-27.1V51.6
c0-14.9,12.2-27.1,27.1-27.1h386.6c14.9,0,27.1,12.2,27.1,27.1V438.2z" />
<GeometryDrawing Brush="{Binding Foreground, RelativeSource={RelativeSource AncestorType=Control}}"
Geometry="M337.4,232.7h-80.3v-80.3c0-6.8-5.5-12.3-12.3-12.3s-12.3,5.5-12.3,12.3v80.3h-80.3c-6.8,0-12.3,5.5-12.3,12.2
c0,6.8,5.5,12.3,12.3,12.3h80.3v80.3c0,6.8,5.5,12.3,12.3,12.3s12.3-5.5,12.3-12.3v-80.3h80.3c6.8,0,12.3-5.5,12.3-12.3
C349.7,238.1,344.2,232.7,337.4,232.7z" />
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</ResourceDictionary>
Then use it:
<Button Name="addButton3" Width="100" Height="100"
Content="{StaticResource addIconImage}" Foreground="Red"/>
<Button Name="addButton4" Width="100" Height="100"
Content="{StaticResource addIconImage}" Foreground="Green"/>
Each time you reference to it, new copy will be created.
But the first way is more flexible.
I read this document which explains how to to create rounded corner in WPF. My xaml is this:
<Border CornerRadius="50,0,50,0" Background="White" BorderBrush="#99ffc0c0" BorderThickness=".5">
<Grid>
<Ribbon x:Name="ribbon" HorizontalAlignment="Left" VerticalAlignment="Top" Width="524"/>
</Grid>
</Border>
In this case my ribbon is not rounded such as my form. What can I do?
You can clip to create rounded corner. If you want to corner all 4 sides, it can be done with simple RectangleGeomentry as shown below:
<Ribbon x:Name="ribbon" HorizontalAlignment="Left" VerticalAlignment="Top" Height="135" Width="524">
<Ribbon.Clip>
<RectangleGeometry RadiusX="50" RadiusY="50" Rect="0,0,524,135" />
</Ribbon.Clip>
</Ribbon>
If you want to do a top-right and bottom-left cornering only, then it is little more tricky. You have to use combined geometry with two rectangles. First one starts a 0,0 but ends outside of the the right bound. And the second one starts at -100,-100 (you have to ensure its far away enough from top/left and ends at the correct coordiantes 624,235 (adding 100 to left,bottom coordinates). Intersect of these two will create top-left, bottom-right rounded corners.
<Ribbon x:Name="ribbon" HorizontalAlignment="Left" VerticalAlignment="Top" Height="135" Width="524">
<Ribbon.Clip>
<CombinedGeometry GeometryCombineMode="Intersect">
<CombinedGeometry.Geometry1>
<RectangleGeometry RadiusX="50" RadiusY="50" Rect="0,0,600,200" />
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<RectangleGeometry RadiusX="50" RadiusY="50" Rect="-100,-100,624,235" />
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Ribbon.Clip>
</Ribbon>
This approach requires that your Ribbon has predefined size, if you want the control to dynamically adjust based on screen you will clip dynamically with code-behind.
Another way to do it is probably with updating the ribbon control template from copy (using Blend).
I'm trying to draw lines on a canvas but i'm not able to draw it if the value of x2 is more than 125020. Please find the XAML code below.`I'm able to see lines if the value of x2 is equal to or below 125020.
<ScrollViewer Name="C1_S" Grid.Row="0" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" VerticalAlignment="Bottom" Grid.ColumnSpan="2" >
<Canvas Name="canvas_core0" Grid.Row="1" HorizontalAlignment="Right" VerticalAlignment="Bottom" Height="640" Width="1000000" MouseWheel="Canvas_MouseWheel" >
<Line Stroke="Black" X1="20" Y1="20" X2="20" Y2="620" StrokeEndLineCap="Triangle" StrokeDashCap="Triangle" />
<Line Stroke="Black" X1="20" Y1="220" X2="125021" Y2="220" StrokeEndLineCap="Triangle" StrokeDashCap="Triangle" />
<Line Stroke="Black" X1="20" Y1="420" X2="{Binding ElementName=canvas_core0, Path=Width}" Y2="420" StrokeEndLineCap="Triangle" StrokeDashCap="Triangle" />
<Line Stroke="Black" X1="20" Y1="620" X2="{Binding ElementName=canvas_core0, Path=Width}" Y2="620" StrokeEndLineCap="Triangle" StrokeDashCap="Triangle" />
<Canvas.Background>
<DrawingBrush TileMode="Tile" Viewport="0,20,40,40" ViewportUnits="Absolute">
<DrawingBrush.Drawing>
<GeometryDrawing>
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,50,50"/>
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Brush="Gray" Thickness=".1"/>
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</Canvas.Background>
<Canvas.RenderTransform>
<MatrixTransform/>
</Canvas.RenderTransform>
</Canvas>
</ScrollViewer>
Can anyone help? Is there any limit for the length of a line while drawing on a canvas?
Huh, must be a bug with WPF drawing long horizontal lines.
I don't have a real answer for you but a work around might be to NOT draw horizontal lines. Instead draw nearly horizontal lines. If you change Y1 or Y2, but not both, from "220" to "220.00001" then the line is visible. Note that "220.000001" (one more 0) did not draw so that seems to be another limit of some sort.
I tested it with X2="125021000" (added three 0s) and the line still showed up.
Increasing the StrokeThickness (even only slightly) works.
I realise this answer is somewhat unsatisfactory but may help if you are desperate and is at least a clue.
I tried playing with UseLayoutRounding and SnapsToDevicePixels but couldn't make it work.
Interesting.
I have made a small cross icon using a polygon as following:
<Viewbox Margin="28,-22,-28,22">
<Polygon
Points="300, 200 325,200 325,250 375,250 375,275 325,275 325,325 300,325 300,275 250,275 250,250 300,250 300,200" Height="513" Width="595">
<Polygon.Fill>
<SolidColorBrush Color="#666666" Opacity="100"/>
</Polygon.Fill>
<Polygon.RenderTransform>
<RotateTransform CenterX="313" CenterY="237" Angle="45" />
</Polygon.RenderTransform>
</Polygon>
</Viewbox>
Now I want this polygon to be loaded into my button. How can I do this?
Put that inside Button.Content:
<Button ...>
<Viewbox ...>
</Viewbox>
</Button>
Don't forget to remove ViewBox.Margin (make Button big enough) or make it negative.