I've been using the same bit of code for several versions of my app with no problems, but I'm now mysteriously receiving NullRerefenceExceptions with the following:
this.Loaded += delegate {
deleteBrush = new DeleteBrushAdorner( background );
AdornerLayer al = AdornerLayer.GetAdornerLayer( background );
al.Add( deleteBrush ); // null ref here??
};
background is just a Border element.
My two thoughts on what could be causing it are a) switching to .NET 4.0, and b) placing instances of the above element (which is a UserControl) in an ItemsControl.
Oddly this doesn't happen all the time, and it's hard to predict when it will happen, so it's not reliable.
In my case I had a class that is based on Window and GetAdornerLayer() returned null. It turned out that the ControlTemplate for my derived class did not contain the AdornerDecorator. Adding that as the top level in the ControlTemplate solved the issue.
<Style TargetType="my:MyWindow" BasedOn="{StaticResource {x:Type Window}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="my:MyWindow">
<AdornerDecorator>
<DockPanel ...>
</DockPanel>
</AdornerDecorator>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The docs for AdornerLayer.GetAdornerLayer specify:
If no adorner layers are found, the method returns null.
So my guess is that there are no adorner layers... do you have any reason to believe that this shouldn't be the case? What guarantee are you currently relying on that there will be an adorner layer in the visual tree?
I'm curious as to whether or not this was really solved. An AdornerDecorator provides an AdornerLayer for element below it -- and everything will be below it. It is a decorator, meaning it has a Child that is the content. That content is being provided with an AdornerLayer. So, if you put an AdornerDecorator in your XAML and the child is the border, the border does have an AdornerLayer.
Furthermore, Window defines an AdornerDecorator as the top of the visual tree so any element in a Window will have an AdornerLayer above it. So, if your conent above was in a Window...
Related
I currently have a class that inherits from Control and I give it a custom shape by setting its style to a style defined in the xaml resources:
<Style x:Key="ParameterStyle" TargetType="{x:Type local:ParameterUI}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ParameterUI}">
<Canvas>
<Ellipse
x:Name="component"
HorizontalAlignment="Left" Height="50"
Margin="0,0,0,50" Stroke="#FF8E8E9F"
VerticalAlignment="Top" Width="50"
StrokeThickness="1.5" Fill="#FF202020"/>
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I was able to define all sorts of mouse events on this control and get it to behave as I want. Now, I want to dynamically add nested controls inside this control, and I want each one of those nested controls to have custom shapes, and mouse events etc, all defined at runtime. Almost none of the UI is static so its all defined at runtime. I tried adding controls, buttons as visual children, logical children, and nothing shows up in the UI. I don't even see anything in the live visual tree. What am I doing wrong here ? Should I be taking a different approach ? How can I access the Style > Setter.Value > ControlTemplate > Canvas at runtime to add more content to it ? Also, the content is different for each instance of the control.
I saw this post which asks a very similar question but the answer is a verbal description and I don't know how to implement this. I also found many solutions that write static UI in xaml but when I try to recreate that in C# it just doesn't work. I couldn't find any other examples on how to do this. Any help is appreciated, thanks!
You'll save yourself a lot of headache in future if you do things properly and use data-binding instead of manipulating the GUI elements directly, but if you insist...
You need to use the Visual Tree Helper to traverse the control in order to find the templated framework element. You may also may need to call ApplyTemplate first to make sure that your control has been templated and is ready to access:
yourTemplatedControl.ApplyTemplate();
var canvas = VisualTreeHelper.GetChild(yourTemplatedControl, 0) as Canvas;
var ellipse = new Ellipse();
ellipse.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
ellipse.VerticalAlignment = System.Windows.VerticalAlignment.Top;
ellipse.Width = 50;
ellipse.Height = 50;
ellipse.Margin = new Thickness(0, 0, 0, 50);
ellipse.Stroke = Brushes.Blue;
ellipse.StrokeThickness = 1.5;
ellipse.Fill = Brushes.Yellow;
canvas.Children.Add(ellipse);
In this code I've just assumed that the first element in the template would be a Canvas, in practice you might prefer to search the visual tree for your canvas element (see here and here for more info).
Most of the tutorials and questions I see are about restyling the listbox to look different, but I'm interested in adding additional controls to make it behave differently. I initially started out trying to make the list builder control out of a checkbox list, but found myself too deep. I decided to abstract and start with a smaller problem.
What I am looking to do first, to get a better understanding of how this works is add "up" and "down" buttons next to the control. I think this can all be done in xaml, so to try and pressure myself to stick to that I'm working in Kaxaml.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<!-- ListBox Order Button Style
Col 1
Listbox
Col 2
Buttons Up and Down
-->
<Style x:Key="{x:Type ListBox}" TargetType="{x:Type ListBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Rectangle Fill="Yellow"/>
<!--<ListBox></ListBox>-->
</Grid>
<StackPanel Grid.Column="1">
<Button>Up</Button>
<Button>Down</Button>
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<Grid>
<ListBox>
<TextBlock>Value 1</TextBlock>
<TextBlock>Value 2</TextBlock>
<TextBlock>Value 3</TextBlock>
<TextBlock>Value 4</TextBlock>
</ListBox>
</Grid>
</Page>
I am currently hung up on a few things.
1) When I try to use a ListBox where the Yellow Rectangle is I start getting infinite loop problems.
2) I'm not sure how to connect the buttons to the listbox once it is there. I think Triggers is the answer, but I don't have much experience with them.
Your infinite loop can be addressed by not relying on the TargetType to apply the style. Instead, apply the style explicitly via a named key (i.e. something other than {x:Type Listbox}). That way the style is applied only when you specifically want it to be applied.
"Connecting" the buttons can be done a variety of ways. The simplest would be to handle the Button.Click event and perform whatever action you want there.
All that said, I think you're going about this the wrong way. Let a ListBox be a ListBox; don't try to make it into something it's not. If you want a reusable control that adds functionality around a ListBox, like buttons to control the contents of the ListBox, you should probably be authoring a UserControl, which is essentially a composite control made up of whatever you want.
Doing so will give you a lot more control over the appearance of the control. You'll also have the opportunity to declare dependency properties on your control that are specific to exactly what that control needs to support (something you can't do just with a Style). Yes, it also means you'll have to expose properties of contained elements via new properties in your UserControl that effectively delegate to the contained elements, but that's a small price to pay for the flexibility and relative simplicity of creating the UserControl in the first place.
I'd like to understand which properties of an xaml Control are applied to the ControlTemplate of that Control. F.e. If I create a Control based on the Window Class like this:
(This is very simplified — It doesn't make sense in the current state I know...)
public class BaseWindow : Window {
public BaseWindow() { }
}
And the Template:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:shell="clr-namespace:Microsoft.Windows.Shell;assembly=Microsoft.Windows.Shell"
xmlns:local="clr-namespace:Arctic">
<Style TargetType="{x:Type local:BaseWindow}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:BaseWindow}">
<Grid Background="Transparent">
<Border Background="{TemplateBinding Background}"/>
<ContentPresenter/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Now, when I specify a BaseWindow Control in my app the Margin Property is applied to the BaseWindow without specifying a TemplateBinding. The Background isn't, I have to declare the TemplateBinding in the Template in order to achieve that. Can you explain to me why some properties are applied to the ControlTemplate by default and others are not?
My guess is, that the Window.xaml (Default Window Template of WPF) binds to some properties like the Margin but ignores some like Background. If that is true, then I do not understand why I can set the Background in a Window Control and it is applied to it. Seems like the Window binds to some properties and stops doing that when you derive from it…
This is probably completely wrong — I just wanted to explain my thoughts.
Window class inherit FrameworkElement and all its properties including FrameworkElement.Margin. Same goes for Control.Background. Your question is why you have to do something to have Control.Background working.
Answer is simple:
Margin is used in layouting, its functionality is implemented/provided by FrameworkElement and it happens always, invisible for you and disregarding of ControlTemplate (because all framework elements participate in layouting and use margin).
Background, in turn, is provided to be use by visuals. It's up to you how to use it, because only you know how control will looks like. Control doesn't know what to do with that property.
So, you have to use TemplateBinding to bind Background to some color in your ControlTemplate, but Margin works without need to do anything in control template.
I've created an AddressInput control for users to enter an address. The control will have a different look depending on where it's used, so I provided a DataTemplate property called AddressTemplate.
The default style looks like this:
<Style TargetType="{x:Type addressUI:AddressInput}">
<Setter Property="AddressTemplate"
Value="{StaticResource DefaultAddressTemplate}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type addressUI:AddressInput}">
<GroupBox Header="Address">
<ContentPresenter ContentTemplate="{Binding Path=AddressTemplate, RelativeSource={RelativeSource TemplatedParent}}"
Content="{Binding Path=Address, RelativeSource={RelativeSource TemplatedParent}}"
x:Name="PART_AddressPresenter" />
</GroupBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
All of my address data templates will contain a combo box for selecting the country (named "PART_CountriesList"). I need to have some code-behind action that fires when the selection changes, which means I need to hook the SelectionChanged event. Inside my AddressInput I need to find PART_CountriesList in the AddressTemplate.
I can get the "PART_AddressPresenter" ContentPresenter like this:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var addressPresenter = Template.FindName("PART_AddressPresenter", this) as ContentPresenter;
}
Now how do I get "PART_CountriesList" contained inside AddressTemplate?
I tried this:
var countriesList = AddressTemplate.FindName("PART_CountriesList", addressPresenter);
An exception is thrown because addressPresenter hasn't had its template applied yet. I know ContentPresenter has the OnApplyTemplate override, but it seems silly to extend it for this use.
I suppose if I were to extend ContentPresenter, I'd make a new reusable version that fires an event whenever the OnApplyTemplate method executes. This would probably solve my problem, but it seems crazy. Is there a better way?
I'm curious if someone has the "right" way to do this, but with FindName I always end up resorting to something like this:
Dispatcher.BeginInvoke( new Action(() =>
{
// Call FindName here
}), System.Windows.Threading.DispatcherPriority.Render );
This can cause flicker though because you're waiting until after the data templating is done and rendered to go exercise your code, so if what you're trying to do affects the look of the control this isn't always a great option.
I have a custom class that is derived from userControl, it describes what features a "service window" should have. It should also describe how a instance of this "service window" should look.
I have a style called serviceStyle. now whenever i create a new instance of the service window, i have to manually set the style in the XAML of the instance, when possible i want to avoid having to do this by basically forcing the style to all childs of service window to serviceStyle. Then i want to be able to do a fade animation on a textBlock in serviceStyle. Now since this is the style of a child of the "Service window" it seems unreachable...
In the meantime i found a way to Set the Style as a default style for all the service windows, this can be done by adding this line in the constructor:
DefaultStyleKeyProperty.OverrideMetadata(typeof(NetcarityService), new FrameworkPropertyMetadata(typeof(NetcarityService)));
Now i need to be able to get from the source to the element textBlock in the xaml of the ServiceWindowStyle....
Thanks in advance.
You don't need to do that in order to make a default style, you just define your style without a key. For example, this style applies to all button in an app (if the style is defined in app.xaml)
<Style TargetType="{x:Type Button}">
<Style.Setters>
<Setter Property="Margin" Value="6"></Setter>
<Setter Property="Padding" Value="6,3,6,3"></Setter>
</Style.Setters>
</Style>
With regards the children I would think you just need to write some code to detect when a child is added and set the default style for the child. I think you can just override OnVisualChildrenChanged.
PS. Setting DefaultStyleKeyProperty should be done in the static constructor