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.
Related
I am trying to create a blur effect in WPF, but I want to blur the content located behind the control (such as a Grid), I do not intend to blur the contents of the Grid.
I want to do something like this image.
If you are looking for something like this:
The answer is to use a BitmapEffect :
<Image Source="http://www.pingminghealth.com/wp-content/uploads/2010/12/cherries.jpg" Stretch="UniformToFill">
<Image.BitmapEffect>
<BlurBitmapEffect Radius="20" />
</Image.BitmapEffect>
</Image>
You can find a tutorial about it on msdn: How to Apply a Blur Effect to a Visual.
Just in case, someone is still looking for a solution in 2018, something like this worked for me: BlurryControls.BlurryUserControl.cs
<Rectangle>
<Rectangle.Effect>
<BlurEffect Radius="{DynamicResource BlurRadius}"/>
</Rectangle.Effect>
<Rectangle.Fill>
<VisualBrush
ViewboxUnits="Absolute"
Viewbox="{Binding RenderTransform.Children[3],
Converter={StaticResource TranslateTransformToRectViewboxVisualBrushConverter},
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Grid}},
UpdateSourceTrigger=PropertyChanged}"
AlignmentX="Left" AlignmentY="Top"
Visual="{Binding ElementName=BackgroundContainer}" Stretch="None">
<VisualBrush.Transform>
<TranslateTransform X="0" />
</VisualBrush.Transform>
</VisualBrush>
</Rectangle.Fill>
</Rectangle>
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 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.
I am trying to create a blur effect in WPF, but I want to blur the content located behind the control (such as a Grid), I do not intend to blur the contents of the Grid.
I want to do something like this image.
If you are looking for something like this:
The answer is to use a BitmapEffect :
<Image Source="http://www.pingminghealth.com/wp-content/uploads/2010/12/cherries.jpg" Stretch="UniformToFill">
<Image.BitmapEffect>
<BlurBitmapEffect Radius="20" />
</Image.BitmapEffect>
</Image>
You can find a tutorial about it on msdn: How to Apply a Blur Effect to a Visual.
Just in case, someone is still looking for a solution in 2018, something like this worked for me: BlurryControls.BlurryUserControl.cs
<Rectangle>
<Rectangle.Effect>
<BlurEffect Radius="{DynamicResource BlurRadius}"/>
</Rectangle.Effect>
<Rectangle.Fill>
<VisualBrush
ViewboxUnits="Absolute"
Viewbox="{Binding RenderTransform.Children[3],
Converter={StaticResource TranslateTransformToRectViewboxVisualBrushConverter},
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Grid}},
UpdateSourceTrigger=PropertyChanged}"
AlignmentX="Left" AlignmentY="Top"
Visual="{Binding ElementName=BackgroundContainer}" Stretch="None">
<VisualBrush.Transform>
<TranslateTransform X="0" />
</VisualBrush.Transform>
</VisualBrush>
</Rectangle.Fill>
</Rectangle>
I have downloaded this pack : http://modernuiicons.com/ and I'm trying to use the xaml icons.
I have added a xaml file to my solution with the following content
<?xml version="1.0" encoding="utf-8"?>
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Name="appbar_check" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
<Path Width="37.9998" Height="31.6665" Canvas.Left="19.0001" Canvas.Top="22.1668" Stretch="Fill" Fill="#FF000000" Data="F1 M 23.7501,33.25L 34.8334,44.3333L 52.2499,22.1668L 56.9999,26.9168L 34.8334,53.8333L 19.0001,38L 23.7501,33.25 Z "/>
</Canvas>
Now, how do I reference this canvas to my usercontrol?
Usercontrol
<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"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
mc:Ignorable="d"
x:Class="UserControlSolution.UserControlButton"
x:Name="UserControl"
Height="50" Background="#FF2F2F2F" BorderBrush="#FF919191">
<Grid x:Name="LayoutRoot" Height="50" RenderTransformOrigin="0.5,0.5">
<Rectangle x:Name="rectangle" RenderTransformOrigin="0.5,0.5" Width="230" Height="50"/>
<TextBlock x:Name="NameLabel" FontSize="16" Foreground="#FFE5E5E5" Height="34" Width="149" Text="Onthaal Telefoon" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,10,0,0" FontFamily="Segoe UI Semibold"/>
<Viewbox HorizontalAlignment="Right" VerticalAlignment="Top" Height="16.5" Width="17.789" Margin="0,15,24.5,0">
// Here I want to reference the canvas
</Viewbox>
</Grid>
</UserControl>
I can copy the content of the canvas offcourse but there must be another solution.
Add the Canvas and Path as a resource on the page or in the App.xaml or whatever, remember to set x:Key. Then use a ContentControl to reference the resource.
<!-- In Resources, either on the Page or App.xaml for app-wide reuse -->
<Canvas x:Key="TickCanvas" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Name="appbar_check" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
<Path Width="37.9998" Height="31.6665" Canvas.Left="19.0001" Canvas.Top="22.1668" Stretch="Fill" Fill="#FF000000" Data="F1 M 23.7501,33.25L 34.8334,44.3333L 52.2499,22.1668L 56.9999,26.9168L 34.8334,53.8333L 19.0001,38L 23.7501,33.25 Z "/>
</Canvas
<!-- On your page, or somewhere -->
<ViewBox>
<ContentControl Content="{StaticResource TickCanvas}" />
</ViewBox>
As proof it works, I was able to see its a tick!
Just a side note, I often take just the path data, the mini-markup and save that as a string resource. Then using a Path I reference the markup resource via Data={StaticResource TickPath} that way I can resize the vector using the Height and Width on the Path itself or let it expand and shrink by its parent by setting Stretch="Uniform". Saves the overhead of the Viewbox.
<!-- In App.xaml for app-wide reuse -->
<x:String x:Key="TickPath">F1 M 23.7501,33.25L 34.8334,44.3333L 52.2499,22.1668L 56.9999,26.9168L 34.8334,53.8333L 19.0001,38L 23.7501,33.25 Z </x:String>
<!-- On page, template or wherever -->
<Path Data="{StaticResource TickPath} />
This technique may not work in this instance as there's a clip geometry there. But for simple vectors its fine, I have hand drawn typefaces (that can't be embedded as fonts) stored as markup in files, I then load them in at runtime - Data={Binding PathData} works just as well.
A variation based on Luke's answer allowing a color to be specified down to the path.
<Style TargetType="{x:Type ContentControl}" x:Key="TickIcon">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Viewbox Width="{Binding Width, RelativeSource={RelativeSource AncestorType=ContentControl}}">
<Canvas x:Name="appbar_check" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
<Path Width="37.9998" Height="31.6665" Canvas.Left="19.0001" Canvas.Top="22.1668" Stretch="Fill" Fill="{Binding Foreground, RelativeSource={RelativeSource AncestorType=ContentControl}}" Data="F1 M 23.7501,33.25L 34.8334,44.3333L 52.2499,22.1668L 56.9999,26.9168L 34.8334,53.8333L 19.0001,38L 23.7501,33.25 Z "/>
</Canvas>
</Viewbox>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
and
<Button Command="{Binding ConnectCommand}" VerticalAlignment="Stretch">
<WrapPanel>
<ContentControl Foreground="AliceBlue" Style="{StaticResource TickIcon}" Width="20" />
<TextBlock>Connect</TextBlock>
</WrapPanel>
</Button>