I need to create a custom property meaning rather using
<Style x:Key="ABC" TargetType="Rectangle">
<Setter Property="Fill" Value="Red"/>
</Style>
I like to have something like Rectangle and assign it an ID so later when it is dropped on Canvas I can retrieve its ID.
<Style x:Key="ABC" TargetType="Rectangle">
<Setter Property="Fill" Value="Red"/>
**<Setter Property="ID" Value="1234567890-ABC"/>**
</Style>
How can I define that custom property?
Regards,
Amit
Define a custom attached property in a separate class:
public class Prop : DependencyObject
{
public static readonly DependencyProperty IDProperty =
DependencyProperty.RegisterAttached("ID", typeof(string), typeof(Prop), new PropertyMetadata(null));
public static void SetID(UIElement element, string value)
{
element.SetValue(IDProperty, value);
}
public static string GetID(UIElement element)
{
return (string)element.GetValue(IDProperty);
}
}
Then you can use this:
<Setter Property="local:Prop.ID" Value="1234567890-ABC"/>
local must be defined in the root element of your XAML appromimately like this:
xmlns:local="clr-namespace:AttPropTest"
where AttPropTest is the namespace of the assembly.
In code, you can determine the ID with Prop.GetID(myRect).
Related
I'm trying to bind two properties so I can change the background color in DataGrid based on their value. Based on these answers
How to bind an enum to a combobox control in WPF?
best way to bind enum propery in datagrid
I have implemented the advice in my code, but I'm missing something and it doesn't work.
Thanks for any advices.
namespace Example
{
public class ExampleClass
{
private ExampleObject exampleObject;
public ExampleObject ExampleObject { get; set; }
}
}
namespace Object
{
public class ExampleObject
{
private Value value;
public ExampleObject ExampleObject { get; set; }
}
public enum Value
{
High,
Low
}
}
Wpf DataGrid DataTrigger where I am changing the colour
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding ExampleObject.Value}" Value="{StaticResource CellConverter}">
<Setter Property="Background" Value="Green">
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding ExampleObject.Value}" Value="{StaticResource CellConverter}">
<Setter Property="Background" Value="Red">
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
CellConvertor class
public class CellConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Value input = ((Value)value);
switch (input)
{
case Value.High:
return "High";
case Value.Low:
return "Low";
default:
return DependencyProperty.UnsetValue;
}
}
}
You must fix the trigger condition in your example.
Additionally, in order to bind to the ExampleObject.Value enum value, the ExampleObject.Value must be a public property:
public class ExampleObject
{
public Value Value {get; set;}
}
In XAML you reference enum values like static variables and constants by using the x:Static markup extension. In fact, C# enum is implemented as a set of constants. When using the x:Static extension, your current value converter CellConverter becomes obsolete:
<!--
In this example the namespace that defines the enum type 'Value'
is assumed to be registered under the XAML alias 'local'
-->
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding ExampleObject.Value}"
Value="{x:Static local:Value.Low}">
<Setter Property="Background" Value="Green" />
</DataTrigger>
<DataTrigger Binding="{Binding ExampleObject.Value}"
Value="{x:Static local:Value.High}">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
Remarks: in general, if you want to use a value converter, then you must configure the Binding accordingly and assign the IValueConverter instance to the Binding.Converter property. You can't assign a IValueConverter directly to a property to make it convert. The IValueConverter must receive an input that he can convert to produce the output. The input is the value provided by the actual Binding (and that's why Binding has a Binding.Converter property - they always go in tandem).
See Microsoft Docs: DataBinding Overview (Data conversion)
Note, since the property DataTrigger.Value is not a DependencyProperty, you can't define its value via a Binding.
Converter example:
<Window>
<Window.Resources>
<MyValueConverter x:Name="MyValueConverter" />
</Window.Resources>
<SomeObject SomeDependencyProperty="{Binding SourceProperty, Converter={StaticResource MyValueConverter}}" />
</Window>
It's also recommende best practice to define a default enum value to avoid errors. The default value for an enum instance is always 0 which in your case would default to High. Better allow to identify an unset state by adding an explicit 0 value named None or Default:
public enum Value
{
None = 0,
High,
Low
}
See: Microsoft Docs: Enum Design
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 have a class that inherits from TextBox
public class DecimalTextBox : TextBox
{
#region Float Color
public static readonly DependencyProperty FloatColorProperty = DependencyProperty.Register("FloatColor", typeof(Color), typeof(DecimalTextBox), new FrameworkPropertyMetadata(Colors.Red));
public Color FloatColor
{
get { return (Color)GetValue(FloatColorProperty); }
set { SetValue(FloatColorProperty, value); }
}
#endregion
. . . . .. OTHER STUFF
}
I want to style this control using something like this:
<Style x:Key="DecimalTextBoxGridStyle" TargetType="DecimalTextBox">
<Setter Property="TextAlignment" Value="Right"/>
<Setter Property="FloatColor" Value="Black"/>
<Setter Property="BorderBrush" Value="Transparent"/>
</Style>
But it style told me
DecimalTexbox type isn't admited in wpf project
How can I do to that ?
There is another approach ?
Include the XAML namespace:
<Style x:Key="DecimalTextBoxGridStyle" TargetType="local:DecimalTextBox">
where local is mapped to the CLR namespace in which the DecimalTextBox class is defined:
<Window ...
xmlns:local="clr-namespace:WpfApplication1"
[My main idea of this is to set visible/hidden for a usercontrol. I used WPF with Mvvmcross.]
I have a user control call SpinningWheelUserControl. I want to visible/hide it with the datatrigger. Below is my xaml code in App.xaml
In App.xaml I have added the namespace of the usercontrol as below.
xmlns:local="clr-namespace:UserControl"
The following is a style setting for my usercontrol.
<Style x:Key="SpinningWheel" TargetType="{x:Type local:SpinningWheelUserControl}" >
<Style.Triggers>
<DataTrigger Binding="{Binding IsVisible}" Value="true">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
<DataTrigger Binding="{Binding IsVisible}" Value="false">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
There is a class for SpinningWheel
public class SpinningWheelViewModel
: MvxNotifyPropertyChanged
{
public bool IsVisible { get; set; }
}
In a constructor of parent class, i use like this code
SpinningWheel = new SpinningWheelViewModel();
SpinningWheel.IsVisible = false;
The usercontrol is hidden for a first running. But when I change the IsVisble to true, it has no change.
SpinningWheel.IsVisible = true
You need to set Visibility instead of IsVisible like this:
SpinningWheel.Visibility = Visibility.Visible;
Oh now i see, you are setting your custom IsVisibility instead of UIElement property.
Issue with your code is you haven't raised PropertyChanged to let UI know that some property change in underlying source object.
private bool isVisible;
public bool IsVisible
{
get { return isVisible;}
set
{
if(isVisible != value)
{
isVisible = value;
RaisePropertyChanged("IsVisible");
}
}
}
Assuming you have implemented INotifyPropertyChanged on your class.
This n+1 video called N=34 : a data-bound busy dialog shows exactly how to do what you are trying to do.
I've created a WPF UserControl which contains a Button and a ComboBox. I'd like to change the style of both, depending on the position of the mouse, so the UIElement with the mouse over is coloured Black and the other is coloured Red. If neither are styled then the default styling will apply.
Don't worry, this nightmarish colour scheme is just to illustrate the concept!
Thanks in advance for your help.
XAML
<UserControl x:Class="WpfUserControlSample.ToolbarButtonCombo"
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:local="clr-namespace:WpfUserControlSample"
x:Name="Control"
mc:Ignorable="d"
d:DesignHeight="30">
<UserControl.Resources>
<Style TargetType="{x:Type local:ToolbarButtonCombo}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsButtonMouseOver}" Value="True">
<Setter Property="ButtonStyle" Value="Black"/>
<Setter Property="ComboStyle" Value="Red"/>
</DataTrigger>
<!--
<DataTrigger Binding="{Binding IsComboMouseOver}" Value="True">
<Setter Property="ButtonStyle" Value="Red"/>
<Setter Property="ComboStyle" Value="Black"/>
</DataTrigger>
-->
</Style.Triggers>
</Style>
</UserControl.Resources>
<StackPanel Orientation="Horizontal" Height="30">
<Button Name="btn" Background="{Binding ButtonStyle,ElementName=Control,Mode=OneWay}">
Test
</Button>
<ComboBox Name="cmb" Background="{Binding ComboStyle,ElementName=Control,Mode=OneWay}"></ComboBox>
</StackPanel>
</UserControl>
Codebehind:
namespace WpfUserControlSample
{
public partial class ToolbarButtonCombo : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public ToolbarButtonCombo()
{
InitializeComponent();
btn.MouseEnter += new MouseEventHandler(btn_MouseChanged);
btn.MouseLeave += new MouseEventHandler(btn_MouseChanged);
}
void btn_MouseChanged(object sender, MouseEventArgs e)
{
OnPropertyChanged("IsButtonMouseOver");
}
public bool IsButtonMouseOver
{
get { return btn.IsMouseOver; }
}
public static readonly DependencyProperty IsButtonMouseOverProperty =
DependencyProperty.Register("IsButtonMouseOver", typeof(string), typeof(ToolbarButtonCombo), new PropertyMetadata("false"));
public string ButtonStyle { get; set; }
public static readonly DependencyProperty ButtonStyleProperty =
DependencyProperty.Register("ButtonStyle", typeof(string), typeof(ToolbarButtonCombo));
public string ComboStyle { get; set; }
public static readonly DependencyProperty ComboStyleProperty =
DependencyProperty.Register("ComboStyle", typeof(string), typeof(ToolbarButtonCombo));
}
}
There are a two problems.
First your DataTrigger bindings do not look correct. They are looking for the IsButtonMouseOver on the DataContext, not the associated control. You'd need to use:
<DataTrigger Binding="{Binding IsButtonMouseOver, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="ButtonStyle" Value="Black"/>
<Setter Property="ComboStyle" Value="Red"/>
</DataTrigger>
Or:
<Trigger Property="IsButtonMouseOver" Value="True">
<Setter Property="ButtonStyle" Value="Black"/>
<Setter Property="ComboStyle" Value="Red"/>
</Trigger>
The other is your IsButtonMouseOver is not implemented correctly. You should do something like:
public static readonly DependencyProperty IsButtonMouseOverProperty = DependencyProperty.Register("IsButtonMouseOver",
typeof(bool), typeof(ToolbarButtonCombo), new PropertyMetadata(false));
public bool IsButtonMouseOver
{
get { return (bool)this.GetValue(IsButtonMouseOverProperty); }
set { this.SetValue(IsButtonMouseOverProperty, value); }
}
void btn_MouseChanged(object sender, MouseEventArgs e)
{
this.IsButtonMouseOver = this.btn.IsMouseOver;
}
Or even more correctly, make the IsButtonMouseOver a read-only dependency property like so:
private static readonly DependencyPropertyKey IsButtonMouseOverPropertyKey = DependencyProperty.RegisterReadOnly("IsButtonMouseOver",
typeof(bool), typeof(ToolbarButtonCombo), new FrameworkPropertyMetadata(false));
public static readonly DependencyProperty IsButtonMouseOverProperty = ToolbarButtonCombo.IsButtonMouseOverPropertyKey.DependencyProperty;
public bool IsButtonMouseOver {
get { return (bool)this.GetValue(IsButtonMouseOverProperty); }
private set { this.SetValue(IsButtonMouseOverPropertyKey, value); }
}
Your other properties (ButtonStyle and ComboStyle) would need to be properly implemented also, and their get/set methods are not backed by the dependency property.