Last active
December 13, 2015 18:09
-
-
Save mwisnicki/4953452 to your computer and use it in GitHub Desktop.
WPF gradient brush that spans multiple elements
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
{ | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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