i created a custom Canvas that inhirat from Canvas, i declared a new Dependency Property "NewMouseOver" that i want to affect via Setter in Trigger.
public class CanvaNetwork : Canvas
{
public CanvaNetwork() { }
public bool NewMouseOver
{
get { return (bool)GetValue(NewMouseOverProperty); }
set { SetValue(NewMouseOverProperty, value); }
}
public static readonly DependencyProperty NewMouseOverProperty =
DependencyProperty.Register("NewMouseOver", typeof(bool),
typeof(CanvaNetwork), new PropertyMetadata(false));
}
and here is my XAML :
<DataTemplate DataType="{x:Type local:Node}">
<local:CanvaNetwork x:Name="ItemCanvas_Node"
NewMouseOver="{Binding MyMouseOver}"
Background="Transparent">
<Path x:Name="Path_NodeProcess"
Stroke="Green"
Fill="Gray"
Stretch="None"
Data="{Binding Path =Geometryform}"
Visibility="{Binding Path=Visibility}">
</Path>
<local:CanvaNetwork.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="NewMouseOver" Value="True" />
</Trigger>
</local:CanvaNetwork.Triggers>
</local:CanvaNetwork>
</DataTemplate>
hera is my Node Class :
Public Node :DependencyObject
{
public static readonly DependencyProperty MyMouseOverProperty =
DependencyProperty.Register("MyMouseOver", typeof(bool), typeof(NodeProcess), new PropertyMetadata(true,new PropertyChangedCallback(On_MyMouseOver)));
private static void On_MyMouseOver(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//..some code
}
public bool MyMouseOver
{
get { return (bool)GetValue(MyMouseOverProperty); }
set { SetValue(MyMouseOverProperty, value); }
}
}
what i want is :
1-i have DependencyProperty : NewMouseOver (has get and set not like IsMouseOver in the original Canvas Class).
2-acces to NewMouseOver via Trigger/Setter and change the state of NewMouseOver .
3-via XAML : set a binding betwin : NewMouseOver (in CanvaNetwork) & MyMouseOver (in Node Class)
4-after that i'll use On_MyMouseOver (in Node Class) and MyMouseOver to make some stuff.
I think that I can answer the question about how to update the DependencyProperty in your canvas object.
To test it, I would define an "on changed" method for the dependency property. You can put a breakpoint here to verify that the dependency property is set.
class CanvaNetwork : Canvas
{
public CanvaNetwork ( ) { }
public static readonly DependencyProperty NewMouseOverProperty
= DependencyProperty.Register ( "NewMouseOver",
typeof (bool),
typeof (CanvaNetwork),
new PropertyMetadata (false, OnNewMouseOverChanged)) ;
public bool NewMouseOver
{
get { return (bool)GetValue (NewMouseOverProperty); }
set { SetValue (NewMouseOverProperty, value); }
}
public static void OnNewMouseOverChanged ( DependencyObject d, DependencyPropertyChangedEventArgs e )
{
}
}
In the DataTemplate, you have to define the triggers within a Style.
<DataTemplate DataType="{x:Type local:Node}">
<local:CanvaNetwork x:Name="ItemCanvas_Node"
Background="red" Height="100" Width="100">
<Path x:Name="Path_NodeProcess"
Stroke="Green"
Fill="Gray"
Stretch="None"
Data="{Binding Path =Geometryform}"
Visibility="{Binding Path=Visibility}">
</Path>
<local:CanvaNetwork.Style>
<Style>
<Setter Property="local:CanvaNetwork.NewMouseOver" Value="False" />
<Style.Triggers>
<Trigger Property="Canvas.IsMouseOver" Value="True">
<Setter Property="local:CanvaNetwork.NewMouseOver" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
</local:CanvaNetwork.Style>
</local:CanvaNetwork>
</DataTemplate>
You can only update a property with a trigger, if the default property is set within the style.
For this to work I have removed your attritute NewMouseOver="{Binding MyMouseOver}". From your list, points 1 and 2 work, but removing this attribute means that point 3 does not work.
However, I think that you are probably taking the wrong approach anyway. Wouldn't it be better to hook up the MouseOver event to a command property in your Node class, as described here:
How to make MouseOver event in MVVM?
Related
I want to set a dependency property that I declared in my Class1 that inherits from DependencyObject:
public static readonly DependencyProperty MyMouseOverProperty = DependencyProperty.Register("MyMouseOver", typeof(bool), typeof(Class1),
new PropertyMetadata(false,new PropertyChangedCallback(On_MyMouseOver)));
private static void On_MyMouseOver(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// some code here
}
public bool MyMouseOver
{
get { return (bool)GetValue(MyMouseOverProperty); }
set { SetValue(MyMouseOverProperty, value); }
}
I'll use "MyMouseOver" in XAML in order to use its state in "On_MyMouseOver" to affect another Object.
<DataTemplate DataType="{x:Type local:Class1}">
<Canvas x:Name="Canvas_Classe1"
Background="Transparent">
<Canvas.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="{Binding Path=MyMouseOver}" Value="True"/>
</Trigger>
</Canvas.Triggers>
</Canvas>
but ... this is not working.
How to use Setter to access to a DependencyProperty declared not within a control but a class ?
You can accomplish this with attached property
public class Class1
{
public static readonly DependencyProperty MyMouseOverProperty = DependencyProperty.RegisterAttached(
"MyMouseOver", typeof(bool), typeof(Class1), new FrameworkPropertyMetadata(false, PropertyChangedCallback)
);
public static void SetMyMouseOver(UIElement element, Boolean value)
{
element.SetValue(MyMouseOverProperty, value);
}
public static bool GetMyMouseOver(UIElement element)
{
return (bool)element.GetValue(MyMouseOverProperty);
}
private static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// some code here
}
}
Set attached property on DataTemplate IsMouseOver
<DataTemplate DataType="{x:Type local:Class1}">
<Canvas x:Name="Canvas_Classe1"
Background="Black">
</Canvas>
<DataTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="local:Class1.MyMouseOver" Value="True"/>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
When I set collection value from xaml within the control as such it seems to set value correctly:
<local:InjectCustomArray>
<local:InjectCustomArray.MyProperty>
<x:Array Type="local:ICustomType">
<local:CustomType/>
<local:CustomType/>
<local:CustomType/>
</x:Array>
</local:InjectCustomArray.MyProperty>
<local:InjectCustomArray>
if value is being set from style it does not appear to be hitting dependency property callback if you set initial value of it in ctor:
<local:InjectCustomArray>
<local:InjectCustomArray.Style>
<Style TargetType="local:InjectCustomArray">
<Style.Triggers>
<DataTrigger Binding="{Binding NonExistingProp}" Value="{x:Null}">
<Setter Property="MyProperty">
<Setter.Value>
<x:Array Type="local:ICustomType">
<local:CustomType/>
<local:CustomType/>
<local:CustomType/>
<local:CustomTypeTwo/>
</x:Array>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="Black"/>
</DataTrigger>
</Style.Triggers>
</Style>
</local:InjectCustomArray.Style>
</local:InjectCustomArray>
Code of control:
public partial class InjectCustomArray : UserControl
{
public InjectCustomArray()
{
InitializeComponent();
// If following line is being commented out then setter value in xaml is being set, otherwise not.
MyProperty = new ICustomType[0];
}
public ICustomType[] MyProperty
{
get { return (ICustomType[])GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(ICustomType[]), typeof(InjectCustomArray), new PropertyMetadata(Callback));
private static void Callback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// This is being hit only once from a ctor.
// If ctor is commented out then this value is being set from setter correctly.
}
}
Collection items code:
public interface ICustomType
{
}
public class CustomType : ICustomType
{
}
public class CustomTypeTwo : ICustomType
{
}
Question: Is there some optimisation within DependancyProperty when values of it is being set within close time range? What is the cause of such behavior?
The cause is that a local property value has higher precedence than a value from a Style Setter.
Instead of setting a local value, you could use the SetCurrentValue method:
public InjectCustomArray()
{
InitializeComponent();
SetCurrentValue(MyPropertyProperty, new ICustomType[0]);
}
See the Dependency Property Value Precedence article on MSDN for more details.
I'm attemping to study from Material Design for XAML source code.
Here is their GitHub:
MaterialDesignInXamlToolkit
Here is the the code I'm looking into:
PackIcon
Here is the helper class for PackIcon:
PackIconBase
PackIconDataFactory
Currently, I'm looking at their icon pack example and doing a quick test on it.
Here is the test class:
public class PackIconTest : Control
{
static PackIconTest()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(PackIconTest), new FrameworkPropertyMetadata(typeof(PackIconTest)));
}
public PackIconTest()
{
Data = "M4.93,4.93C3.12,6.74 2,9.24 2,12C2,14.76 3.12,17.26 4.93,19.07L6.34,17.66C4.89,16.22 4,14.22 4,12C4,9.79 4.89,7.78 6.34,6.34L4.93,4.93M19.07,4.93L17.66,6.34C19.11,7.78 20,9.79 20,12C20,14.22 19.11,16.22 17.66,17.66L19.07,19.07C20.88,17.26 22,14.76 22,12C22,9.24 20.88,6.74 19.07,4.93M7.76,7.76C6.67,8.85 6,10.35 6,12C6,13.65 6.67,15.15 7.76,16.24L9.17,14.83C8.45,14.11 8,13.11 8,12C8,10.89 8.45,9.89 9.17,9.17L7.76,7.76M16.24,7.76L14.83,9.17C15.55,9.89 16,10.89 16,12C16,13.11 15.55,14.11 14.83,14.83L16.24,16.24C17.33,15.15 18,13.65 18,12C18,10.35 17.33,8.85 16.24,7.76M12,10A2,2 0 0,0 10,12A2,2 0 0,0 12,14A2,2 0 0,0 14,12A2,2 0 0,0 12,10Z";
}
private static readonly DependencyPropertyKey DataPropertyKey =
DependencyProperty.RegisterReadOnly(nameof(Data), typeof(string), typeof(PackIconTest), new PropertyMetadata(""));
public static readonly DependencyProperty DataProperty = DataPropertyKey.DependencyProperty;
[TypeConverter(typeof(GeometryConverter))]
public string Data
{
get { return (string)GetValue(DataProperty); }
private set { SetValue(DataPropertyKey, value); }
}
}
Here is the XAML usage:
<local:PackIconTest Width="200" Height="200"/>
The icon doesn't show. What am I missing?
I solved this propblem by making a style applying for PackIconTest target type.
Here is the xaml code:
<Window.Resources>
<Style TargetType="local:PackIconTest">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Path Data="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Data}"
Stroke="Black"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
I have two custom controls/visuals and I need an Orientation property on both. In both cases, the default should be "Horizontal" but the user of the control/visual should be able to specify Orientation="Vertical" to arrange the components of the control/visual vertically. What I have works great on my ImageButton control, but not so well on my HeaderedLabel visual. Although both of them compile fine, Intellisense doesn't like one of them. Here's an example of their use...
<Visuals:ImageButton Image="Icons/ok.png" Content="Normal Content"/>
<Visuals:ImageButton Image="Icons/ok.png" Content="Vertical Content" Orientation="Vertical"/>
<Visuals:HeaderedLabel Header="Normal Header" Content="Normal Content"/>
<Visuals:HeaderedLabel Header="Vertical Header" Content="Vertical Content" Orientation="Vertical"/>
...which produces the following when rendered inside a vertical StackPanel:
So it does what I want, but the problem is this: While Intellisense recognizes the possible options for Orientation for the ImageButton, it does not recognize the possible options for Orientation for the HeaderedLabel. And while the code compiles & runs fine, there's a persistent error in the Visual Studio "Error List" pane: "'Vertical' is not a valid value for property 'Orientation'.", and there's a blue squiggly line under the text Orientation="Vertical" for the second HeaderedLabel in my xaml example above.
Here are the relevant files:
// File 'ImageButton.cs'
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Visuals
{
public class ImageButton : Button
{
static ImageButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton),
new FrameworkPropertyMetadata(typeof(ImageButton)));
}
public ImageSource Image
{
get { return (ImageSource)GetValue(ImageProperty); }
set { SetValue(ImageProperty, value); }
}
public Orientation Orientation
{
get { return (Orientation)GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}
// Note that for ImageButton, I can just say 'Orientation.Horizontal' and
// the compiler resolves that to System.Windows.Controls.Orientation...
public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register("Orientation", typeof(Orientation),
typeof(ImageButton), new UIPropertyMetadata(Orientation.Horizontal));
public static readonly DependencyProperty ImageProperty =
DependencyProperty.Register("Image", typeof(ImageSource),
typeof(ImageButton), new UIPropertyMetadata(null));
}
}
.
// File 'HeaderedLabel.cs'
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Visuals
{
public class HeaderedLabel : Control
{
static HeaderedLabel()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(HeaderedLabel),
new FrameworkPropertyMetadata(typeof(HeaderedLabel)));
}
public object Header
{
get { return (object)GetValue(HeaderProperty); }
set { SetValue(HeaderProperty, value); }
}
public object Content
{
get { return (object)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
public object Orientation
{
get { return (object)GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.Register("Header", typeof(object), typeof(HeaderedLabel),
new UIPropertyMetadata(null));
public static readonly DependencyProperty ContentProperty =
DependencyProperty.Register("Content", typeof(object), typeof(HeaderedLabel),
new UIPropertyMetadata(null));
// Note that for HeaderedLabel, unlike ImageButton, I have to specify the fully-
// qualified name of 'Orientation.Horizontal', otherwise the compiler resolves it
// to Visuals.HeaderedLabel.Orientation and gives a compiler error...
public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register("Orientation", typeof(System.Windows.Controls.Orientation),
typeof(HeaderedLabel), new UIPropertyMetadata(System.Windows.Controls.Orientation.Horizontal));
}
}
.
<!-- 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:Visuals"
xmlns:bind="clr-namespace:Visuals.BindingConverters">
<Style TargetType="{x:Type local:ImageButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ImageButton}">
<Button>
<StackPanel Orientation="{TemplateBinding Orientation}">
<Image Source="{TemplateBinding Image}"/>
<ContentPresenter/>
</StackPanel>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type local:HeaderedLabel}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:HeaderedLabel}">
<StackPanel Orientation="{TemplateBinding Orientation}">
<StackPanel Orientation="Horizontal">
<ContentControl Content="{TemplateBinding Header}" />
<TextBlock Text=":" />
</StackPanel>
<ContentControl Content="{TemplateBinding Content}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Any ideas why the compiler would resolve Orientation.Horizontal to System.Windows.Controls.Orientation.Horizontal for the ImageButton, but not for the HeaderedLabel? And more importantly, any ideas why Intellisense can't figure out the options for HeaderedLabel.Orientation?
BTW, I'm using VisualStudio 2012 and .NET Framework 4.0.
All of your properties, including the Orientation property, are declared as having the type object.
You should have this instead:
public Orientation Orientation
{
get { return (Orientation)GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}
The XAML editor should be able to correctly accept values of type Orientation if you declare the property correctly. Otherwise, it will attempt to assign a string value of "Vertical" to the property, which when passed to the SetValue() method will fail, because the DependencyProperty object itself was initialized with Orientation as the valid type and it has no way to convert from the string value to an Orientation value.
If you declare the property correctly, then WPF will understand automatically that it needs to convert the string value shown in the XAML to an Orientation value for the property (i.e. parse the string value as the appropriate enum type), and in that case the initialization should work.
I'm trying to create a "DropDownButton" with an icon in it, and I want to be able to set the icon source via an attached property (I found this is the (only?) way to do this). But for some reason, everything I tried fails, the best I could get was an empty Image container.
I thought this looked pretty good, but now I'm getting these errors:
The local property "Image" can only be applied to types that are derived from "IconButton".
The attachable property 'Image' was not found in type 'IconButton'.
The attached property 'IconButton.Image' is not defined on 'Button' or one of its base classes.
I'm probably doing this completely wrong (I've been trying and editing for about 2 hours now), but I just know there must be a way of doing this.
Relevant code is provided below, if anybody can even point me in the right direction that would be awesome!
EDIT: Updated code, still experiencing issue
Now I get this error in debug log:
System.Windows.Data Error: 40 : BindingExpression path error: 'Image' property not found on 'object' ''ContentPresenter' (Name='')'. BindingExpression:Path=Image; DataItem='ContentPresenter' (Name=''); target element is 'Image' (Name=''); target property is 'Source' (type 'ImageSource')
ImageButton.cs (thank you Viv):
class ImageButton : Button
{
public static readonly DependencyProperty ImageProperty =
DependencyProperty.Register("Image", typeof(ImageSource), typeof(ImageButton),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsRender));
public ImageSource Image
{
get { return (ImageSource)GetValue(ImageProperty); }
set { SetValue(ImageProperty, value); }
}
}
ImageButton Style:
<Style TargetType="{x:Type Controls:ImageButton}" x:Key="FormIconDropDownButton">
<Setter Property="Margin" Value="5" />
<Setter Property="Padding" Value="10,5,4,5" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type Controls:ImageButton}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image Style="{StaticResource FormButtonIcon-Small}"
Source="{Binding Image, RelativeSource={RelativeSource TemplatedParent}}"/>
<TextBlock Grid.Column="1"
Text="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}"
VerticalAlignment="Center"
Margin="0,0,9,0"/>
<Path Grid.Column="2"
Fill="Black"
Data="M 0 0 L 3.5 4 L 7 0 Z"
VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
In window xaml:
<Controls:ImageButton Content="Hello"
Style="{StaticResource FormIconDropDownButton}"
Image="{StaticResource Icon-Small-Locations}" />
You're just using the wrong control type in your xaml. You're still using the base class instead of your derived class.
Also you're declaring a dependency property not an attached property.
Attached Properties are registered with DependencyProperty.RegisterAttached(...)
Now you'll need to add a namespace to where your IconButton class is defined in your xaml such as
xmlns:local="clr-namespace:Mynamespace"
and then switch occurrences of
{x:Type Button} to {x:Type local:IconButton}
and
<Button ...> to <local:IconButton ...>
I wouldn't recommend an attached property for this tbh. Attached properties get way over-used when they shouldn't be probably just my opinion.
Check This thread for some differences between DP and AP usage. In this case it's a custom Button that shows an Image. Make it unique than homogenize the lot.
Update:
Download link using derived class(ImageButton) of Button with normal DP.
Everything looks correct except that you haven't declared an attached property. Instead you've just declared a normal DependencyProperty on your IconButton class which is then only valid to set on IconButton or classes derived from it. The declaration of an attached property (which can be set on any type) uses a different call to register and also uses get/set methods instead of a wrapper property:
public static readonly DependencyProperty ImageProperty =
DependencyProperty.RegisterAttached(
"Image",
typeof(ImageSource),
typeof(IconButton),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsRender));
public static ImageSource GetImage(DependencyObject target)
{
return (ImageSource)target.GetValue(ImageProperty);
}
public static void SetImage(DependencyObject target, ImageSource value)
{
target.SetValue(ImageProperty, value);
}
This is my example of Extending Base class, use Dependency Properties in Style and in View.
For more details write in this post.
public class ItemsList : ListView {
public static readonly DependencyProperty ItemIconProperty = DependencyProperty.Register("ItemIcon", typeof(ImageSource), typeof(ItemsList));
public ImageSource ItemIcon {
get { return (ImageSource)GetValue(ItemIconProperty); }
set { SetValue(ItemIconProperty, value); }
}
public static readonly DependencyProperty DoubleClickCommandProperty = DependencyProperty.Register("DoubleClickCommand", typeof(ICommand), typeof(ItemsList));
public ControlTemplate DoubleClickCommand {
get { return (ControlTemplate)GetValue(DoubleClickCommandProperty); }
set { SetValue(DoubleClickCommandProperty, value); }
}
}
/Style for Extended ItemList where is 'ItemIcon' DependencyProperty Declared/
<Style x:Key="BaseDataSourcesWindowListMenuStyle" TargetType="Controls:ItemsList">
<Setter Property="ItemIcon" Value="/Presentation.Shared;component/Resources/Images/data_yellow.png" />
</Style>
<Style x:Key="DataSourcesListMenuStyle" TargetType="Controls:ItemsList"
BasedOn="{StaticResource BaseDataSourcesWindowListMenuStyle}">
<Setter Property="DoubleClickCommand" Value="{Binding Path=VmCommands.EditDataSourceDBCommand}" />
</Style>
/HOW I'M USING 'ItemIcon' DependencyProperty ON VIEW/
<Controls:ItemsList Grid.Column="0" Grid.Row="1" Margin="8" ItemsSource="{Binding DataSourceDbs}"
Style="{DynamicResource DataSourcesListMenuStyle}"
SelectedItem="{Binding SelectedDataSourceDB, Mode=TwoWay}" />
First of all, I think that's a dependency property you declared there, and it belongs to the IconButton custom control, and you're trying to use it on a Button control.
See http://msdn.microsoft.com/en-us/library/ms749011.aspx for reference on how to declare attached properties, and assign that property to the Button class instead of the IconButton since you're not using that custom control in the code above.