XAML x:Class Generic - c#

I have this class
public class FeatureTabBase<T> : UserControl, IFeatureTab<T>
where T : BaseModel
{
public string TabGuid { get; set; }
public T FeaturedElement
{
get { return (T)GetValue(FeaturedElementProperty); }
set { SetValue(FeaturedElementProperty, value); }
}
// Using a DependencyProperty as the backing store for FeaturedElement. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FeaturedElementProperty =
DependencyProperty.Register("FeaturedElement", typeof(T), typeof(FeatureTabBase<T>), new PropertyMetadata(null));
}
That implements this interface
public interface IFeatureTab<T>
where T : class
{
T FeaturedElement { get; set; }
string TabGuid { get; set; }
}
And this instance from it
public partial class MyClass : FeatureTabBase<MyType>
{
public MyClass()
{
InitializeComponent();
}
}
But don't know how instantiate it on XAML
All I'm trying to do is a generic console that can show some pages for my different kind of items.
I was reading about x:TypeArguments at
https://learn.microsoft.com/en-us/dotnet/desktop-wpf/xaml-services/xtypearguments-directive
But nothing works.
Any Ideas?

Add x:TypeArguments to the UserControl declaration in XAML:
<ctr:FeatureTabBase
x:Class="YourNamespace.MyClass"
x:TypeArguments="local:MyType"
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:ctr="clr-namespace:YourNamespace"
xmlns:local="clr-namespace:YourNamespace"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
...
</ctr:FeatureTabBase>

Here's a control I use which will at least illustrate the concept.
Obviously, this is not going to be cut and paste for whatever it is you have in mind.
An editrow allows me to easily line up a series of labelled controls inside a stackpanel, and add various standardised functionality to the controls I make content.
public class EditRow : ContentControl
{
public string LabelFor
{
get { return (string)GetValue(LabelForProperty); }
set { SetValue(LabelForProperty, value); }
}
public static readonly DependencyProperty LabelForProperty = DependencyProperty.RegisterAttached(
"LabelFor",
typeof(string),
typeof(EditRow));
public string LabelWidth
{
get { return (string)GetValue(LabelWidthProperty); }
set { SetValue(LabelWidthProperty, value); }
}
public static readonly DependencyProperty LabelWidthProperty = DependencyProperty.RegisterAttached(
"LabelWidth",
typeof(string),
typeof(EditRow)
);
public string PropertyWidth
{
get { return (string)GetValue(PropertyWidthProperty); }
set { SetValue(PropertyWidthProperty, value); }
}
public static readonly DependencyProperty PropertyWidthProperty = DependencyProperty.RegisterAttached(
"PropertyWidth",
typeof(string),
typeof(EditRow)
);
public EditRow()
{
this.IsTabStop = false;
}
}
I template this in a resource dictionary. ( There are other options including custom control generic xaml)
<Style TargetType="{x:Type spt:EditRow}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type spt:EditRow}">
<Grid Height="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding RelativeSource={
RelativeSource FindAncestor,
AncestorType=spt:EditRow},
Path=LabelWidth, TargetNullValue=2*}"/>
<ColumnDefinition Width="{Binding RelativeSource={
RelativeSource FindAncestor,
AncestorType=spt:EditRow},
Path=PropertyWidth, TargetNullValue=3*}"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding RelativeSource={
RelativeSource FindAncestor,
AncestorType=spt:EditRow},
Path=LabelFor}"
HorizontalAlignment="Right"
TextAlignment="Right"
Margin="2,4,0,2"/>
<Border Padding="8,2,8,2" Grid.Column="1" BorderThickness="0">
<ContentPresenter>
<ContentPresenter.Resources>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource ErrorToolTip}"/>
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource ErrorToolTip}"/>
<Style TargetType="{x:Type DatePicker}" BasedOn="{StaticResource ErrorToolTip}"/>
</ContentPresenter.Resources>
</ContentPresenter>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Usage:
<ItemsControl>
<spt:EditRow LabelFor="Name:" >
<TextBox Text="{Binding EditVM.TheEntity.CustomerName,
UpdateSourceTrigger=PropertyChanged,
NotifyOnSourceUpdated=True,
NotifyOnValidationError=True,
Mode=TwoWay}" />
</spt:EditRow>
<spt:EditRow LabelFor="Address:" >
<TextBox Text="{Binding EditVM.TheEntity.Address1,
UpdateSourceTrigger=PropertyChanged,
NotifyOnSourceUpdated=True,
NotifyOnValidationError=True,
Mode=TwoWay}" />
</spt:EditRow>
Notice that I have a TextBox as content of each of those editrows, but it could be a datepicker or whatever.
You could bind that content. Then use datatype on viewmodel type for variable datatemplates.

XAML doesnt support generics, so the ctrl:FeatureTabBase will never work. Also, you cannot inherit the XAML part of a UserControl if you derive a new class from an existing UserControl.
You can't use strongly-typed DataTemplates as they only hook up to the concrete class specified in the type. You need to take a different approach. Maybe simplify ?
public class FeatureTab : UserControl
{
public static readonly DependencyProperty FeaturedElementProperty =
DependencyProperty.Register(
"FeaturedElement",
typeof(ModelBase),
typeof(FeatureTab)
, new PropertyMetadata(null));
public string TabGuid { get; set; }
public ModelBase FeaturedElement
{
get => (ModelBase) GetValue(FeaturedElementProperty);
set => SetValue(FeaturedElementProperty, value);
}
}
NOTE This answer is valid for .Net frameworks prior to 4.7, if you point your project to .Net framework 4.7.2 Generics must work on xaml.

Related

Derive from IconElement or IconSourceElement in UWP

I'm trying to create the same custom symbol icon control as MS SymbolIcon, which will get the enum symbol values as input, and the equivalent path data value will be retrieved from the dictionary<Symbol, string> collection. But the symbol icon class inherited from IconElement and the same below issue faced in my application.
'IconElement does not take a constructor that takes 0 arguments'
Derive from IconElement in UWP
but I have marked my constructor as extern and enclosed with semicolon to resolve the constructor issue.
public class CustomSymbolIcon : IconElement
{
public extern CustomSymbolIcon();
}
But my question is, I can get input from the end user as Symbol Enum and retrieved equivalent path geometry based on input from the stored dictionary. But I couldn't bind the path geometry to the path element(Targeted Custom icon class)and I can't write the template style for this class. Because IconElement was derived from the framework element.
I can achieve these all with control class , but I can't use this inside the <NavigationView.Icon> (its a IconElement base) tag due to base class.
public class SymbolToIconConversion : Control //IconElement instead of control
{
internal static Dictionary<Symbol, string> enumValuesCollection = new Dictionary<Symbol, string>();
public SymbolToIconConversion()
{
this.DefaultStyleKey = typeof(SymbolToIconConversion);
PopulateEnumCollection();
}
public static Dictionary<Symbol, string> EnumValuesCollection
{
get { return enumValuesCollection; }
set { enumValuesCollection = value; }
}
internal void PopulateEnumCollection()
{
enumValuesCollection.Add(Symbol.Accept, "M0,4 5,9 9,0 4,5");
enumValuesCollection.Add(Symbol.Close, "F1 M 22,12L 26,12L 26,22L 36,22L 36,26L 26,26L 26,36L 22,36L 22,26L 12,26L 12,22L 22,22L 22,12 Z");
enumValuesCollection.Add(Symbol.Save, "M0,4 5,9 9,0 4,5");
enumValuesCollection.Add(Symbol.Add, "M0,5 H10 M5,5 V10Z");
}
public Symbol Symbol
{
get { return (Symbol)GetValue(SymbolProperty); }
set { SetValue(SymbolProperty, value); }
}
// Using a DependencyProperty as the backing store for Symbol. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SymbolProperty =
DependencyProperty.Register("Symbol", typeof(Symbol), typeof(SfSymbolIcon), new PropertyMetadata(typeof(Symbol), new PropertyChangedCallback(OnSymbolChanged)));
internal Geometry Geometry
{
get { return (Geometry)GetValue(GeometryProperty); }
set { SetValue(GeometryProperty, value); }
}
// Using a DependencyProperty as the backing store for Geometry. This enables animation, styling, binding, etc...
internal static readonly DependencyProperty GeometryProperty =
DependencyProperty.Register("Geometry", typeof(Geometry), typeof(SymbolToIconConversion), new PropertyMetadata(null));
private static void OnSymbolChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SymbolToIconConversion symbolIcon = d as SymbolToIconConversion;
if (symbolIcon != null)
{
foreach (var value in EnumValuesCollection)
{
if (symbolIcon.Symbol == value.Key)
{
symbolIcon.Geometry = (Geometry)XamlBindingHelper.ConvertValue(typeof(Geometry), value.Value);
return;
}
}
}
}
<Style TargetType="core:SymbolToIconConversion">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="core:SymbolToIconConversion">
<Viewbox x:Name="ContentViewbox" AutomationProperties.AccessibilityView="Raw" HorizontalAlignment="Stretch" Height="{ThemeResource AppBarButtonContentHeight}" Margin="{ThemeResource AppBarButtonContentViewboxCollapsedMargin}">
<Path x:Name="Content"
Width="{Binding Width, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Height="{Binding Height, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Fill="{Binding Foreground, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Data="{Binding Geometry, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
</Viewbox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
How to initialize dictionary in constructor of custom class? - Need to populate the dictionary when control loaded. I can't call this method in extern constructor.
If possible, path geometry retrieval using symbol achieved by Dictionary<Symbol, String> collection populated. Is this efficient way?,Bcz it leads to key already added in collection issue when initialize the control at second time. Please suggest alternate ways to achieve this.
How can write style for framework element? I need to bind the Path data in control style. But it doesn't have template property.
Can anyone suggest how to achieve this?
Derive from IconElement or IconSourceElement in UWP
I'm afraid you can't make CustomSymbolIcon that inherit IconElement, and IconElement does not provide method to set ControlTemplate, for your scenario, we suggest you use custom Datatemplate to replace NavigationViewItem like the following
<NavigationView.MenuItemTemplate>
<DataTemplate>
<Grid Width="{Binding ElementName=nvSample, Path=OpenPaneLength}" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<local:SymbolToIconConversion Symbol="Accept" VerticalAlignment="Center"/>
<StackPanel
Grid.Column="1"
Margin="45,0,10,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Center">
<TextBlock x:Name="Header" Text="Header" />
<TextBlock x:Name="Line1" Text="TheFirstLine" />
</StackPanel>
</Grid>
</DataTemplate>
</NavigationView.MenuItemTemplate>

Update Normal Property in Dependency property/AttachedProperty,

I am trying to bind a normal property of AvalonDock,
xmlns:xcad="http://schemas.xceed.com/wpf/xaml/avalondock"
<xcad:LayoutAnchorable Title="Folder" CanHide="{Binding IsHideExplorerView}">
<Views:ExplorerView DataContext="{Binding ExplorerViewModel}"/>
</xcad:LayoutAnchorable>
Here CanHide is a Normal property, if trying to bind will throw the exception like
A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
My question is, Is it possible any way to make a normal property to override DependencyProperty to make it Bindable.
Edit
Added a class which inherit LayoutAnchorable but PropertyChangedCallback of DependencyProperty Never calls.
public class ExtendedAnchorableItem : LayoutAnchorable
{
public static readonly DependencyProperty IsCanHideProperty =
DependencyProperty.RegisterAttached("IsCanHide", typeof(bool), typeof(ExtendedAnchorableItem),
new FrameworkPropertyMetadata((bool)false,
new PropertyChangedCallback(OnCanHideChanged)));
public bool IsCanHide
{
get { return (bool)GetValue(IsCanHideProperty); }
set { SetValue(IsCanHideProperty, value);
this.IsVisible = value; // No effect.
}
}
private static void OnCanHideChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((ExtendedAnchorableItem)d).Hide();
}
}
XAML
<xcad:LayoutAnchorablePane>
<Utility:ExtendedAnchorableItem IsCanHide="{Binding IsHideExplorer}">
<Views:ExplorerView DataContext="{Binding ExplorerViewModel}"/>
</Utility:ExtendedAnchorableItem>
</xcad:LayoutAnchorablePane>
Similarly i have tried creating an AttachedProperty which can hook it to LayoutAnchorable but PropertyChangedCallback Never get called click here for a new question i have posted.
Any Help guys ?
I did and example previously in my case i need to create new button with 2 images one when the button is available and the other one when it's disabled, to do that first i created new user control named "MyButton" my xaml was like this
<Button ToolTip="{Binding ButtonLabel,RelativeSource={RelativeSource AncestorType=UserControl,Mode=FindAncestor},UpdateSourceTrigger=PropertyChanged}"
Command="{Binding ButtonCommand,RelativeSource={RelativeSource AncestorType=UserControl,Mode=FindAncestor},UpdateSourceTrigger=PropertyChanged}"
Cursor="Hand" VerticalAlignment="Center" >
<Button.Template>
<ControlTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="45"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Name="ButtonImage" IsEnabled="{Binding Path=IsEnabled,RelativeSource={RelativeSource AncestorType=Button,Mode=FindAncestor}}" >
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="Source" Value="{Binding ActiveImage,RelativeSource={RelativeSource AncestorType=UserControl,Mode=FindAncestor},UpdateSourceTrigger=PropertyChanged}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Source" Value="{Binding DeactiveImage,RelativeSource={RelativeSource AncestorType=UserControl,Mode=FindAncestor},UpdateSourceTrigger=PropertyChanged}"/>
</Trigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<Label Name="LabelContent" Content="{Binding ButtonLabel,RelativeSource={RelativeSource AncestorType=UserControl,Mode=FindAncestor},UpdateSourceTrigger=PropertyChanged}"
Grid.Column="1" IsEnabled="{Binding Path=IsEnabled,RelativeSource={RelativeSource AncestorType=Button,Mode=FindAncestor}}" VerticalContentAlignment="Center" />
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
then i added dependency Properties for ActiveImage and DeactiveImage using this code
public static DependencyProperty activeImage =
DependencyProperty.Register("ActiveImage", typeof(type of this property like "string"), typeof(type of the custom control that you need like "MyButton"), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public string ActiveImage
{
get { return (string)GetValue(activeImage); }
set { SetValue(activeImage, value); }
}
then i used this new control in my project
<custom:MyButton ButtonCommand="{Binding DecreaseImagesCount}" ButtonLabel="ZoomIn" ActiveImage="/Images/ActiveImages/ZoomIn.png" DeactiveImage="/Images/GrayImages/ZoomIn.png"
Grid.Column="2" Margin="3,4" />
notice that i can do binding the path for Button Image now
If it is enough for you to just set that property from your view model then you could use an attached behavior.
Just create a new class and add an attached property like this (I did not really test this, since I actually do not have AvalonDock at hand, but you should get the idea):
public class YourBehavior
{
public static readonly DependencyProperty YourCanHideProperty = DependencyProperty.RegisterAttached(
"YourCanHide",
typeof(bool),
typeof(LayoutAnchorable),
new PropertyMetadata(YourCanHidePropertyChanged));
private static void YourCanHidePropertyChanged(
DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs e)
{
LayoutAnchorable control = dependencyObject as LayoutAnchorable;
if (control != null)
{
control.CanHide = e.NewValue as bool;
}
}
public static bool GetYourCanHideProperty(LayoutAnchorablewindow)
{
return window.GetValue(YourProperty) as bool?;
}
public static void SetYourCanHideProperty(LayoutAnchorable control, bool value)
{
window.SetValue(YourProperty, value);
}
}
Now you should be able to use that behavior like this:
<xcad:LayoutAnchorable Title="Folder" namespacealias:YourBehavior.YourCanHideProperty="{Binding IsHideExplorerView}"/>
If you want to have it working in both directions just check out the attached Blend behaviors.
Yes, you can do it.. you need to implement INotifypropertyChanged interface and raise a ProprtyChanged Event inside the property setter. After changing the property to a DependencyProperty, you will get the notification mechanism, so the property change is propagated to the target (in this case xcad) .
you can find lot of examples implementing the INotifyPropertyChanged..

Pushing ActualWidth/ActualHeight from a custom control to TemplateBinding

I have set up a simple example to try to achieve a simple thing: exposing a dependency property in a custom control that exposes a ActualWidth/ActualHeight of a control within this custom control.
In order to attempt to achieve that I have:
customcontrol.cs
public class CustomControl : ContentControl
{
static CustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl), new FrameworkPropertyMetadata(typeof(CustomControl)));
}
public static readonly DependencyProperty RenderedWidthProperty = DependencyProperty.Register(
"RenderedWidth", typeof (double), typeof (CustomControl), new PropertyMetadata(default(double)));
public double RenderedWidth
{
get { return (double) GetValue(RenderedWidthProperty); }
set { SetValue(RenderedWidthProperty, value); }
}
public static readonly DependencyProperty RenderedHeightProperty = DependencyProperty.Register(
"RenderedHeight", typeof (double), typeof (CustomControl), new PropertyMetadata(default(double)));
public double RenderedHeight
{
get { return (double) GetValue(RenderedHeightProperty); }
set { SetValue(RenderedHeightProperty, value); }
}
}
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:Custom_Control_Pushing_ActualWidth">
<Style TargetType="{x:Type local:CustomControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomControl}">
<Grid local:SizeObserver.Observe="True"
local:SizeObserver.ObservedWidth="{Binding RenderedWidth, RelativeSource={RelativeSource TemplatedParent}}"
local:SizeObserver.ObservedHeight="{Binding RenderedHeight, RelativeSource={RelativeSource TemplatedParent}}">
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
viewmodel.cs
class ViewModel
{
private double width;
private double height;
public double Width
{
get { return width; }
set
{
width = value;
Console.WriteLine("Width: {0}", value);
}
}
public double Height
{
get { return height; }
set
{
height = value;
Console.WriteLine("Height: {0}", value);
}
}
}
mainwindow.xaml
<Window x:Class="Custom_Control_Pushing_ActualWidth.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Custom_Control_Pushing_ActualWidth"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<Grid>
<local:CustomControl RenderedWidth="{Binding Width, Mode=OneWayToSource}" RenderedHeight="{Binding Height, Mode=OneWayToSource}" />
</Grid>
</Window>
And I use SizeObserver from this SO answer.
However although I see the code in the dependency property being updated in the size observer, the bound viewmodel property's setter doesn't get set with the values. Something's wrong with my binding and I don't know what it is.
How can I correctly bind the DependencyProperty with the ViewModel's property?
Change this:
<Grid local:SizeObserver.Observe="True"
local:SizeObserver.ObservedWidth="{Binding RenderedWidth, RelativeSource={RelativeSource TemplatedParent}}"
local:SizeObserver.ObservedHeight="{Binding RenderedHeight, RelativeSource={RelativeSource TemplatedParent}}">
</Grid>
To this:
<Grid local:SizeObserver.Observe="True"
local:SizeObserver.ObservedWidth="{Binding RenderedWidth, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWayToSource}"
local:SizeObserver.ObservedHeight="{Binding RenderedHeight, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWayToSource}">
</Grid>
So add Mode=OneWayToSource to the end of the binding.
This way the Width property in the ViewModel is properly updating for me.
The reason behind this is not entirely clear for me, but I think that the default binding mode of the ObservedWidth and ObservedHeight attached properties are OneWay. So they only update the target properties (ObservedWidth, ObservedHeight) when the source properties (RenderedWidth, RenderedHeight) change.
You want the exact opposite.
With the OneWayToSource modification the changes in the ActualWidth and ActualHeight properties will nicely propagate up to your ViewModel.

Binding a Parent DataContext from within an ItemTemplate in Windows Store Apps

I got a problem binding an ItemTemplates Parent context inside an item template.
There are plenty of "workarounds" which only work in WPF (i.e. using FindAncestor and AncestorType). This are out of question, as it's not supported in Windows Store Apps.
Other solutions suggest using ElementName. While this works in Windows Store Apps, it's not an acceptable solution, as it reusable DataTemplates impossible.
One solution I did read about was using Attached Properties/Attached Behaviors, which sounds like the way to go and is a very generic and reusable method. But I couldn't make it work so far.
My current attempt is, to create a global Attached Property and access it in the ItemTemplate.
public class GlobalProperties : DependencyObject
{
public static object GetParentDataContext(DependencyObject obj)
{
return (object)obj.GetValue(ParentDataContextProperty);
}
public static void SetParentDataContext(DependencyObject obj, object value)
{
obj.SetValue(ParentDataContextProperty, value);
}
// Using a DependencyProperty as the backing store for ParentDataContext. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ParentDataContextProperty =
DependencyProperty.RegisterAttached("ParentDataContext",
typeof(object),
typeof(GlobalProperties),
new PropertyMetadata(null, ParentDataContextChanged));
private static void ParentDataContextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SetParentDataContext(d, e.NewValue);
}
}
And my XAML (simplified by inlining the DataTemplate within the ListViews XAML Code. It will be later stored outside in an DataTemplate.xaml file.
<ListView
my:GlobalProperties.ParentDataContext="{Binding}"
ItemsSource="{Binding Questions}"
>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
<Setter Property="Margin" Value="0,-1,0,0" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid Background="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(my:GlobalProperties.ParentDataContext).Site.Styling.TagBackgroundColor}">
<!-- Item Related DataBindings -->
</Grid>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Where my is the namespace where my GlobalProperties is defined.
When I have a breakpoint in ParentDataContextChanged it's definitely called with the DataContext. But I can't get the XAML code to work to read it. The Background Xaml Property is always empty and the background always remains white (color saved in the TagBackgroundColor property is red).
Anyone know what's wrong with the code/attempt?
update
Also Background="{Binding RelativeSource={RelativeSource Self}, Path=(my:GlobalProperties.ParentDataContext).Site.Styling.TagBackgroundColor}" doesn't work, since that's what there in most website that show attached properties for such a case.
update 2
Example of of the ViewModel for better clarification
public class QuestionsViewModel : ViewModel
{
// Site.Styling.BackgroundColor to be bound to certain ItemTemplate
// Properties, but don't have access to it from the ItemTemplate
// because it has the context of one item from Questions
public Site Site { get; set; };
// Questions bound to ListView.DataSource > becomes Item's DataContext
public IEnumerable<Question> Questions { get; set; };
}
public class Site
{
public Style Styling { get; set; }
}
public class Style
{
public string ForegroundColor { get; set; }
public string BackgroundColor { get; set; }
public string LinkColor { get; set; }
}
With reference to above comment, I'm putting sample code.
Main.xaml
<Page
x:Class="TempApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converter="using:TempApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView x:Name="MyList">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
<Setter Property="Margin" Value="0,-1,0,0" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Grid x:Name="ListItemDataTemplateGrid"
HorizontalAlignment="Stretch">
<Grid.Resources>
<converter:ValueToBackgroundConverter x:Key="ValueToBackgroundConverter" BackgroundColor="{Binding BgColor}" />
</Grid.Resources>
<Grid Background="{Binding Converter={StaticResource ValueToBackgroundConverter}}">
<!--Your Content-->
</Grid>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
Main.xaml.cs
public sealed partial class MainPage : Page
{
public List<TempList> ListDataSource = new List<TempList>();
public MainPage()
{
this.InitializeComponent();
FillList();
}
private void FillList()
{
ListDataSource.Clear();
ListDataSource.Add(new TempList { BgColor = "Red" });
ListDataSource.Add(new TempList { BgColor = "Red" });
MyList.ItemsSource = ListDataSource;
}
}
public class TempList
{
public string BgColor { get; set; }
}
ValueToBackgroundConverter.cs
class ValueToBackgroundConverter : DependencyObject, IValueConverter
{
public string BackgroundColor
{
get { return (string)GetValue(BackgroundColorProperty); }
set { SetValue(BackgroundColorProperty, value); }
}
public static readonly DependencyProperty BackgroundColorProperty =
DependencyProperty.Register("BackgroundColor",
typeof(string),
typeof(ValueToBackgroundConverter), null
);
public object Convert(object value, System.Type targetType, object parameter, string language)
{
//I've used static colors but you can do manipulations to convert string to color brush
if (BackgroundColor != null)
return new SolidColorBrush(Color.FromArgb(0xFF, 0xA3, 0xCE, 0xDC));
else
return new SolidColorBrush(Color.FromArgb(0xFF, 0xE3, 0xF0, 0xF4));
}
public object ConvertBack(object value, System.Type targetType, object parameter, string language)
{
throw new System.NotImplementedException();
}
}

Binding a property that hides another in WinRT XAML (Windows8, Metro, Windows Store App)

right up front i want to say: please don't suggest alternative solutions, unless you can accomplish it without modifying the Types that are of the pattern BaseXXXXXX
that said, this behavior goes way beyond perplexing as far as I am concerned, it would seem that using the new keyword in order to hide a property in C# means that WinRT XAML (Windows8, Metro, Windows Store App) binding no longer functions correctly. I have no idea why this is.
Here is an example:
C#:
namespace WinRtSandbox
{
public class BaseClass
{
public string Property1 { get; set; }
public int[] Property2 { get; set; }
public object Property3 { get; set; }
}
public class ModifiedClass : BaseClass
{
public new string Property1 { get; set; }
public new long[] Property2 { get; set; }
public new string Property3 { get; set; }
}
public sealed partial class MainPage : Page
{
public BaseClass Normal { get; set; }
public ModifiedClass Modified { get; set; }
public MainPage()
{
this.Normal = new BaseClass
{
Property1 = "WTF",
Property2 = new[] { 2, 3, 4 },
Property3 = "Work?"
};
this.Modified = new ModifiedClass
{
Property1 = "WTF",
Property2 = new[] { 2L, 3L, 4L },
Property3 = "Work?"
};
this.InitializeComponent();
}
}
}
WinRT XAML:
<Page
x:Class="WinRtSandbox.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WinRtSandbox"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Border Background="#22000000" Padding="40" Width="400" Height="500">
<Grid>
<Grid.Resources>
<Style TargetType="Rectangle">
<Setter Property="Height" Value="1"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Margin" Value="0,15,0,15"/>
<Setter Property="Fill" Value="{StaticResource ApplicationForegroundThemeBrush}"/>
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<ItemsControl>
<TextBlock Text="this.Normal"/>
<Rectangle/>
<TextBlock Text="this.Normal.Property1"/>
<Rectangle/>
<TextBlock Text="this.Normal.Property2"/>
<Rectangle/>
<TextBlock Text="this.Normal.Property3"/>
</ItemsControl>
<Rectangle Fill="Red"/>
<ItemsControl>
<TextBlock Text="this.Modified"/>
<Rectangle/>
<TextBlock Text="this.Modified.Property1"/>
<Rectangle/>
<TextBlock Text="this.Modified.Property2"/>
<Rectangle/>
<TextBlock Text="this.Modified.Property3"/>
</ItemsControl>
</StackPanel>
<StackPanel Grid.Column="1">
<ItemsControl DataContext="{Binding Normal}">
<TextBlock Text="{Binding}"/>
<Rectangle/>
<TextBlock Text="{Binding Property1}"/>
<Rectangle/>
<TextBlock Text="{Binding Property2}"/>
<Rectangle/>
<TextBlock Text="{Binding Property3}"/>
</ItemsControl>
<Rectangle Fill="Red"/>
<ItemsControl DataContext="{Binding Modified}">
<TextBlock Text="{Binding}"/>
<Rectangle/>
<TextBlock Text="{Binding Property1}"/>
<Rectangle/>
<TextBlock Text="{Binding Property2}"/>
<Rectangle/>
<TextBlock Text="{Binding Property3}"/>
</ItemsControl>
</StackPanel>
</Grid>
</Border>
</Grid>
</Page>
The all-too-incorrect result looks something like:
basically, every one of those blank lines should be filled do any of you XAML hotshots have any idea why these bindings are failing and is there anything that can be done to work around what I can only assume is a heinous bug? Any help or insight would be greatly appreciated, thank you in advance... -ck
update: the output dump i forgot
Error: BindingExpression path error: 'Property2' property not found on 'WinRtSandbox.ModifiedClass'. BindingExpression: Path='Property2' DataItem='WinRtSandbox.ModifiedClass'; target element is 'Windows.UI.Xaml.Controls.TextBlock' (Name='null'); target property is 'Text' (type 'String')
Error: BindingExpression path error: 'Property3' property not found on 'WinRtSandbox.ModifiedClass'. BindingExpression: Path='Property3' DataItem='WinRtSandbox.ModifiedClass'; target element is 'Windows.UI.Xaml.Controls.TextBlock' (Name='null'); target property is 'Text' (type 'String')
Update:
Bug filed with Microsoft: https://connect.microsoft.com/VisualStudio/feedback/details/782993/binding-a-property-that-hides-another-in-winrt-xaml so we'll see how that goes
I agree it does a appear to be a bug.
I know you said you don't want alternatives, but for the sake of anyone else who might read this question, I'll ignore you.
You can fix this is by making the properties each into a DependencyProperty
public class BaseClass : DependencyObject
{
public static readonly DependencyProperty Property1Property = DependencyProperty.Register(
"Property1",
typeof(string),
typeof(BaseClass),
new PropertyMetadata(null));
public string Property1
{
get { return (string)GetValue(Property1Property); }
set { SetValue(Property1Property, value); }
}
public static readonly DependencyProperty Property2Property = DependencyProperty.Register(
"Property2",
typeof(int[]),
typeof(BaseClass),
new PropertyMetadata(null));
public int[] Property2
{
get { return (int[])GetValue(Property2Property); }
set { SetValue(Property2Property, value); }
}
public static readonly DependencyProperty Property3Property = DependencyProperty.Register(
"Property3",
typeof(object),
typeof(BaseClass),
new PropertyMetadata(null));
public object Property3
{
get { return GetValue(Property3Property); }
set { SetValue(Property3Property, value); }
}
}
public class ModifiedClass : BaseClass
{
public static readonly new DependencyProperty Property1Property = DependencyProperty.Register(
"Property1",
typeof(string),
typeof(ModifiedClass),
new PropertyMetadata(null));
public new string Property1
{
get { return (string)GetValue(Property1Property); }
set { SetValue(Property1Property, value); }
}
public static readonly new DependencyProperty Property2Property = DependencyProperty.Register(
"Property2",
typeof(long[]),
typeof(ModifiedClass),
new PropertyMetadata(null));
public new long[] Property2
{
get { return (long[])GetValue(Property2Property); }
set { SetValue(Property2Property, value); }
}
public static readonly new DependencyProperty Property3Property = DependencyProperty.Register(
"Property3",
typeof(string),
typeof(ModifiedClass),
new PropertyMetadata(null));
public new string Property3
{
get { return (string)GetValue(Property3Property); }
set { SetValue(Property3Property, value); }
}
}
Turns out this is an "unsupported bug" whatever that means... Here's the direct quote:
Hi, sorry for our late response. We don't support this bug so far.
please go to http://support.microsoft.com or call 1-800-MICROSOFT for
assistance. Thanks.
... hopefully this will be fixed in .NET 4.5.1
view the bug report on:
https://connect.microsoft.com/VisualStudio/feedback/details/782993/binding-a-property-that-hides-another-in-winrt-xaml

Categories