How to allow default value when using binding for custom control - c#

All, I am working on a custom control of ours that has a Border with a Background set to the below
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFF7F7F7" Offset="0.164" />
<GradientStop Color="#FFDBDBDB" Offset="0.664" />
<GradientStop Color="#FFB3B3B3" Offset="0.978" />
</LinearGradientBrush>
However, I want to allow the user to override this. I created a dependency property of type Brush, however I cannot figure out how to bind to the Background, while still having this default in the XAML. Is this even possible?

Take a look at the TemplateBinding Markup Extension
<Style TargetType="{x:Type your:YourControl}">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFF7F7F7" Offset="0.164" />
<GradientStop Color="#FFDBDBDB" Offset="0.664" />
<GradientStop Color="#FFB3B3B3" Offset="0.978" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
Then in some part of your default ControlTemplate:
<Border Background="{TemplateBinding Background}">
....
</Border>
This will allow the consumer to override the default Background, while maintaining the Style-defined one:
<your:YourControl/> <!-- Will use default Background -->
<your:YourControl Background="Green"/> <!-- will have Green background -->

Related

C# WPF Background style for all Windows in ResourceDictionary - Target type with relative path?

I'm organizing my projects with folders. So I have put all window files inside "MyWindows" folder, and in another folder called "Styles" I have a ResourceDictionary. Now I want to write a style for background that would apply to all desired windows, like this:
<Style TargetType="Window" x:Key="Ozadje">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" MappingMode="RelativeToBoundingBox" StartPoint="0.5,0">
<GradientStop Color="#FF66A7B6" Offset="0.997"/>
<GradientStop Color="White"/>
<GradientStop Color="#FFAEF1F1"/>
<GradientStop Color="#FFACEAEA"/>
<GradientStop Color="#FF9BF1E6"/>
<GradientStop Color="#FFBFD1CF" Offset="0.06"/>
<GradientStop Color="#FF6CAAB7" Offset="0.924"/>
<GradientStop Color="#FF99BFC4"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="MyWindows/Window1" BasedOn="{StaticResource {x:Type Window}}" />
I can't set this because all Windows are not in a local: namespace that resource dictionary is. And another problem is that styles for windows must be applied manually for each Window via reference.
Is there anything I can do - besides calling Style="{StaticResource Ozadje}" in Window properties?...I'm curious if I can use a relative path.
P.S.: I'm a beginner in WPF.
Define a namespace mapping for the namespace in which Window1 is defined, e.g.:
<Style xmlns:MyWindows="clr-namespace:MyWindows"
TargetType="{x:Type MyWindows:Window1}" BasedOn="{StaticResource Ozadje}" />

WPF Border Color Binding to parent Controls Tag

I have a problem where I have to bind to Tag property. But don't know what will come here.
<Border x:Name="BorderStatus" CornerRadius="2" Tag="Transparent">
<Border.Background>
<LinearGradientBrush>
<GradientStop Color="{Binding Tag, ????}" Offset="0"/>
<GradientStop Color="{Binding Tag, ????}" Offset="0.47"/>
<GradientStop Color="Red" Offset="0.77"/>
<GradientStop Color="DarkRed" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
</Border>
This is done cause there are triggers which will change the tag property.
We can bind with ElementName but is there any other way?
Two ways of accomplishing your goal I can think of are these:
I. Use Binding.ElementName property:
Color="{Binding Tag, ElementName=BorderStatus}"
II. Use RelativeSource in FindAncestor mode:
Color="{Binding Tag, RelativeSource={RelativeSource FindAncestor, AncestorType=Border}}"

Control that uses "Color" type dependency property

I'm trying to write a Button control that displays a background gradient, starting with a color specified by a custom property, and ending with a hard-coded color. Seems to me this should be a straightforward control template, but I can't get it to work.
Subclassed "Button" with a Color dependency property:
public class GradientButton : Button
{
public Color BackgroundColor
{
get { return (Color)GetValue(BackgroundColorProperty); }
set { SetValue(BackgroundColorProperty, value); }
}
public static readonly DependencyProperty BackgroundColorProperty =
DependencyProperty.Register("BackgroundColor", typeof(Color), typeof(GradientButton), new PropertyMetadata((sender, args) => {
System.Diagnostics.Debug.WriteLine("Set bg col");
}));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(GradientButton), null);
public GradientButton()
: base()
{
this.DefaultStyleKey = typeof(GradientButton);
}
}
Control template, which sets a background gradient using the BackgroundColor DP:
<Style TargetType="custom:GradientButton">
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="Width" Value="200" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="custom:GradientButton">
<Grid>
<Grid.Background>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="{TemplateBinding BackgroundColor}" Offset="0" />
<GradientStop Color="Gray" Offset="1" />
</LinearGradientBrush>
</Grid.Background>
<TextBlock Text="{TemplateBinding Text}" />
<TextBlock Text="{TemplateBinding BackgroundColor}" HorizontalAlignment="Right" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
If I use the control like this:
<custom:GradientButton Text="Foo" BackgroundColor="#FF0000" />
then I expect to see a button with red-to-gray background gradient. Instead, only the gray part of the gradient appears:
What painfully obvious thing am I missing?
Edit For emphasis:
I added "Text" DP to prove my DP / TemplateBinding is in fact kosher.
Note that I can't display the "BackgroundColor", even as text in a text block -- and yet I have confirmed in the debugger that it is in fact being set on the control.
Sorry for making confuse. My answer is not correct. The issue here is TemplateBinding. TemplateBinding is a lightwight binding at compile time while using TemplatedParent happens at runtime, thus TemplateBinding has some limitation, one of which is it cannot bind to Freezables. What you can do is to use traditional templated parent instead:
<GradientStop Color="{Binding Path=BackgroundColor, RelativeSource={RelativeSource TemplatedParent}}" Offset="0" />
You can refer to http://blogs.msdn.com/b/liviuc/archive/2009/12/14/wpf-templatebinding-vs-relativesource-templatedparent.aspx
"You'd think the compiler or the runtime would throw some kind of error, but apparently not."
A good point. I did not see any binding error either in output window. However, I used Snoop to look into Color property and found exactly what I expected:
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=BackgroundColor; DataItem=null; target element is 'GradientStop' (HashCode=4605357); target property is 'Color' (type 'Color')
No problem,
Your first issue is how you're declaring your Gradient. The way you currently have will show a solid line of Red (or whatever color specified) for the top half, and a solid line of Gray for the bottom half. You want something like this and it does the rest of it for you.
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Red" Offset="0"/>
<GradientStop Color="Gray" Offset="1"/>
</LinearGradientBrush>
Also, you could make this easier on yourself and just utilize properties already existent that are there for purposes just like this one so you don't need any additional code. Like the Tag Property (which personally I stuff all sorts of different crap into for different purposes depending on the instances. So something like this will also suffice just fine without the need for the extra dependency declarations (unless it really matters that's called "BackgroundColor");
<Style TargetType="custom:GradientButton">
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="Width" Value="200" />
<Setter Property="Tag" Value="Red"/><!-- For the sake of having a default -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="custom:GradientButton">
<Grid>
<Grid.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="{TemplateBinding Tag}" Offset="0"/>
<GradientStop Color="Gray" Offset="1"/>
</LinearGradientBrush>
</Grid.Background>
<ContentPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and that should do ya up nicely, hope this helps. :)
I've discovered a workaround: if, instead of TemplateBinding, I use a RelativeSource binding, then my button renders as you would expect:
Color="{Binding RelativeSource={RelativeSource AncestorType=custom:GradientButton},Path=BackgroundColor}"
Of course, this makes nary a whit of sense. I'm convinced that this is a bug (or at least one of those things that man was not meant to know).

Color inversion in XAML

In my WP8 application I want to make a color inversion effect. I have no idea what tools I should use so I'll just explain what I want in a very general terms.
How it is supposed to work: say I have a UserControl that consists of black rectangle and some white text on top of it. I want to apply something to that user control that will invert colors of a part of UserControl that it covers. Some invisible rectangle that spans say 50% of UserControl and in that area background will be white and text will be black. I want it to be dynamic so I can change the area it covers at runtime.
Here's an image to illustrate this:
Inversion effect applied to a half of control.
I believe it's possible to achieve this by using two controls with same text, inverted colors and opacity mask but I wonder if this can be done in a more clean and direct way?
I think what you're looking for ideally would either be two TextBlocks with OpacityMask applied to the one on top like;
<Grid MaxWidth="100">
<TextBlock Text="Hey check it out we can change object gradients! yay!" Foreground="Red"
TextWrapping="Wrap" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<TextBlock Text="Hey check it out we can change object gradients! yay!" Foreground="Blue"
TextWrapping="Wrap" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock.OpacityMask>
<LinearGradientBrush StartPoint="0.1,0.1" EndPoint="0.75,0.75">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0.322" Color="Black"/>
<GradientStop Offset="0.739" Color="Transparent"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</TextBlock.OpacityMask>
</TextBlock>
</Grid>
Or you could just apply a LinearGradientBrush directly to the Foreground (or Background of other element) itself like;
<Border Width="100" Height="50">
<Border.Background>
<LinearGradientBrush StartPoint="0.062,0.552" EndPoint="0.835,0.548">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0.5" Color="White"/>
<GradientStop Offset="0.5" Color="Black"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.Background>
<TextBlock Text="Hello World!" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock.Foreground>
<LinearGradientBrush StartPoint="0.1,0.1" EndPoint="0.75,0.75">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0.5" Color="Black"/>
<GradientStop Offset="0.5" Color="White"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</TextBlock.Foreground>
</TextBlock>
</Border>
or gettin 80's style fancy;
<Border Width="100" Height="50">
<Border.Background>
<LinearGradientBrush StartPoint="0.472,0.047" EndPoint="0.47,0.942">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0.541" Color="White"/>
<GradientStop Offset="0.548" Color="Black"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.Background>
<TextBlock Text="Hello World!" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock.Foreground>
<LinearGradientBrush StartPoint="0.472,0.047" EndPoint="0.47,0.942">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0.631" Color="Black"/>
<GradientStop Offset="0.635" Color="White"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</TextBlock.Foreground>
</TextBlock>
</Border>
Give that a shot, hope this helps.

Dynamically creating Expression Blend controls in WPF C#

I have created a button in Expression Blend 4. I want to dynamically create instances of this button at run time.
The code for the button is below:
<Button Content="Button" HorizontalAlignment="Left" Height="139" Margin="46,107,0,0" VerticalAlignment="Top" Width="412" Grid.ColumnSpan="2">
<Button.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Button.Background>
</Button>
In addition to the supplying the code can you put comments in explaining what you are doing so that I can learn the general principle.
I know this is a simple question so I have been reading up places such as: Expression blend & WPF, Is there a way to "extract" WPF controls of Expression Blend?, and http://social.msdn.microsoft.com/forums/en-US/wpf/thread/ffa981b8-9bba-43a2-ab5e-8e59bc10fc0d/ unfortunately none of these have helped.
In your WPF application you should have a App.xaml file, in there you can add Styles that are to be used thoughout your UI.
Example:
<Application x:Class="WpfApplication8.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<!--The style for all your buttons, setting the background property to your custom brush-->
<Style TargetType="{x:Type Button}"> <!--Indicate that this style should be applied to Button type-->
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
</Application>
Or if you dont want to apply to all buttons, you can give your Style a Key so you can apply to certain Buttons in your UI
<Application x:Class="WpfApplication8.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<!--Add a x:Key value so you can use on certain Buttons not all-->
<Style x:Key="MyCustomStyle" TargetType="{x:Type Button}">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
</Application>
To use this Style on a Button, just add a binding to the Style property of the Button
<Button Style="{StaticResource MyCustomStyle}" />
this will apply the Style just to this Button
Or if you really want to do it in code behind you can just add the Brush you want to the background
Button b = new Button
{
Background = new LinearGradientBrush(Colors.Black, Colors.White, new Point(0.5, 1), new Point(0.5, 0))
};
its very easy to translate xaml to code because xaml uses the exact same property names, like the code brush I posted above:
new LinearGradientBrush(Colors.Black, Colors.White, new Point(0.5, 1), new Point(0.5, 0))
is ....
Brush(firstColor,secondColor,StartPoint EndPoint)
Xaml just accesses properties in the button, they will all have the same names in C#.

Categories

Resources