Derive from IconElement or IconSourceElement in UWP - c#

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>

Related

XAML x:Class Generic

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.

How to link InkToolbar to an InkCanvas which is inside CustomControl?

I am creating a CustomControl which contain InkCanvas. Now the problem is How do I link InkToolbar(which is outside the CustomControl) to an InkCanvas(which is inside the CustomControl)?
Solution Tried:
I tried to get the InkCanvas outside the CustomControl using below code but It is not working.
Here is my code(With the solution I tried which is not working):
//In CustomControl Code Behind
InkCanvas PATH_INK_CANVAS;
protected override void OnApplyTemplate()
{
PATH_INK_CANVAS = GetTemplateChild<InkCanvas>("PATH_INK_CANVAS");
}
T GetTemplateChild<T>(string elementName) where T : DependencyObject
{
var element = GetTemplateChild(elementName) as T;
if (element == null)
throw new NullReferenceException(elementName);
return element;
}
public InkCanvas InkCanvas
{
get { return PATH_INK_CANVAS; }
}
public static readonly DependencyProperty InkCanvasProperty =
DependencyProperty.Register("InkCanvas", typeof(InkCanvas), typeof(RichInkTextBox), new PropertyMetadata(0));
//In CustomControl XAML
<Style>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Name="MainGrid" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
<InkCanvas Name="PATH_INK_CANVAS" Canvas.ZIndex="-1"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
//In Page
<local:CustomControl x:Name="MyCustomControl"/>
<InkToolbar Grid.Row="0" TargetInkCanvas="{x:Bind MyCustomControl.InkCanvas}"/>
I don't think that's the right syntax to define a read-only dependency property. Try something like the following instead -
public InkCanvas InkCanvas
{
get => (InkCanvas)GetValue(InkCanvasProperty);
private set => SetValue(InkCanvasProperty, value);
}
public static readonly DependencyProperty InkCanvasProperty = DependencyProperty.Register(
"InkCanvas", typeof(InkCanvas), typeof(InkCanvasWrapper), new PropertyMetadata(null));
Also, make sure you set the Mode of the x:Bind to OneWay as the default value of the InkCanvas dependency property is null (you are setting the default value to 0 which is wrong).
<InkToolbar Grid.Row="0" TargetInkCanvas="{x:Bind MyCustomControl.InkCanvas, Mode=OneWay}" />

WPF: Parent which would display a specific child depending on input value

As far as I understand, usual Visibility={Binding SomeValue, Converter={…}} still keeps node in visual and logical tree even if it’s invisible. But what if I want to remove it completely and at the same time keep syntax light?
Right now, I’ve made a class called Switch allowing me to do stuff like that:
<Switch Value="{Binding Status}">
<TextBlock Switch.When="{x:Static Status.NotFound}" Text="Not found" />
<Button Switch.When="{x:Static Status.ConnectionError}" Text="Connection error. Try again?" />
<Grid Switch.When="{x:Static Status.Loaded}">…</Grid>
</Switch>
Here is a source code of that Switch thing.
I like how it looks and works, but sometimes some errors occur. For instance, while removing visual child from its previous location, rarely, but System.InvalidOperationException: Cannot modify the logical children for this node at this time because a tree walk is in progress. might happen. Cases like this are solvable, but the whole thing with them makes me think I’m doing something very wrong. What could it be? Maybe the whole idea is just not compatible with WPF at all? Or maybe I’m just missing something (like that thing that I have to override IEnumerator LogicalChildren { get; } to make it work properly)?
I think the answer is probably that you're trying to reparent the child controls the right way. Bad idea! They're just arbitrary content; don't treat them as controls. The actual reparenting in my code is done by hidden ContentPresenter magic in the template. All our control class code does is just sling them around like potatoes.
Here's a working version of the control that shouldn't give you any backtalk. But note that I had trouble comparing boxed enum values to each other. I'm interested in how you solved that problem.
Switch.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
namespace SwitchTestProject
{
[ContentProperty("Items")]
public class Switch : Control
{
public Switch()
{
Items = new List<DependencyObject>();
}
static Switch()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Switch), new FrameworkPropertyMetadata(typeof(Switch)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
OnValueChanged(null);
}
#region Switch.When Attached Property
public static Object GetWhen(DependencyObject obj)
{
return (Object)obj.GetValue(WhenProperty);
}
public static void SetWhen(DependencyObject obj, Object value)
{
obj.SetValue(WhenProperty, value);
}
public static readonly DependencyProperty WhenProperty =
DependencyProperty.RegisterAttached("When", typeof(Object), typeof(Switch),
new PropertyMetadata(null));
#endregion Switch.When Attached Property
#region Content Property
public Object Content
{
get { return (Object)GetValue(ContentProperty); }
protected set { SetValue(ContentPropertyKey, value); }
}
internal static readonly DependencyPropertyKey ContentPropertyKey =
DependencyProperty.RegisterReadOnly(nameof(Content), typeof(Object), typeof(Switch),
new PropertyMetadata(null));
public static readonly DependencyProperty ContentProperty = ContentPropertyKey.DependencyProperty;
#endregion Content Property
#region Value Property
public Object Value
{
get { return (Object)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(nameof(Value), typeof(Object), typeof(Switch),
new FrameworkPropertyMetadata(null, Value_PropertyChanged));
protected static void Value_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as Switch).OnValueChanged(e.OldValue);
}
private void OnValueChanged(object oldValue)
{
if (Value is IComparable)
{
// Boxed value types have to be a special case.
// Unless I jumped to an unwarranted conclusion about == not working.
var icompval = Value as IComparable;
foreach (var item in Items)
{
var icompwhen = GetWhen(item) as IComparable;
if (icompwhen != null && icompval.CompareTo(icompwhen) == 0)
{
Content = item;
return;
}
}
}
else
{
Content = Items.FirstOrDefault(item => GetWhen(item) == Value);
}
}
#endregion Value Property
#region Items Property
public List<DependencyObject> Items
{
get { return (List<DependencyObject>)GetValue(ItemsProperty); }
protected set { SetValue(ItemsPropertyKey, value); }
}
internal static readonly DependencyPropertyKey ItemsPropertyKey =
DependencyProperty.RegisterReadOnly(nameof(Items), typeof(List<DependencyObject>), typeof(Switch),
new PropertyMetadata(null));
public static readonly DependencyProperty ItemsProperty = ItemsPropertyKey.DependencyProperty;
#endregion Items Property
}
}
App.xaml or Themes\Generic.xaml
You could do a lot more with styling the parent here.
<Style TargetType="local:Switch">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:Switch">
<ContentPresenter
/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Usage:
<local:Switch
Value="{Binding Status}"
>
<TextBlock
local:Switch.When="{x:Static local:Status.NotFound}"
>This is a test</TextBlock>
<TextBlock
local:Switch.When="{x:Static local:Status.ConnectionError}"
>There was an error in the connection</TextBlock>
</local:Switch>
Pure XAML alternative
The bug in your Switch control is probably fixable, but this will work reliably without any nonsense (other than all the verbosity).
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding Status}" Value="NotFound">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="Not found" />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Status}" Value="ConnectionError">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Button Content="Connection error. Try again?" />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Status}" Value="Loaded">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Ellipse
Height="32"
Width="32"
Fill="DeepSkyBlue"
/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
We set the content using DataTemplates rather than setting the Content property directly because if we use the latter method, only one instance of each child control will ever exist, so we wouldn't be able to factor the style out as a resource and reuse it.
And because templating is the canonical way to create new controls in XAML.

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..

WPF binding to property of non-simple property of other class

A little bit can't figure out how to use WPF binding in this case:
Assume, we have an object Car with non-simple property of type CarInfo:
public class CarInfo : DependencyObject
{
public static readonly DependencyProperty MaxSpeedProperty =
DependencyProperty.Register("MaxSpeed", typeof (double), typeof (CarInfo), new PropertyMetadata(0.0));
public double MaxSpeed
{
get { return (double) GetValue(MaxSpeedProperty); }
set { SetValue(MaxSpeedProperty, value); }
}
}
public class Car : DependencyObject
{
public static readonly DependencyProperty InfoProperty =
DependencyProperty.Register("Info", typeof (CarInfo), typeof (Car), new PropertyMetadata(null));
public CarInfo Info
{
get { return (CarInfo) GetValue(InfoProperty); }
set { SetValue(InfoProperty, value); }
}
}
Also assume, Car is an ui element and it has the Car.xaml, something simple:
<Style TargetType="assembly:Car">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="assembly:Car">
<Grid >
!--> <TextBlock Text="{Binding Path=MaxSpeed}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
So, I wanted this TextBlock, in my Car.xaml, to represent the property "MaxSpeed" of my CarInfo class, which is actually a property of my Car class. How can I do this?
Thank you in advance, appreciate any help! :)
It depends upon what is assigned to the DataCOntext of the UI element representing the Car - you need to specify a binding path relative to that. In this case I would suggest you start with this:
<TextBlock Text="{Binding Path=Info.MaxSpeed}" />
this is assuming that a Car object has been assigned to the DataContext of the Car UI element.
Note that your properties don't have to be dependency properties - you can also bind to normal properties (depending on what you are doing).
Edit
It seems you are looking to use element binding, so you should be able to achieve what you want by using either the TemplatedParent or an ancestor as your relative source. See this previous SO answer for an example. Your binding should look something like this:
<TextBlock Text="{Binding Path=Info.MaxSpeed, RelativeSource={RelativeSource TemplatedParent}}" />
This will take you back to your templated parent control (the Car), then travel down the Info property of the UI element to the MaxSpeed property on its contents.
As I said in my comment, you are making this very messy by having your UI element so closely match your data element and then assigning your data object to a relatively non standard property on the UI element. You might have your reasons, but XAML and WPF don't need to be that complicated.
<TextBlock Text="{Binding Path=Info.MaxSpeed}" />
That code works well for me:
<TextBlock Text="{Binding Path=Info.MaxSpeed, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
and usage:
Car.Info = new CarInfo { MaxSpeed = 100.0 };

Categories