WPF Template.FindName return always null - c#

Template
<Style TargetType="{x:Type local:Viewport}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:Viewport}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ItemsPresenter/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<Canvas x:Name="PART_Canvas" IsItemsHost="True"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
And the code in OnApplyTemplate
content = this.Template.FindName("PART_Canvas", this) as FrameworkElement;
the content returns always null, why it doesn't work?
if I replace with this, the program quits directly
content = this.ItemsPanel.FindName("PART_Canvas", this) as FrameworkElement;

With FindName you can find only elements declared in a Template. ItemsPanel is not part of that template. ItemsControl puts ItemsPanel into ItemsPresenter place holder via which you can access your Canvas but first you need to name ItemsPresenter in your template:
<ControlTemplate TargetType="{x:Type local:Viewport}">
<Border>
<ItemsPresenter x:Name="PART_ItemsPresenter"/>
</Border>
</ControlTemplate>
then, using VisualTreeHelper get your Canvas, but I think earliest place when you can call code below is when FrameWorkElement is Loaded. This is my example:
public class MyListBox : ListBox
{
public MyListBox()
{
AddHandler(FrameworkElement.LoadedEvent, new RoutedEventHandler(ControlIsLoaded));
}
private void ControlIsLoaded(object sender, RoutedEventArgs e)
{
var canvas = VisualTreeHelper.GetChild(this.Template.FindName("PART_ItemsPresenter", this) as DependencyObject, 0);
}
}

Related

ItemTemplate Wrapper in UserControl Based on ItemsControl

I am working on my own user control which derives from ItemsControl. To give a brief description of what I want to achieve here is what the XAML looks like right now:
<ItemsControl x:Class="MyApp.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MyApp"
mc:Ignorable="d"
d:DesignHeight="60" d:DesignWidth="600">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Template>
<ControlTemplate>
<Grid>
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ItemsPresenter/>
</Border>
</Grid>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
This presents my data the way I want to, in a horizontal StackPanel, nothing fancy. In the visual tree you'll see this:
ItemsControl
[...Other Visual Item in The Tree...]
ItemsPresenter
ContentPresenter
Item1 (ItemTemplate)
ContentPresenter
Item2 (ItemTemplate)
Now I would like to modify the XAML of my user control so that the tree would look like this:
ItemsControl
[...Other Visual Item in The Tree...]
ItemsPresenter
ContentPresenter
SomeContainerDefinedInMyUserControlXAML
Item1 (ItemTemplate)
ContentPresenter
SomeContainerDefinedInMyUserControlXAML
Item2 (ItemTemplate)
The goal here is to have a wrapper container around the templated item. Its behavior would be bound to internal properties of the UserControl, allowing me to define item behaviors immune to the ItemTemplate choice made by the user of my control.
I was trying to add DataTemplates on ContentPresenter in ItemsControl.Resources, but that failed. Could someone help me out here? :)
You may create a derived ContentPresenter or ContentControl in the ItemControl's Get​Container​For​Item​Override method:
public class MyContainer : ContentControl
{
}
public class MyItemsControl : ItemsControl
{
protected override DependencyObject GetContainerForItemOverride()
{
return new MyContainer();
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
return item is MyContainer;
}
}
I'd also suggest to move the derived ItemsControl's XAML to a default Style in Themes\Generic.xaml (as done by default for custom controls):
<Style TargetType="local:MyItemsControl">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MyItemsControl">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ItemsPresenter/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Also add
static MyItemsControl()
{
DefaultStyleKeyProperty.OverrideMetadata(
typeof(MyItemsControl),
new FrameworkPropertyMetadata(typeof(MyItemsControl)));
}
to the control's code.

wpf change ComboBox border width programatically

How can I change the border width of a combobox programatically.
I tried :
combobox.BorderWidth = ...
but It did not work
Ilan
I found out from your answers the following solution :
<Window.Resources>
<Style x:Key="ComboBoxStyleKey" x:Name="ComboBoxStyle" TargetType="{x:Type ComboBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid>
<Border x:Name="ContentPresenterBorder"
BorderBrush="{TemplateBinding Property=BorderBrush}"
BorderThickness="{TemplateBinding Property=BorderThickness}"
Background="{TemplateBinding Property=Background}"
CornerRadius="3">
<Grid>
<ToggleButton x:Name="DropDownToggle" />
<ContentPresenter x:Name="ContentPresenter" />
</Grid>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
Using this style I managed to change the border thickness
But now I have a new problem
The ToggleButton and the ContentPresenter does not work.
I want them to have the default behavior.
Is there a way to assign the default behavior to them (Something like style="default style")?
Thanks,
You can do it like this,
this.comboBox.BorderThickness = new Thickness(1, 1, 1, 3);

Custom control including wpf TabControl fails bindings with ElementName

I've a CustomControl which has an Items property. An internal TabControl is bound to it like this:
<Style TargetType="{x:Type local:CustomControl1}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomControl1}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<TabControl Name="PART_TabControl"
>
</TabControl>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
OnApplyTemplate:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
TabControl tabControl = Template.FindName("PART_TabControl", this) as TabControl;
foreach (var item in Items)
{
Dispatcher.BeginInvoke(new Action(() => tabControl.Items.Add(item)), System.Windows.Threading.DispatcherPriority.ContextIdle);
}
}
To demonstrate the problem I've added "Dispatcher.BeginInvoke". The problem occurs when tabitems are added after a while. The usage below results in a binding failure:
<ccl:CustomControl1 Name="cc1">
<ccl:TabItemCollection>
<TabItem Header="Tab1">
<TextBox Name="txtData" />
</TabItem>
<TabItem Header="Tab2">
<TextBlock Text="{Binding ElementName=txtData, Path=Text}" />
</TabItem>
</ccl:TabItemCollection>
</ccl:CustomControl1>
I noticed that after snooping the textblock, the problem is fixed. And from the source code I see this code does the job:
BindingExpression expression = BindingOperations.GetBindingExpression(dObj, property);
if (expression != null && !expression.HasError && expression.Status != BindingStatus.Active)
{
dObj.ClearValue(property);
BindingOperations.SetBinding(dObj, property, expression.ParentBindingBase);
}
As I've many controls in tab items this kind of "fix binding" code causes a lot of performance issues.
Is there any workaround for that?

Creating a Custom XAML TextBox for Windows 8 Store app

I trying to create a custom textbox control in windows 8 XAML.
I've right clicked on my project -> Add -> New Item
I've then selected Templated Control and entered the name MyTextBox
I have then made this class derive from TextBox and added a test Method called Hello. So it now looks like this:
public sealed class MyTextBox : TextBox
{
public MyTextBox()
{
this.DefaultStyleKey = typeof(MyTextBox);
}
public void Hello()
{
//Do something here!
}
}
Within my project a file has also been added called Generic.xaml with the following style:
<Style TargetType="local:MyTextBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MyTextBox">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
So within here I tried adding BasedOn="TextBox" with the Style tag:
<Style TargetType="local:WatermarkTextBox" BasedOn="TextBox">
This doesn't work.
What do I need to do to create this custom TextBox and then how do I use it within my XAML
This is for a Windows RT so the XAML maybe different from WPF.
One of best article and my favorite : Building a deployable custom control for XAML Metro style apps
Here is MSDN sample app : XAML user and custom controls sample
UPDATE 1 :
<Style TargetType="local:WatermarkTextBox" BasedOn="TextBox">
You don't have to specify BasedOn attribute. If you are developing watermark textbox then I would recommend you to check Callisto's watermark textbox code.
Generic.xaml
WatermarkTextBox.cs
BasedOn values can be pointed through StaticResources like BasedOn={StaticResource DefaultTextBoxStyle}
I have a NumericOnlyTextBox implementation. Instead of making it BasedOn textbox I've added a textbox and all its dependencies.
In example:
<Style TargetType="local:NumericOnlyTextBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:NumericOnlyTextBox">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<StackPanel>
<TextBox x:Name="TextBoxPart"
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EnteredText, Mode=TwoWay}"
MaxLength="{TemplateBinding MaxLength}">
</TextBox>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="IsTabStop" Value="False"></Setter>
</Style>

How to get ContentPresenter in code behind?

I have the code below under Themes\Generic.xaml
Now I need to get cPresenter in code behind how I can do it?
In fact I try to convert Silverlight implementation into WPF code.
And I want to use something like Silverlight has:
FrameworkElement cp = this.GetTemplateChild("cPresenter") as FrameworkElement;
Themes\Generic.xaml
<Style TargetType="local:Marquee">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:Marquee">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer Width="Auto" Height="Auto" HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch" VerticalScrollBarVisibility="Disabled">
<Canvas>
<ContentPresenter x:Name="cPresenter" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Canvas>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
There's no reason that shouldn't work in WPF also.
Put this in your code for Marquee.
private ContentPresenter cPresenter;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.cPresenter = this.GetTemplateChild("cPresenter") as ContentPresenter;
}

Categories

Resources