How to access a WPF control located in a ControlTemplate? - wpf

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".

Related

Why is Grid.Row attached property specified without any enclosing Grid?

I am reading code from a WPF project which has the following XAML code (I have omitted the boilerplate parts):
<Window x:Class="AdornedControlSample.Window1"
...
x:Name="window"
...
>
<Canvas
Grid.Row="1"
x:Name="canvas"
>
...
Please note that there is no Grid defined whatsoever anywhere in this XAML file. So, my questions are:
What is the purpose of the Grid.Row="1" within the Canvas definition?
What purpose does x:Name="window" serve? I have not seen anything in the code-behind that references a window. (There are references to Window1, though.)
Question 1
In your example, Grid.Row has no purpose at all. You gave it a value, but that value will not be used because there is no Grid around the Canvas. It also doesn't give an error because it is a valid property/value.
The RowProperty is defined in the Canvas class.
Question 2
It gives a unique name (within the window itself) to the UI-element. For now, it has no purpose. So you can remove it safely. However, if you want to access the window in the code-behind of the view, the x:name serves a purpose. But you can also access the window via the this keyword.

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>

Sharing controls in ResourceDictionary

I have a grid that I want to share in several places, the grid has several controls (textboxes, labels, textblocks and buttons). I placed it in my ResourceDictionary like so:
<Grid Background="LightGray" x:Key="gridShare">
<!-- other controls here -->
</Grid>
But when I use it in more than one place I get an exception saying:
"Specified element is already the logical child of another element. Disconnect it first."
Which makes sense, so I'm wondering if there's any other way to achieve this.
Thanks in advance.
If I correctly understand what you're trying to do (present the exact same composite control in multiple places), you can put the grid in a ControlTemplate and apply it to ContentControls:
In the ResourceDictionary:
<ControlTemplate x:Key="frequentlyUsedTemplate">
<Grid>
[...]
</Grid>
</ControlTemplate>
In a UserControl:
<ContentControl Content="[...]" Template="{StaticResource frequentlyUsedTemplate}"/>
What you want to do is create a custom control with this as a template, then use an instance of this custom control instead of the Grid itself. Alternately create a UserControl and use instances of it.

Using custom control's alias in XAML

In a WPF (Silverlight) UserControl I have lots of custom control instances:
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:My="clr-namespace:MyProject;assembly=MyAssembly" Name="uc1" >
...
<Grid x:Name="grid1">
...
<My:CustControl x:Name="name1" />
<My:CustControl x:Name="name2" />
<My:CustControl x:Name="name3" />
...
</Grid>
In MyProject I have not only the CustControl implemented, but also the CustControl1, the CustControl2, the CustControl3 and the like. Sometimes, I have to use another custom control type in the UserControl. Can I have only one place for changing custom control's name instead of changing it in all the numerous references in the XAML?
In other words, can I use an alias for those multiple custom control references inside my XAML and one place where I define the real CustomControl class' name for that alias?
Or maybe some other approach for solving this problem?
You could write a simple empty wrapper class, which would inherit the control you currently use. Then in XAML you would use this wrapper instead of the specific CustControl. At any time, you could change the base class for the wrapper at one single place.
But I must say: this design stinks. Are you sure you couldn't solve this issue with global styles/templates for one CustControl class?

WPF set named style elements from code behind?

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.

Resources