UWP TemplateBinding in ControlTemplate to fixed value - c#

I want to bind a property (myHeight) in my Controltemplate to the parent. The following is my code so far.
Resource dict
<Style TargetType="local2:TestingControl" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local2:TestingControl">
<Border
Height="{TemplateBinding myHeight}"
Background="Green"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<StackPanel>
<ContentPresenter Content="{TemplateBinding Content}"/>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
TestingControl.cs
[ContentProperty(Name = "Content")]
public sealed class TestingControl : Control
{
public TestingControl()
{
this.DefaultStyleKey = typeof(TestingControl);
}
public static readonly double myHeight = (double)100;
public object Content
{
get { return (string)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
public static readonly DependencyProperty ContentProperty =
DependencyProperty.Register("Content", typeof(string), typeof(TestingControl), new PropertyMetadata(string.Empty));
}
What i'm trying to bind is the myHeight. I'd like to have this in the .cs since I need to run some operations on it. This fails to load entirely!
I also tried the following approach
Resource dict
<x:Double x:Key="myHeight">100</x:Double>
<Style TargetType="local2:TestingControl" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local2:TestingControl">
<Border
Height="{ThemeResource myHeight}"
Background="Green"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
TestingControl.cs
[ContentProperty(Name = "Content")]
public sealed class TestingControl : Control
{
public TestingControl()
{
this.DefaultStyleKey = typeof(TestingControl);
var x = (double)Resources["myHeight"];
}
public object Content
{
get { return (string)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
public static readonly DependencyProperty ContentProperty =
DependencyProperty.Register("Content", typeof(string), typeof(TestingControl), new PropertyMetadata(string.Empty));
}
The problem with the second approach, is that when reading the property in the .cs code, var x = (double)Resources["myHeight"]; I get an exception.
Resolutions to either (preferably both, since I'm just trying to learn UWP) would be greatly appreciated.

The first thing is TemplateBinding should bind the dependency property and you write the static filed that can not bind to Height.
The second thing is ThemeResource will find the Theme but you define a static source.
<x:Double x:Key="myHeight">100</x:Double>
<Style TargetType="local2:TestingControl" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local2:TestingControl">
<Border
Height="{StaticResource myHeight}"
Background="Green"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The third thing is you get the resource at first but the resource inits after OnApplyTemplate.
You should move the code that gets the resource to OnApplyTemplate.
protected override void OnApplyTemplate()
{
try
{
// find something in TestingControl.Resources
var x = Resources["myHeight"];
}
catch (Exception e)
{
}
try
{
// find something in App.Resources
var x = App.Current.Resources["myHeight"];
}
catch (Exception e)
{
}
base.OnApplyTemplate();
}
If your resource is written in App.xaml that you should use App.Current.Resources to get the resource.
If you want to get the resource in your customs control that you should add the resource in your control.
<local:TestingControl>
<local:TestingControl.Resources>
<x:Double x:Key="myHeight">100</x:Double>
</local:TestingControl.Resources>
</local:TestingControl>

Related

Binding attached property to attached property inside a control template

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>

Nested UserControls DataContext

Abstract:
I have two UserControls named Zone and ZoneGroup. One of these controls (ZoneGroup) includes two instances of the other one (Zone). Both of them set DataContext of the root element to this, at the Loaded event-handler.
The problem is that DataContext of inner controls (Zones) is set before loading (the DataContextChanged event occurred before Loaded) which causes some malfunctions in UI. (inside Zone controls initial state is wrong.) If I prevent it, everything works fine (at least seems to be!) except I encounter with the following error report. (In the Output window)
public partial class Zone : UserControl
{
∙∙∙
private void Zone_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
// Adding this if-clause solve UI problems but makes some binding errors!
if (this.IsLoaded)
brdRoot.DataContext = this;
}
}
System.Windows.Data Error: 40 : BindingExpression path error: 'ZoneBrush' property not found on 'object' ''ZoneGroup' (Name='')'. BindingExpression:Path=ZoneBrush; DataItem='ZoneGroup' (Name=''); target element is 'brdRoot' (Name=''); target property is 'BorderBrush' (type 'Brush')
Details:
There is a UserControl named Zone containing several data-bindings like so..
<UserControl x:Class="MyApp.Zone"
∙∙∙>
<Border x:Name="brdRoot" BorderBrush="{Binding ZoneBrush}" BorderThickness="1">
∙∙∙
</Border>
</UserControl>
So, I set brdRoot data-context as
public partial class Zone : UserControl
{
public Brush ZoneBrush
{
get { return (Brush)GetValue(ZoneBrushProperty); }
set { SetValue(ZoneBrushProperty, value); }
}
∙∙∙
public Zone()
{
InitializeComponent();
}
private void Zone_Loaded(object sender, RoutedEventArgs e)
{
brdRoot.DataContext = this;
}
∙∙∙
}
Also, there is another UserControl that has two ContentPresenters in order to contain and manage two Zone controls.
<UserControl x:Class="MyApp.ZoneGroup"
∙∙∙>
<Border x:Name="brdRoot" BorderBrush="Gray" BorderThickness="1">
<StackPanel Orientation="Horizontal">
<ContentPresenter Content="{Binding MainZone}"
Margin="{Binding MainZonePadding}"/>
<ContentPresenter Content="{Binding MemberZone}"/>
</StackPanel>
</Border>
</UserControl>
And the code-behind is:
public partial class ZoneGroup : UserControl
{
public Thickness MainZonePadding
{
get { return (Thickness)GetValue(MainZonePaddingProperty); }
set { SetValue(MainZonePaddingProperty, value); }
}
public Zone MainZone
{
get { return (Zone)GetValue(MainZoneProperty); }
set { SetValue(MainZoneProperty, value); }
}
public Zone MemberZone
{
get { return (Zone)GetValue(MemberZoneProperty); }
set { SetValue(MemberZoneProperty, value); }
}
public ZoneGroup()
{
InitializeComponent();
}
private void ZoneGroup_Loaded(object sender, RoutedEventArgs e)
{
brdRoot.DataContext = this;
}
∙∙∙
}
Edit ► Sketch:
My app works fine as expected, but some BindingExpression errors are reported.
You're overcomplicating it all too much, with all those UserControls and all those DependencyProperties. Look at this sample which uses 0 lines of C# code (XAML-Only):
<Window x:Class="MiscSamples.ItemsControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ItemsControl" Height="300" Width="300">
<Window.Resources>
Style for the ItemsControl:
<Style TargetType="ItemsControl" x:Key="ZoneItemsControlStyle">
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Padding" Value="5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ItemsControl">
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Padding="5">
<DockPanel>
<TextBlock HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Text="{Binding Items.Count,RelativeSource={RelativeSource TemplatedParent}, StringFormat='{}{0} Item(s)'}"
Foreground="{TemplateBinding Foreground}"
DockPanel.Dock="Top"/>
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}">
<ItemsPresenter/>
</Border>
</DockPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" IsItemsHost="True"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
DataTemplate For Brushes:
<DataTemplate DataType="{x:Type Brush}">
<Border BorderBrush="{Binding}" Margin="1" BorderThickness="1" Padding="2,3,2,3">
<TextBlock Text="{Binding}" TextAlignment="Center" Foreground="Black"/>
</Border>
</DataTemplate>
</Window.Resources>
Now, its usage:
<Grid>
<ItemsControl VerticalAlignment="Center" HorizontalAlignment="Center"
Style="{StaticResource ZoneItemsControlStyle}">
<SolidColorBrush Color="Red"/>
<SolidColorBrush Color="Green"/>
<SolidColorBrush Color="Black"/>
<SolidColorBrush Color="Blue"/>
</ItemsControl>
</Grid>
</Window>
Result:
See how I'm making use of DataTemplates in order to show a custom piece of UI for a specific Data Type? (In this case, System.Windows.Media.Brush class)
I'm "using the Brushes as ViewModels". You could also create your own ViewModels of course, and then create a specific DataTemplate for each VM type.
Also, see how I'm using the TemplateBinding MarkupExtension to bind several properties inside the ControlTemplate to the corresponding value in the ItemsControl instance.
Finally, see how you can actually add ANY kind of items to the ItemsControl.
Also, I must mention that I used this Style-based approach in order to enable reusability. You could place another ItemsControl somewhere else in the application and set its Style="{StaticResource ZoneItemsControlStyle}" and you're done. But if you only plan to use this a single time, you can just place all the properties hardcoded in the ItemsControl.Template ControlTemplate.
This is not a direct answer!
As #HighCore said, I tried to use an ItemsControl instead of implementing two ContentPresenters in my user-control. Just for clarity I made a new simple app to be able to describe it simply. So please consider some new assumptions:
Here again, there are two UserControls; MyItem and MyItemsControl as follows.
<UserControl x:Class="MyApp.MyItem"
∙∙∙>
<Grid x:Name="grdRoot">
<Border BorderBrush="{Binding ItemBorderBrsuh}" BorderThickness="1">
<TextBlock x:Name="txtColorIndicator"
Text="Item"
TextAlignment="Center"
Margin="5"/>
</Border>
</Grid>
</UserControl>
C# code-behind:
public partial class MyItem : UserControl
{
#region ________________________________________ ItemBorderBrsuh
public Brush ItemBorderBrsuh
{
get { return (Brush)GetValue(ItemBorderBrsuhProperty); }
set { SetValue(ItemBorderBrsuhProperty, value); }
}
public static readonly DependencyProperty ItemBorderBrsuhProperty =
DependencyProperty.Register("ItemBorderBrsuh",
typeof(Brush),
typeof(MyItem),
new FrameworkPropertyMetadata(new SolidColorBrush(Colors.Black), FrameworkPropertyMetadataOptions.None, OnItemBorderBrsuhPropertyChanged));
private static void OnItemBorderBrsuhPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
MyItem instance = sender as MyItem;
if (instance != null && e.NewValue is SolidColorBrush)
instance.txtColorIndicator.Text = (e.NewValue as SolidColorBrush).Color.ToString();
}
#endregion
public MyItem()
{
InitializeComponent();
grdRoot.DataContext = this;
}
}
And this is the MyItemsControl.
<UserControl x:Class="MyApp.MyItemsControl"
∙∙∙>
<StackPanel>
<TextBlock x:Name="txtHeader" Margin="0,0,0,5" TextAlignment="Center" Text="0 Item(s)"/>
<Border BorderBrush="Gray" BorderThickness="1" Padding="5">
<ItemsControl x:Name="itemsControl" ItemsSource="{Binding Items}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:MyItem />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
</StackPanel>
</UserControl>
C# Code-behind:
public partial class MyItemsControl : UserControl
{
private ObservableCollection<MyItem> _Items = new ObservableCollection<MyItem>();
public ObservableCollection<MyItem> Items
{
get
{
return _Items;
}
set
{
_Items = value;
}
}
private void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
txtHeader.Text = Items.Count + " Item(s)";
}
public MyItemsControl()
{
InitializeComponent();
Items.CollectionChanged += Items_CollectionChanged;
this.DataContext = this;
}
}
Here is how to use MyItem within MyItemsControl.
<Grid>
<local:MyItemsControl HorizontalAlignment="Center" VerticalAlignment="Center" Padding="5" BorderBrush="Black" BorderThickness="1">
<local:MyItemsControl.Items>
<local:MyItem ItemBorderBrsuh="Green" Margin="1"/>
<local:MyItem ItemBorderBrsuh="Red" Margin="1"/>
<local:MyItem ItemBorderBrsuh="Blue" Margin="1"/>
<local:MyItem ItemBorderBrsuh="Orange" Margin="1"/>
</local:MyItemsControl.Items>
</local:MyItemsControl>
</Grid>
Now, there is no problem with BindingExpressions, but an important question remains. How to replace
{
grdRoot.DataContext = this;
}
and
{
this.DataContext = this;
}
with a true ViewModel?
Screenshot:
Edit: I tried to implement MVVM pattern but there are some problems. I asked the first one here.

How to select items in a custom control

I wrote a custom control to displays items from an ItemsSource at calculated positions.
Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Control="clr-namespace:MyControls.Control">
<Style TargetType="{x:Type Control:MyControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Control:MyControl}">
<Border x:Name="DrawingArea"
BorderThickness="{TemplateBinding Border.BorderThickness}"
BorderBrush="{TemplateBinding Border.BorderBrush}"
Padding="{TemplateBinding Control.Padding}"
Background="{TemplateBinding Panel.Background}"
SnapsToDevicePixels="true">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Canvas IsItemsHost="True" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ClipToBounds="true" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
...
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
MyControl.cs
public class MyControl : MultiSelector, INotifyPropertyChanged
{
private bool _layoutHandlingDone = false;
private Border _drawingArea;
private Border DrawingArea
{
get { return _drawingArea ?? (_drawingArea = (Border) Template.FindName("DrawingArea", this)); }
}
static MyControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyControl), new FrameworkPropertyMetadata(typeof(MyControl)));
}
protected override Size MeasureOverride(Size aviableSize) { ... }
protected override Size ArrangeOverride(Size finalSize) { ... }
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (Template != null && _layoutHandlingDone == false)
{
_layoutHandlingDone = true;
ItemContainerGenerator.StatusChanged += (sender, args) =>
{
if(ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
InvalidateMeasure();
};
DrawingArea.SizeChanged += (sender, args) => UpdateLayout();
}
}
}
Layouting and drawing the items works very well. Now I want the items to be selectable. For this I derived my control form MultiSelector, but it seems that this isn't enough.
What functionality does the MultiSelector provide and how should I use it in my control?
I strongly suggest to separate things here. Managing a collection of items and arranging them in a container control are two distinct concerns, and ItemsControl provides support for both.
Instead of overriding MeasureOverride and ArrangeOverride, you should write your specialized Panel class (and override MeasureOverride and ArrangeOverride there) and use that class in the ItemsPanel template of your ItemsControl.
You could then easily derive from ListBox instead of MultiSelector and would get all the selection functionality for free.

Writing a custom control in Windows 8 Metro

I'm writing my first custom user control against WinRT, and I've run into a problem.
I would like to expose an image, PART_NwBadge and it's visibility as Dependency Properties in my control. Then I would like to supply default values through setters in the style. This part is not working. Instead, the default value from the DependencyProperty (in BadgedButton.cs) is being applied.
Is it even possible to do what I've described? Or should I be setting the default values in the C# code? If I do need to set the values in the C# code, would someone comment on how to load image resources in code? After a great deal of searching I've yet to find a solution that works.
Finally, since this is my first serious attempt at writing a custom control, please suggest any improvements I could make, even if they are not directly related to the problem.
Windows 8 Consumer Preview
C#/WinRT/Metro
Visual Studio 11 Beta
Themes/Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="using:InkSdkTestApplication.Controls">
<Style TargetType="l:BadgedButton">
<Setter Property="Width" Value="36"/>
<Setter Property="Height" Value="36"/>
<Setter Property="Background" Value="#1C1C1C"/>
<Setter Property="BorderBrush" Value="White"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="NwBadge">
<Setter.Value>
<Image Width="16" Height="16" Source="../Assets/mouse_16x16.png"/>
</Setter.Value>
</Setter>
<Setter Property="NwBadgeVisibility" Value="Collapsed"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="l:BadgedButton">
<Border x:Name="PART_Border"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<ContentPresenter x:Name="PART_Content"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{TemplateBinding Content}"/>
<Image x:Name="PART_NwBadge"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="16" Height="16"
Visibility="{TemplateBinding NwBadgeVisibility}"
Source="{TemplateBinding NwBadge}"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Controls/BadgedButton.cs
namespace InkSdkTestApplication.Controls
{
public sealed class BadgedButton : Control
{
#region // Dependency Properties
public static DependencyProperty ContentProperty =
DependencyProperty.Register(
"Content",
typeof(FrameworkElement),
typeof(BadgedButton),
new PropertyMetadata(null));
public static DependencyProperty NwBadgeProperty =
DependencyProperty.Register(
"NwBadge",
typeof(Image),
typeof(BadgedButton),
new PropertyMetadata(null));
public static DependencyProperty NwBadgeVisibilityProperty =
DependencyProperty.Register(
"NwBadgeVisibility",
typeof(Visibility),
typeof(BadgedButton),
new PropertyMetadata(Visibility.Visible));
#endregion
#region // Public Properties
public FrameworkElement Content
{
get { return (FrameworkElement)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
public Image NwBadge
{
get { return (Image)GetValue(NwBadgeProperty); }
set { SetValue(NwBadgeProperty, value); }
}
public Visibility NwBadgeVisibility
{
get { return (Visibility)GetValue(NwBadgeVisibilityProperty); }
set { SetValue(NwBadgeVisibilityProperty, value); }
}
#endregion
public BadgedButton()
{
this.DefaultStyleKey = typeof(BadgedButton);
}
}
}
http://timheuer.com/blog/archive/2012/03/07/creating-custom-controls-for-metro-style-apps.aspx

WPF custom control problem

I have a problem, and I have not found the solution yet. I woud like to create a base custom control and use it in another custom control. The base control works fine when I use it in a window, but when I use it in the other custom control, the binding does not work.
What's wrong with my code?
Code:
Model:
public class ElementModel
{
public string Name { get; set; }
public string FullName { get; set; }
}
The base control:
public class ListControl : Control
{
static ListControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ListControl), new FrameworkPropertyMetadata(typeof(ListControl)));
}
public ListControl()
{
SetValue(ElementListProperty, new List<ElementModel>());
}
public static readonly DependencyProperty ElementListProperty =
DependencyProperty.Register(
"ElementList",
typeof(List<ElementModel>),
typeof(ListControl),
new FrameworkPropertyMetadata(new List<ElementModel>())
);
public List<ElementModel> ElementList
{
get { return (List<ElementModel>)GetValue(ElementListProperty); }
set { SetValue(ElementListProperty, value); }
}
}
The Wrapper Control:
public class ListWrapper : Control
{
static ListWrapper()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ListWrapper), new FrameworkPropertyMetadata(typeof(ListWrapper)));
}
public ListWrapper()
{
SetValue(EMListProperty, new List<ElementModel>());
}
public static readonly DependencyProperty EMListProperty =
DependencyProperty.Register(
"EMList",
typeof(List<ElementModel>),
typeof(ListWrapper),
new FrameworkPropertyMetadata(new List<ElementModel>())
);
public List<ElementModel> EMList
{
get { return (List<ElementModel>)GetValue(EMListProperty); }
set { SetValue(EMListProperty, value); }
}
}
File 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:UIControl">
<Style TargetType="{x:Type local:ListControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ListControl}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ListBox ItemsSource="{TemplateBinding ElementList}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="Name:"/>
<TextBlock Text="{Binding Path=Name}" />
<Label Content="Full name:"/>
<TextBlock Text="{Binding Path=FullName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type local:ListWrapper}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ListWrapper}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<local:ListControl ElementList="{TemplateBinding EMList}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
If I put the controls in the window and binding properties, than the ListControl works fine and shows the elements, but the WrapperList does not.
<Window x:Class="MainApplication.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="clr-namespace:UIControl;assembly=UIControl"
Title="Window1" Height="304" Width="628">
<Grid>
<ui:ListControl x:Name="listCtr" ElementList="{Binding Path=EList}" HorizontalAlignment="Left" Width="300" />
<ui:ListWrapper x:Name="listWrp" EMList="{Binding Path=EList}" HorizontalAlignment="Right" Width="300" Background="Gray"/>
</Grid>
And the test data injection:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
DataContext = this;
}
List<ElementModel> elist = null;
public List<ElementModel> EList
{
get
{
if (elist == null)
{
elist = new List<ElementModel>();
ElementModel em = new ElementModel() { Name = "Apple", FullName = "Red Apple" };
elist.Add(em);
em = new ElementModel() { Name = "Pineapple", FullName = "Yellow Pineapple" };
elist.Add(em);
}
return elist;
}
}
}
Project archive
I don’t know why, but if I should wrap this list of element (List) into new collection type (example ElementCollection : ObservalbeCollection) and use this collection type in the dependency property, it works fine...
public static readonly DependencyProperty EMListProperty =
DependencyProperty.Register(
"EMList",
typeof(ElementCollection),
typeof(ListWrapper),
new FrameworkPropertyMetadata(new ElementCollection())
);
public ElementCollection EMList
{
get { return (ElementCollection)GetValue(EMListProperty); }
set { SetValue(EMListProperty, value); }
}
I couldn't find anywhere where you are setting the binding context:
http://msdn.microsoft.com/en-us/library/ms752347.aspx

Categories

Resources