Skip to content

Instantly share code, notes, and snippets.

@mwisnicki
Last active December 13, 2015 18:09
Show Gist options
  • Save mwisnicki/4953452 to your computer and use it in GitHub Desktop.
Save mwisnicki/4953452 to your computer and use it in GitHub Desktop.
WPF gradient brush that spans multiple elements
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Shapes;
namespace WpfSharedGradient
{
class DiagramBlock : StackPanel
{
}
class DiagramBlockPart : Border
{
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
namespace WpfSharedGradient
{
public class DrawingOptions
{
#region SharedBackground
public static readonly DependencyProperty SharedBackgroundProperty =
DependencyProperty.RegisterAttached("SharedBackground", typeof (Brush), typeof (DrawingOptions),
new PropertyMetadata(null, OnSharedBackgroundChanged));
private static void OnSharedBackgroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ui = (FrameworkElement) d;
var newValue = (Brush) e.NewValue;
SetSharedBackgroundData(ui, new SharedBackgroundData { Element = ui, Background = newValue });
}
public static void SetSharedBackground(FrameworkElement element, Brush value)
{
element.SetValue(SharedBackgroundProperty, value);
}
[Browsable(true)]
[AttachedPropertyBrowsableForType(typeof (FrameworkElement))]
public static Brush GetSharedBackground(FrameworkElement element)
{
return (Brush) element.GetValue(SharedBackgroundProperty);
}
#endregion
#region InheritBackground
public static readonly DependencyProperty InheritBackgroundProperty =
DependencyProperty.RegisterAttached("InheritBackground", typeof (bool), typeof (DrawingOptions),
new PropertyMetadata(false, OnInheritBackgroundChanged));
private static void OnInheritBackgroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var fe = (FrameworkElement) d;
var newValue = (bool) e.NewValue;
var oldValue = (bool) e.OldValue;
// XXX only when fe has BackgroundProperty
if (oldValue && !newValue)
{
var context = (SharedBackgroundInheritorContext) fe.GetValue(SharedBackgroundInheritorContextProperty);
fe.ClearValue(SharedBackgroundInheritorContextProperty);
if (context != null)
context.Dispose();
// XXX does not restore original
BindingOperations.ClearBinding(fe, Panel.BackgroundProperty);
}
if (!oldValue && newValue)
{
fe.SetValue(SharedBackgroundInheritorContextProperty, new SharedBackgroundInheritorContext(fe));
fe.SetBinding(Panel.BackgroundProperty, new Binding
{
Path = new PropertyPath(SharedBackgroundDataProperty),
RelativeSource = RelativeSource.Self,
Converter = BackgroundDataToBrushConverter.Instance
});
}
}
public static void SetInheritBackground(FrameworkElement element, bool value)
{
element.SetValue(InheritBackgroundProperty, value);
}
[Browsable(true)]
[AttachedPropertyBrowsableForType(typeof (FrameworkElement))]
public static bool GetInheritBackground(FrameworkElement element)
{
return (bool) element.GetValue(InheritBackgroundProperty);
}
#endregion
#region Internals
public class SharedBackgroundData
{
public Brush Background { get; set; }
public FrameworkElement Element { get; set; }
}
public static readonly DependencyProperty SharedBackgroundDataProperty =
DependencyProperty.RegisterAttached("SharedBackgroundData", typeof (SharedBackgroundData),
typeof (DrawingOptions),
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.Inherits));
public static void SetSharedBackgroundData(UIElement element, SharedBackgroundData value)
{
element.SetValue(SharedBackgroundDataProperty, value);
}
public static SharedBackgroundData GetSharedBackgroundData(UIElement element)
{
return (SharedBackgroundData) element.GetValue(SharedBackgroundDataProperty);
}
public static readonly DependencyProperty BackgroundTransformProperty =
DependencyProperty.RegisterAttached("BackgroundTransform", typeof (Transform), typeof (DrawingOptions),
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.Inherits));
public static void SetBackgroundTransform(UIElement element, Transform value)
{
element.SetValue(BackgroundTransformProperty, value);
}
public static Transform GetBackgroundTransform(UIElement element)
{
return (Transform) element.GetValue(BackgroundTransformProperty);
}
public class BackgroundDataToBrushConverter : IValueConverter
{
public static readonly BackgroundDataToBrushConverter Instance = new BackgroundDataToBrushConverter();
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
var data = (SharedBackgroundData) value;
if (data == null)
return null;
var brush = data.Background.Clone();
return brush;
BindingOperations.SetBinding(brush, Brush.TransformProperty, new Binding
{
RelativeSource = RelativeSource.Self,
Path = new PropertyPath(BackgroundTransformProperty)
});
return brush;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
private static Transform CreateBackgroundTransform(FrameworkElement root, FrameworkElement target)
{
var tf = new TransformGroup();
// scale to root size
tf.Children.Add(new ScaleTransform(root.ActualWidth / target.ActualWidth,
root.ActualHeight / target.ActualHeight));
// offset position by distance from root edge
var p0 = root.TranslatePoint(new Point(), target);
tf.Children.Add(new TranslateTransform(p0.X, p0.Y));
return tf;
}
private class SharedBackgroundInheritorContext : IDisposable
{
private readonly FrameworkElement fe;
private Brush brush;
public SharedBackgroundInheritorContext(FrameworkElement fe)
{
this.fe = fe;
fe.LayoutUpdated += OnLayoutUpdated;
}
private void OnLayoutUpdated(object sender, EventArgs eventArgs)
{
var data = GetSharedBackgroundData(fe);
var brush = (Brush) fe.GetValue(Panel.BackgroundProperty);
if (brush != null)
{
brush.Transform = CreateBackgroundTransform(data.Element, fe);
}
}
public void Dispose()
{
fe.LayoutUpdated -= OnLayoutUpdated;
}
}
private static readonly DependencyProperty SharedBackgroundInheritorContextProperty =
DependencyProperty.RegisterAttached("SharedBackgroundInheritorContext",
typeof (SharedBackgroundInheritorContext),
typeof (DrawingOptions), new FrameworkPropertyMetadata(null));
#endregion
}
}
<Window x:Class="WpfSharedGradient.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:WpfSharedGradient"
Title="Window1"
Height="300"
Width="300">
<Window.Resources>
<RadialGradientBrush x:Key="brush"
Center="0.3,0.3"
GradientOrigin="0.3,0.3">
<GradientStop Color="Black"
Offset="1" />
<GradientStop Color="White" />
</RadialGradientBrush>
</Window.Resources>
<Grid>
<my:DiagramBlock x:Name="element"
my:DrawingOptions.SharedBackground="{StaticResource brush}">
<my:DiagramBlock.Effect>
<DropShadowEffect ShadowDepth="0"
BlurRadius="10" />
</my:DiagramBlock.Effect>
<my:DiagramBlockPart x:Name="child1"
Height="100"
Margin="10"
BorderThickness="1"
BorderBrush="Black"
my:DrawingOptions.InheritBackground="True">
</my:DiagramBlockPart>
<Grid>
<my:DiagramBlockPart x:Name="child2"
Height="20"
Margin="10"
BorderThickness="1"
BorderBrush="Black"
my:DrawingOptions.InheritBackground="True">
</my:DiagramBlockPart>
</Grid>
<Grid Margin="10">
<my:DiagramBlockPart x:Name="child3"
Height="100"
Margin="10"
BorderThickness="1"
BorderBrush="Black"
my:DrawingOptions.InheritBackground="True">
</my:DiagramBlockPart>
</Grid>
</my:DiagramBlock>
</Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace WpfSharedGradient
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment