How to get ContentPresenter in code behind? - c#

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;
}

Related

strange behavior of TemplateBinding

I am trying to create a custom control. After setting the cornerRadius property to be bound it works only for the default radius which is "8". Here is the definition of property:
public CornerRadius cornerRadius
{
get { return (CornerRadius)GetValue(cornerRadiusProperty); }
set { SetValue(cornerRadiusProperty, value); }
}
public static readonly DependencyProperty cornerRadiusProperty =
DependencyProperty.Register("cornerRadius", typeof(CornerRadius), typeof(expandMenuA), new PropertyMetadata(new CornerRadius(8)));
In the main border, everything works fine, but for the second it works only for default "8" when I change a value in different project, which has a reference to this custom control, nothing happens, and it stays the default:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:expandMenuA">
<Style TargetType="{x:Type local:expandMenuA}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:expandMenuA}">
<Border CornerRadius="{TemplateBinding cornerRadius}" x:Name="mainBorder" Background="#232323">
<StackPanel>
<Button Height="{TemplateBinding expanderHeight}" Background="#2d2d2d">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border CornerRadius="{TemplateBinding local:expandMenuA.cornerRadius}" Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Left" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Button.Template>
<Button.Content>
<TextBlock Text="{TemplateBinding menuTitle}" Foreground="White" Margin="10, 0, 0, 0" Background="{TemplateBinding BorderBrush}"/>
</Button.Content>
</Button>
<ContentPresenter/>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
TemplateBinding in ControlTemplate for Button will seek Button's property, which will resolve to default DP value. use Binding with RelativeSource:
<ControlTemplate TargetType="Button">
<Border CornerRadius="{Binding Path=cornerRadius, RelativeSource={RelativeSource AncestorType={x:Type local:expandMenuA}}}" Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Left" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>

Blur The Background Of A Custom Control

The title might not be clear but I will explain now. I have a custom control that represents a modal (inspired by SingltonSean), When I trigger a command, it shows this modal that covers the rest of the elements behind it, it somewhat acts like a popup. Now I want everything behind it to be blurred. How can I achieve that?
This is my modal custom control:
<Style TargetType="{x:Type viewModel:Modal}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type viewModel:Modal}">
<ControlTemplate.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</ControlTemplate.Resources>
<Grid Visibility="{TemplateBinding IsOpen, Converter={StaticResource BooleanToVisibilityConverter}}">
<Grid.Background>
<SolidColorBrush Color="Black" Opacity="0.025" />
</Grid.Background>
<Border HorizontalAlignment="Center" VerticalAlignment="Center" UseLayoutRounding="True" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid.OpacityMask>
<VisualBrush Visual="{Binding ElementName=border}" />
</Grid.OpacityMask>
<Border x:Name="border" Background="White" CornerRadius="20" />
<Grid Width="500" Height="600">
<StackPanel Width="300" HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Padding="10 5" Content="Close Modal" />
</StackPanel>
</Grid>
</Grid>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You need to apply blur where content is:
<Grid>
<Grid>
<Grid.Effect>
<BlurEffect Radius="20"/>
</Grid.Effect>
<TextBlock Text="Some content I want to blur"/>
</Grid>
<TextBlock Text="Popup"/>
</Grid>
If you want to make a control, which is able to blur something before in visual tree, you still need to apply effect to it. One possibility is to use attached behavior:
public static class Behavior
{
public static UIElement GetBlurTarget(DependencyObject obj) => (UIElement)obj.GetValue(BlurTargetProperty);
public static void SetBlurTarget(DependencyObject obj, UIElement value) => obj.SetValue(BlurTargetProperty, value);
public static readonly DependencyProperty BlurTargetProperty =
DependencyProperty.RegisterAttached("BlurTarget", typeof(UIElement), typeof(Behavior), new PropertyMetadata((d, e) =>
{
if (e.NewValue is UIElement element)
element.Effect = new BlurEffect { Radius = 20 };
}));
}
Then layout should be like this:
<Grid>
<Grid x:Name="container">
<TextBlock Text="Some content I want to blur" />
</Grid>
<TextBlock Text="Popup" local:Behavior.BlurTarget="{Binding ElementName=container}"/>
</Grid>
Both cases will produce same result:

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?

WPF Template.FindName return always null

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);
}
}

Categories

Resources