WPF set named style elements from code behind? - wpf

I have a user control that applies a style to button, with the style containing a ControlTemplate section. Within the ControlTemplate, there are various UI elements such as an Ellipse and a Path.
If I give those elements -- the Ellipse and Path -- a name with x:Name, can I access them from code behind?
It appears the style's Ellipse and Path are not visible because I get a compile error (C#).
Am I going about this the wrong way?

Because a template can be instantiated multiple times, it's not possible to bind a generated member via x:Name. Instead, you have to find the named element within the template applied to a control.
Given simplified XAML:
<ControlTemplate x:Key="MyTemplate">
<Ellipse x:Name="MyEllipse" />
</ControlTemplate>
You would do something like this:
var template = (ControlTemplate)FindResource("MyTemplate");
template.FindName("MyEllipse", myControl);
Or even more simply:
var ellipse = (Ellipse)myControl.Template.FindName("MyEllipse", myControl);
You can read about FrameworkTemplate.FindName.
Some examples and discussion here, here and here.

Related

Setting FontFamily of TextBox applicationwide

I have a big project at hand which involves a large amount of views and usercontrols. Additionaly, I want to set the FontFamily of every element to a certain font.
This works with most of the usercontrols like textBlocks, buttons and labels. Sadly this does not hold for textBoxes. They remain unchanged.
Before I create the whole GUI, I am overriding most of the metadata for elements containing text:
TextElement.FontFamilyProperty.OverrideMetadata(typeof(TextElement),
new FrameworkPropertyMetadata(new FontFamily("Calibri")));
TextBlock.FontFamilyProperty.OverrideMetadata(typeof(TextBlock),
new FrameworkPropertyMetadata(new FontFamily("Calibri")));
After a bit of searching, I found this article using the same method: http://blog.davidpadbury.com/
It clearly states at the end:
"In the above image you’ll see that we’ve successfully change the font on the text blocks, labels and buttons. Unfortunately the font inside the TextBox remains unchanged, this is due to it receiving it’s FontFamily property from it’s base class Control. Control adds itself as an Owner of the TextElement FontFamilyProperty but specifies it’s own metadata which we are then unable to override."
It also suggests to create a control template, which then sets the fontFamily. Is there another way? I want to set the fontFamily programmatically at the start without using XAML or creating a controlTemplate and using it as a base template for every textBox.
Thanks in advance.
You can declare a Style without the x:Key property and it will apply to all controls of that type:
<Style TargetType="{x:Type Control}">
<Setter Property="FontFamily" Value="Calibri" />
</Style>
Alternatively, you can simply set this on the MainWindow definition which will affect most elements:
<Window TextElement.FontFamily="Calibri" ...>
...
</Window>
Ahhh... I've just noticed your condition of not using xaml... sorry, I should have looked closer the first time.
UPDATE >>>
After a little research, it seems that you can do this in code like this:
Style style = new Style(typeof(TextBlock));
Setter setter = new Setter();
setter.Property = TextElement.FontFamilyProperty;
setter.Value = new FontFamily("Calibri");
style.Setters.Add(setter);
Resources.Add(typeof(TextBlock), style);
Unfortunately, you'd have to do other Styles for other types of controls too.
UPDATE 2 >>>
I just thought of something... that previous example just set the Style into the local Resources section which would be out of scope for your other modules. You could try setting the Style to the Application.Resources section which has global scope. Try replacing the last line of the code example above to this:
App.Current.Resources.Add(typeof(TextBlock), style);

Not found SetResourceReference in new Groupbox I'm creating in code behind

I want to set a reference to a Template in a groupbox I'm creating in the code behind.In a example I found, they mostly use SetResourceReference method. However, I see that this method doesn't seem to exist on the new Groupbox property in creating.
XAML:
<ControlTemplate TargetType="GroupBox" x:Key="GBTemplate">
Code Behind:
groupbox gb_plat
gb_plat= create GroupBox
gb_plat.SetResourceReference(Groupbox.TemplateProperty,"GBTemplate")
This method have come out with 2 errors,
1.Undefined variable: TemplateProperty
2. Can't find element SetResourceReference
If you are setting the control template for your groupbox then you will have to do:
gb_plate.Template = Resources["GBTemplate"] as ControlTemplate;

Child elements in XAML

I'm just learning XAML so bear with me.
When you nest an element in XAML it seems like that element is set to the "Child" property of the parent UI.
However in the following code the child element is set to the value. That sort of makes sense - kinda.
However then Border Element below was set to the ControlTemplate, yet ControlTemplate has no Child element, so can someone tell me what exactly is the relationship between the Border and ControlTemplate below? May be you could re-write this snippet in c# as an explanation.
<Setter Property="Template" >
<Setter.Value>
<ControlTemplate TargetType="dtp:PickerSelectorItem">
<Border HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="Picker">
<VisualState x:Name="Focused">
<Storyboard>
<!-- There is more code but snipped for irrelevance-->
Also how does the XAML compiler makes sense of what the child element actually does? Ie, how does it know that the child element should be set to the "Child" property, whereas other times it'd be set to the "Value" property as seen above.
The XAML parser uses the ContentPropertyAttribute to determine how to handle child xaml elements. For example, if you look at the following two base controls you'll see how their usage is:
ContentControl:
[ContentPropertyAttribute("Content")]
public class ContentControl : Control, IAddChild { ... }
ItemsControl:
[ContentPropertyAttribute("Items")]
public class ItemsControl : Control, IAddChild, IContainItemStorage { ... }
It used to be that you would implement the IAddChild interface, but that is obsolete now. Also, the xaml parsing engine can recognize if your "content" property is pointing to a single object or a collection of objects. Basically, if you want to create your own custom control, make sure to use the correct attribute to control how your child(ren) are handled.
The XAML parser knows which property to assign the inner XAML to according to the parent object type. For example, nesting XAML under a ContentControl will call parentContentControl.Content = child, while for ItemsControl it will add the children to the Items collection: parentItemsControl.Items.Add(child).
I guess the same is true for FrameworkTemplate objects (which ControlTemplate derives from), and child controls for these types are assigned to the VisualTree property.
In your question, ControlTemplate uses FrameElementFactory to construct the controls defined on XAML and builds the visual tree which is finally assigned to ControlTemplate's VisualTree property.

Control Template that wraps another control in XAML

I want to create a custom control that extends a built-in control and then has a template that wraps that control with a container?
The C# class:
class ExtraBorderTextBox : TextBox {}
The Xaml:
<ControlTemplate>
<Border>
<TextBox/>
</Border>
</ControlTemplate>
That doesnt' work because the TextBox in the control template isn't my custom control, it is a second instance.
I need access to the properties and events on TextBox, having a different parent doens't make sense, I would have to replicate all of that in my class.
This is a simplified example; imagine Border being replaced with a ContentControl that has a 50 line control template for itself. I guess I want something like ContentPresenter (like I have in the ContentControl), but there isn't anything like a "ControlPresenter". Right? Am I missing something, or am I stuck with replicating my content control for the TextBox, or replicating the TextBox behaviour and presentation for my content control?
Thanks.
Update:
There is an answer here that does what I want, which is to copy the default template for System.Windows.Controls.TextBox. This will do what I want; I can insert my container into that. I was hoping that WPF provided a way that is more maintainable to do something like this, something like a adorner/decorator pattern.
Is there any way to make this better in some way? Would using something like Expression Blend make this so that I don't have to hand-edit the XAML pasted in from the webpage?
You could use the default control template as a base and modify it. The default control templates can be found here: http://msdn.microsoft.com/en-us/library/aa970773.aspx
If I understood you right, you want to inherit from TextBox, do some overriding, and use that new class in XAML.
If so:
1) declare the xmlns namespace at the top of your file:
<UserControl
...
xmlns:local="TheAssemblyWhereExtraBorderTextBoxResides"
...>
2) use your custom textbox:
<ControlTemplate>
<Border>
<local:ExtraBorderTextBox />
</Border>
</ControlTemplate>

How to access a WPF control located in a ControlTemplate?

Usually, the WPF controls are declared in the .xaml files and not in the code behind (.xaml.cs files). However, sometimes I need to use some of those controls in code behind in order to manipulate them. How can I get the handle of such a control if it "resides" in the xaml file?
You can use the FindName() method of the ControlTemplate class.
// Finding the grid that is generated by the ControlTemplate of the Button
Grid gridInTemplate = (Grid)myButton1.Template.FindName("grid", myButton1);
I'm unsure about what you're asking, so I'll try and answer both instances that I'm interpreting as your question.
1)
If you want to declare an explicit control, and then edit it directly, all you have to do is set the name property like such:
<Canvas x:Name="myCanvas"/>
You can then access the canvas through the Name as such:
myCanvas.Background = Brushes.Blue;
2)
If you're looking to declare a generic control, and then use it multiple times, you can do it like this:
<Window>
<Window.Resources>
<Ellipse x:Key="myEllipse" Height="10" Width="10">
</Window.Resources>
</Window>
You can then access that predefined control using this syntax in code:
Ellipse tempEllipse = (Ellipse)FindResource("MyEllipse");
If you want to use the Resourse as a template for multiple controls, add x:Shared="false".

Resources