I've got nested custom ContentControls, and I'm having difficulty getting the inner control to render inside of the outer control. Here's an example of how the two controls are composed:
<Window ...>
<StackPanel>
<local:Outer> <!-- Nothing in here gets displayed -->
<local:Inner>
<TextBlock>Fahrvergnügen</TextBlock>
</local:Inner>
</local:Outer>
<local:Inner> <!-- This is displayed just fine -->
<TextBlock>some text</TextBlock>
</local:Inner>
</StackPanel>
</Window>
Inner and Outer's styles are defined as follows:
<ResourceDictionary ...>
<Style TargetType="{x:Type local:Outer}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:Outer}">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type local:Inner}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:Inner}">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
An added wrinkle is that Outer is using a ContentPropertyAttribute that points to a List:
[ContentPropertyAttribute("InnerItems")]
public class Outer : ContentControl
{
private List<Inner> innerItems = new List<Inner>();
static Outer()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Outer), new FrameworkPropertyMetadata(typeof(Outer)));
}
public List<Inner> InnerItems { get { return this.innerItems; } }
}
For completeness, here's Inner's definition, which is trivial:
public class Inner : ContentControl
{
static Inner()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Inner), new FrameworkPropertyMetadata(typeof(Inner)));
}
}
I'm guessing the problem is that Outer's content cannot be displayed with a simple <ContentPresenter /> but I'm not sure what to replace it with. Suggestions?
Related
Here is a working example of my issue: https://github.com/themimcompany/AttachedPropertyIssue
I have a Style for a Button that defines a ControlTemplate. Inside the ControlTemplate I have a TextBlock that has the same attached property. I want to set/bind the TextBlock's attached property to the value of the Button's attached property. The attached property is a simple int value.
How do I get this working? Is this possible to do in UWP? I get errors that don't give me indication on how to fix this. Like Unspecified error...
Here is my Style:
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<TextBlock local:Translation.TranslationModelID="{Binding Path=(local:Translation.TranslationModelID),
RelativeSource={RelativeSource TemplatedParent}}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Here is my attached property definition:
public class Translation
{
public static int GetTranslationModelID(DependencyObject obj)
{
return (int)obj.GetValue(TranslationModelIDProperty);
}
public static void SetTranslationModelID(DependencyObject obj, int value)
{
obj.SetValue(TranslationModelIDProperty, value);
}
public static readonly DependencyProperty TranslationModelIDProperty =
DependencyProperty.RegisterAttached("TranslationModelID", typeof(int), typeof(FrameworkElement), new PropertyMetadata(0));
}
Here is the button I'm trying to define, notice I want to assign the attached property of the button- then take the value in the ControlTemplate and assign it to the TextBlock's attached property (look at the style):
<Button local:Translation.TranslationModelID="1" />
Explaining once more: I have an attached property assigned to a Button and I would like to assign the value of that Button's attached property to the same attached property of a TextBlockin the ControlTemplate of that Button's Style. It's not working as I expect. I get Unspecified error.. exception at run time. How do I get this working?
Here is a working example of my issue: https://github.com/themimcompany/AttachedPropertyIssue
EDIT:
This approach is giving me a different error. The property 'TranslationModelID' was not found in type 'PassingAttachedPropertyInControlTemplater.Translation'. [Line: 17 Position: 40]
<Page.Resources>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<TextBlock local:Translation.TranslationModelID="{TemplateBinding local:Translation.TranslationModelID}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<Grid>
<Button x:Name="btn" local:Translation.TranslationModelID="1" />
</Grid>
I want to assign an attached property of the templated parent to an attached property of an element inside a ControlTemplate using TemplateBinding or regular Binding
For your requirement, you could create Templated Control that inherit button. Then set TextBlock Attached property in the code behind. Please check the follow segment code.
public sealed class CustomButton : Button, IDisposable
{
private long token;
private TextBlock Tbk;
public CustomButton()
{
this.DefaultStyleKey = typeof(CustomButton);
}
private void CallBackMethod(DependencyObject sender, DependencyProperty dp)
{
UpdateAttachedPropertyValue();
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
token = this.RegisterPropertyChangedCallback(AttachedPropertyTest.Translation.TranslationModelIDProperty, CallBackMethod);
Tbk = GetTemplateChild("TBK") as TextBlock;
UpdateAttachedPropertyValue();
}
public void Dispose()
{
this.UnregisterPropertyChangedCallback(AttachedPropertyTest.Translation.TranslationModelIDProperty,token);
}
private void UpdateAttachedPropertyValue()
{
AttachedPropertyTest.Translation.SetTranslationModelID(Tbk, AttachedPropertyTest.Translation.GetTranslationModelID(this));
}
}
Xaml
<Style TargetType="local:CustomButton" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:CustomButton">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<TextBlock x:Name="TBK"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I have a custom control based on a button, and I put an image inside. I can set the source of the image in the xaml, but if I try and bind it, it doesn't work.
Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomControl">
<Style TargetType="{x:Type local:MyCustomControl}" BasedOn = "{StaticResource {x:Type Button}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyCustomControl}">
<Grid x:Name="InnerGrid">
<Image Source="pathname"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
This works just fine, however if I replace the <Image Source="pathname"/> with <Image Source={Binding MyImage, RelativeSource={RelativeSource Self}}"/>, and reference a Delegate Property in the class, it breaks.
MyCustomControl.cs
public class MyCustomControl : Button
{
static DependencyProperty m_myimage = null;
private DependencyProperty MyImageProperty
{
get
{
return m_myimage;
}
}
public BitmapImage MyImage
{
get
{
return (BitmapImage)GetValue(MyImageProperty);
}
set
{
SetValue(MyImageProperty, value);
}
}
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
MyImage = new BitmapImage(new Uri(pathname));
}
private static void RegisterDependencyProperties()
{
if (m_myimage == null)
{
m_myimage = DependencyProperty.Register("MyImage",
typeof(BitmapImage), typeof(MyCustomControl), null);
}
}
static MyCustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl),
new FrameworkPropertyMetadata(typeof(MyCustomControl)));
RegisterDependencyProperties();
}
}
How can I get it to work?
Figured it out myself after about 2 hours. The xaml binding should look like,
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyCustomControl}">
<Grid x:Name="InnerGrid">
<Image Source="{Binding MyImage, RelativeSource={RelativeSource TemplatedParent}}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
Note that the binding relative resource went from RelativeSource={RelativeSource Self} to RelativeSource={RelativeSource TemplatedParent}
I have a listbox "listBox_Results" and several ItemTemplates(one of them ItemTemplateStyle1), in my ItemContainerStyle I'm setting Template property for item. So I want change my ItemTemplate in trigger "IsSelected".
(in common sense: I want my listboxitem change size and content display on selection, by dynamicly setting diffrent ItemTemplate)
Do you have any solutions?
Best regards
upd:If you think this question is unclear or not useful, most apreciate if tell you me why, before you minus
Code:
<ListBox Name="listBox_Results"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
BorderThickness="0"
Margin="2"
Grid.Row="0"
ItemTemplate="{StaticResource ItemTemplateStyle1}"
ItemsSource="{Binding}" >
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="{Binding Path=VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="Padding" Value="2,2,2,2"/>
<Setter Property="Margin" Value="2"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="Bd" Margin="1" SnapsToDevicePixels="true" CornerRadius="3" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" >
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Background" TargetName="Bd">
<Setter.Value>
#E1E1E1
</Setter.Value>
</Setter>
...
First, take out your inline styling and create a ResourceDictionary to keep things together. This will also help with the template switch I am suggesting.
In the Resource Dictionary, you will define the two templates that you want (the selected and unselected list item templates), the style of the list item and the list box itself. I am abbreviating the code, just to show how I would put the items together.
In the ResourceDictionary
<ControlTemplate x:Key="unselectedTemplate" TargetType="{x:Type ListBoxItem}">
<Grid>
<ContentPresenter />
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="selectedTemplate" TargetType="{x:Type ListBoxItem}">
<Grid>
<ContentPresenter Margin="3"/>
</Grid>
</ControlTemplate>
<Style x:Key="listboxItemStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="Template" Value="{StaticResource unselectedTemplate}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsSelected, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Template" Value="{StaticResource selectedTemplate}"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="listBoxStyle" TargetType="{x:Type ListBox}">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="ItemContainerStyle" Value="{StaticResource listboxItemStyle}"/>
</Style>
Then, when you are creating your list box on the page... just reference the list box style key.
<ListBox Name="listbox_Results" Style="{StaticResource listBoxStyle}" ItemsSource="{Binding}"/>
Make sure the ControlTemplates are defined before the styles, I found when I don't I run into errors. Also, this keeps your layout page cleaner, and the styles are easier to reuse if you need to use them again.
I uploaded a very basic example here.
You have to use an Data template selector which will select a particular data template according to the conditions in your selector.
You have to write the data templates in xaml with separate names and select them from the DataTemplateSelector class file.
For that you need to inherit from the base class DataTemplateSelector
I will share some sample code with you. Please check this and you will get an idea of how to use an item template selector.
XAML :
<Window x:Class="WpfApplication5.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication5"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="NormalUserDataTemplate">
<StackPanel>
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="PremiumUserDataTemplate">
<StackPanel Background="LightBlue">
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
<local:PremiumUserDataTemplateSelector x:Key="myPremiumUserDataTemplateSelector" />
</Window.Resources>
<Grid>
<ListView x:Name="myListView" ItemTemplateSelector="{StaticResource myPremiumUserDataTemplateSelector}">
</ListView>
</Grid>
</Window>
Code behind :
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<User> users = new List<User>();
for (int i = 0; i < 10; ++i)
{
var user = new User { ID = i, Name = "Name " + i.ToString(), Age = 20 + i };
if (i == 2 || i == 4)
{
user.IsPremiumUser = true;
}
users.Add(user);
}
myListView.ItemsSource = users;
}
}
public class PremiumUserDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
FrameworkElement elemnt = container as FrameworkElement;
User user = item as User;
if(user.IsPremiumUser)
{
return elemnt.FindResource("PremiumUserDataTemplate") as DataTemplate;
}
else
{
return elemnt.FindResource("NormalUserDataTemplate") as DataTemplate;
}
}
}
public class User
{
public int ID { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public bool IsPremiumUser { get; set; }
}
I try to make custom control based on DataGridTemplateColumn for using it in simple datagrid.
My XAML code looks:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dgNuCol="clr-namespace:Columns.DataGridNumberColumn"
xmlns:nb="clr-namespace:NumberBox" >
<Style TargetType="{x:Type dgNuCol:DataGridNumberColumn}">
<Setter Property="CellEditingTemplate">
<Setter.Value>
<ControlTemplate TargetType="{x:Type dgNuCol:DataGridNumberColumn}">
<nb:NumberBox Text="123"/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="CellTemplate">
<Setter.Value>
<ControlTemplate TargetType="{x:Type dgNuCol:DataGridNumberColumn}">
<StackPanel>
<TextBlock Text="123" HorizontalAlignment="Center" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
And my cs code:
namespace Columns.DataGridNumberColumn
{
public partial class DataGridNumberColumn : DataGridTemplateColumn
{
}
}
And I try to use it in my datagrid:
<dgNuCol:DataGridNumberColumn Header="Values" CanUserReorder="False"/>
Nothing happens. There are no any changes in my datagrid, just empty cell. What I am doing wrong?
UPDATE:
I try to make it programmatically:
public partial class DataGridNumberColumn : DataGridTemplateColumn
{
public DataGridNumberColumn()
{
FrameworkElementFactory textFactory = new FrameworkElementFactory(typeof(TextBox));
textFactory.SetValue(TextBox.TextProperty, "123");
DataTemplate textTemplate = new DataTemplate();
textTemplate.VisualTree = textFactory;
this.CellTemplate = textTemplate;
}
}
But, again, no any changes in datagrid.
UPDATE2:
<DataGrid>
<DataGrid.Columns>
<dgNuCol:DataGridNumberColumn Header="Values" CanUserReorder="False"/>
</DataGrid.Columns>
</DataGrid>
I'm sure this is a simple thing to do, and to look up, but I've yet to have any luck. Basically I want to style a WPF Slider such that when used I can give it two additional strings to display.
The new control looks like this:
public class SliderPicker : Slider
{
public string LeftLabel
{
get { return (string)GetValue(LeftLabelProperty); }
set { SetValue(LeftLabelProperty, value); }
}
public static readonly DependencyProperty LeftLabelProperty =
DependencyProperty.Register("LeftLabel", typeof(string), typeof(SliderPicker), new UIPropertyMetadata(String.Empty));
public string RightLabel
{
get { return (string)GetValue(RightLabelProperty); }
set { SetValue(RightLabelProperty, value); }
}
public static readonly DependencyProperty RightLabelProperty =
DependencyProperty.Register("RightLabel", typeof(string), typeof(SliderPicker), new UIPropertyMetadata(String.Empty));
}
The style like this:
<Style x:Key="SlickSlider" TargetType="{x:Type Slider}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Slider}">
...
<TextBlock Text="{TemplateBinding LeftLabel}" Grid.Row="2" HorizontalAlignment="Left" />
<TextBlock Text="{TemplateBinding RightLabel}" Grid.Row="2" HorizontalAlignment="Right" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And usage:
<controls:SliderPicker LeftLabel="RealPreference" RightLabel="RealCoverage" Width="400" Style="{StaticResource SlickSlider}"/>
This doesn't seem to work. So, how do I set the DP on the control and show it in the template? I thought that's what TemplateBinding was for?
You only need a small fix. Change {x:Type Slider} to {x:Type controls:SliderPicker}
You need to apply the custom control as the type in your style and template. Without it your trying to look for LeftLabelProperty and RightLabelProperty in Slider and not SliderPicker with your template binding.
Change your style to
<Style x:Key="SlickSlider" TargetType="{x:Type controls:SliderPicker}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:SliderPicker}">
...
<TextBlock Text="{TemplateBinding LeftLabel}" Grid.Row="2" HorizontalAlignment="Left" />
<TextBlock Text="{TemplateBinding RightLabel}" Grid.Row="2" HorizontalAlignment="Right" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Have tried this with the class you've posted and it works fine :)