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>
Related
I am trying to create a custom control which streamlines the look of toolbars across my application. Unfortunately i can't seem to find a way to forward children from my control to the underlying stackpanel. I've tried to solve it by using an ItemsControl, but that does not appear to be working either sadly.
Another approach i took is using OnApplyTemplate in the ToolbarPanel, but that does not seem to be working either.
Does anyone know how to get control forwarding working here?
ToolbarPanel.cs
[ContentProperty(nameof(InternalContent))]
public class ToolbarPanel : ContentControl
{
static ToolbarPanel()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ToolbarPanel), new FrameworkPropertyMetadata(typeof(ToolbarPanel)));
}
public static readonly DependencyProperty InternalContentProperty = DependencyProperty.Register(
nameof(InternalContent), typeof(object), typeof(ToolbarPanel), new PropertyMetadata(default(object)));
public object InternalContent
{
get { return (object) GetValue(InternalContentProperty); }
set { SetValue(InternalContentProperty, value); }
}
}
Generic.xaml
<Style TargetType="{x:Type local:ToolbarPanel}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ToolbarPanel}">
<ItemsControl ItemsSource="{TemplateBinding InternalContent}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentPresenter />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Thanks to #Clemens i managed to come up with an approach that ended up working. Key factors here of why previous attempts failed is because i did not use ItemsControl and because i did not set IsItemsHost to true. Once i did that, the StackPanel did exactly what i was expecting.
ToolbarPanel.cs
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
namespace NetworkMonitor.Framework.Controls
{
[ContentProperty(nameof(Items))]
public class ToolbarPanel : ItemsControl
{
static ToolbarPanel()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ToolbarPanel), new FrameworkPropertyMetadata(typeof(ToolbarPanel)));
}
public static readonly DependencyProperty ItemsSpacingProperty = DependencyProperty.Register(
nameof(ItemsSpacing), typeof(Thickness), typeof(ToolbarPanel), new PropertyMetadata(default(Thickness)));
public Thickness ItemsSpacing
{
get { return (Thickness) GetValue(ItemsSpacingProperty); }
set { SetValue(ItemsSpacingProperty, value); }
}
}
}
PanelExtensions.cs
using System.Windows;
using System.Windows.Controls;
namespace NetworkMonitor.Framework.Controls.Extensions
{
public class PanelExtensions
{
public static readonly DependencyProperty ChildMarginProperty = DependencyProperty.RegisterAttached(
"ChildMargin", typeof(Thickness), typeof(PanelExtensions), new UIPropertyMetadata(new Thickness(), ChildMarginChanged));
private static void ChildMarginChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is Panel panel)
{
panel.Loaded += PanelOnLoaded;
}
}
private static void PanelOnLoaded(object sender, RoutedEventArgs e)
{
if (sender is Panel panel)
{
panel.Loaded -= PanelOnLoaded;
var itemsMargin = GetChildMargin(panel);
var skipLast = GetSkipLastMargin(panel);
for (int i = 0; i < panel.Children.Count; i++)
{
if (skipLast && i == (panel.Children.Count - 1))
break;
var child = panel.Children[i];
if (child is FrameworkElement frameworkElement)
{
frameworkElement.Margin = itemsMargin;
}
}
}
}
[AttachedPropertyBrowsableForChildren(IncludeDescendants = true)]
[AttachedPropertyBrowsableForType(typeof(Panel))]
public static void SetChildMargin(DependencyObject element, Thickness value)
{
element.SetValue(ChildMarginProperty, value);
}
[AttachedPropertyBrowsableForChildren(IncludeDescendants = true)]
[AttachedPropertyBrowsableForType(typeof(Panel))]
public static Thickness GetChildMargin(DependencyObject element)
{
return (Thickness) element.GetValue(ChildMarginProperty);
}
public static readonly DependencyProperty SkipLastMarginProperty = DependencyProperty.RegisterAttached(
"SkipLastMargin", typeof(bool), typeof(PanelExtensions), new PropertyMetadata(true));
public static void SetSkipLastMargin(DependencyObject element, bool value)
{
element.SetValue(SkipLastMarginProperty, value);
}
public static bool GetSkipLastMargin(DependencyObject element)
{
return (bool) element.GetValue(SkipLastMarginProperty);
}
}
}
Generic.xaml
<Style TargetType="{x:Type local:ToolbarPanel}">
<Setter Property="Padding" Value="8"></Setter>
<Setter Property="ItemsSpacing" Value="0,0,8,0"></Setter>
<Setter Property="BorderThickness" Value="0,1,0,0"></Setter>
<Setter Property="BorderBrush" Value="Black"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ToolbarPanel}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}" >
<StackPanel Orientation="Horizontal"
IsItemsHost="True"
extensions:PanelExtensions.ChildMargin="{TemplateBinding ItemsSpacing}"
extensions:PanelExtensions.SkipLastMargin="True">
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
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?
I am creating a simple AutoComplete TextBox and have a list of values which If the user starts to enter any characters in them string, the appropriate string will appear.
Now I have created my Textbox with a Binding property to my ViewModel:
<TextBox Text="{Binding ServerURL, UpdateSourceTrigger=PropertyChanged}" />
So when the user enters a new character It will trigger my property to get fired and therefore fire a method which will retrieve the values it relates to.
private string _serverURL;
public string ServerURL {
get { return _serverURL; }
set
{
_serverURL = value;
ServerURL_TextChanged();
OnPropertyChanged("ServerURL");
}
}
The method will then just populate a ListBox with the results that string refers to.
When I select a value from the ListBox i want to set the Full string value to the TextBox text property, but when i do this it triggers the method ServerURL_TextChanged().
Is there a way I can set the ServerURL property, but not to fire the method inside it?
For a solution there is a need to separate the ways with which you can set ServerURL property.
public string ServerURL {
get { return _serverURL; }
set
{
setServerURL(value, isSetByUser = true);
}
}
private function void setServerURL(string value, bool isSetByUser){
_serverURL = value;
ServerURL_TextChanged(isSetByUser);
OnPropertyChanged("ServerURL");
}
When the List is changed you can call from the code setServerURL(someValue, isSetByUser = false);
And then in ServerURL_TextChanged implementation decide what to do with it.
The easiest way to implement this functionality is to handle TextChanged events from the code-behind, where you will have full control of the UI for this kind of decision making. It does not violate MVVM principles to manage UI operations from the code-behind.
Here is an example of such a code-behind implementation. You may find it useful.
public partial class AutoCompleteComboBox : UserControl
{
private Window w;
public AutoCompleteComboBox()
{
InitializeComponent();
}
~AutoCompleteComboBox()
{
if(w == null)
{
return;
}
else
{
w.MouseLeftButtonDown -= Window_MouseLeftDown;
}
}
#region Behaviours
public void FocusTextBox()
{
txt.Focus();
txt.CaretIndex = txt.Text.Length;
}
#endregion
#region DependencyProperties
public static readonly DependencyProperty InputPaddingProperty =
DependencyProperty.Register(
"InputPadding",
typeof(Thickness),
typeof(AutoCompleteComboBox)
);
public Thickness InputPadding
{
get
{
return (Thickness)GetValue(InputPaddingProperty);
}
set
{
SetValue(InputPaddingProperty, value);
}
}
public static readonly DependencyProperty TextBoxHeightProperty =
DependencyProperty.Register(
"TextBoxHeight",
typeof(double),
typeof(AutoCompleteComboBox)
);
public double TextBoxHeight
{
get
{
return (double)GetValue(TextBoxHeightProperty);
}
set
{
SetValue(TextBoxHeightProperty, value);
}
}
public static readonly DependencyProperty ItemPanelMaxHeightProperty =
DependencyProperty.Register(
"ItemPanelMaxHeight",
typeof(double),
typeof(AutoCompleteComboBox)
);
public double ItemPanelMaxHeight
{
get
{
return (double)GetValue(ItemPanelMaxHeightProperty);
}
set
{
SetValue(ItemPanelMaxHeightProperty, value);
}
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register(
"ItemsSource",
typeof(IEnumerable),
typeof(AutoCompleteComboBox)
);
public IEnumerable ItemsSource
{
get
{
return (IEnumerable)ItemsSource;
}
set
{
SetValue(ItemsSourceProperty, value);
}
}
public static readonly DependencyProperty DisplayMemberPathProperty =
DependencyProperty.Register(
"DisplayMemberPath",
typeof(string),
typeof(AutoCompleteComboBox)
);
public string DisplayMemberPath
{
get
{
return GetValue(DisplayMemberPathProperty).ToString();
}
set
{
SetValue(DisplayMemberPathProperty, value);
}
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(
"Text",
typeof(string),
typeof(AutoCompleteComboBox),
new FrameworkPropertyMetadata(
"",
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault
)
);
public string Text
{
get
{
return GetValue(TextProperty).ToString();
}
set
{
SetValue(TextProperty, value);
}
}
public string TargetValue { get; set; } = "";
public static readonly DependencyProperty IsDropDownOpenProperty =
DependencyProperty.Register(
"IsDropDownOpen",
typeof(bool),
typeof(AutoCompleteComboBox),
new FrameworkPropertyMetadata(
false,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault
)
);
public bool IsDropDownOpen
{
get
{
return (bool)GetValue(IsDropDownOpenProperty);
}
set
{
SetValue(IsDropDownOpenProperty, value);
}
}
#endregion
#region Events
private void me_Loaded(object sender, RoutedEventArgs e)
{
w = VisualTreeHelpers.FindAncestor<Window>(this);
w.MouseLeftButtonDown += Window_MouseLeftDown;
FocusTextBox();
}
private void Window_MouseLeftDown(object sender, MouseButtonEventArgs e)
{
IsDropDownOpen = false;
}
private void lst_KeyDown(object sender, KeyEventArgs e)
{
}
private void lst_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (TargetValue != null && TargetValue.Trim().Length > 0)
{
txt.Text = TargetValue;
IsDropDownOpen = false;
}
FocusTextBox();
}
private void lst_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
}
private void lst_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (lst.SelectedItem != null)
{
TargetValue = lst.SelectedItem.ToString();
}
}
private void txt_LostFocus(object sender, RoutedEventArgs e)
{
if (lst.IsFocused == false)
{
IsDropDownOpen = false;
FocusTextBox();
}
}
private void lst_LostFocus(object sender, RoutedEventArgs e)
{
MessageBox.Show("text changed");
if (txt.IsFocused == false)
{
IsDropDownOpen = false;
}
}
private void txt_TextChanged(object sender, TextChangedEventArgs e)
{
IsDropDownOpen = true;
}
private void txt_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (IsDropDownOpen && lst.Items.Count > 0)
{
if (lst.SelectedIndex < 0)
{
lst.SelectedIndex = 0;
}
if (e.Key == Key.Up && lst.SelectedIndex > 0)
{
lst.SelectedIndex--;
}
else if (e.Key == Key.Down && lst.SelectedIndex < lst.Items.Count - 1)
{
lst.SelectedIndex++;
}
else if(e.Key == Key.Enter || e.Key == Key.Tab)
{
if(lst.SelectedIndex > -1)
{
txt.Text = TargetValue;
IsDropDownOpen = false;
FocusTextBox();
}
}
}
}
#endregion
}
And here's the XAML
<UserControl x:Class="SHARED_COMPONENTS.AutoCompleteComboBox"
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"
x:Name="me"
Loaded="me_Loaded"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition />
</Grid.RowDefinitions>
<TextBox
x:Name="txt"
Background="CornflowerBlue"
Foreground="White"
Grid.Row="0"
Text="{Binding ElementName=me, Path=Text,UpdateSourceTrigger=PropertyChanged}"
TextChanged="txt_TextChanged" PreviewKeyDown="txt_PreviewKeyDown"
Height="{Binding ElementName=me, Path=ActualHeight}"
Padding="{Binding ElementName=me,Path=InputPadding}"
/>
<Popup IsOpen="{Binding ElementName=me, Path=IsDropDownOpen}" ClipToBounds="False">
<Border Grid.Row="1">
<Border.Effect>
<DropShadowEffect Color="Black" />
</Border.Effect>
<ListBox
x:Name="lst"
Grid.Row="1"
ItemsSource="{Binding ElementName=me, Path=ItemsSource}"
PreviewKeyDown="lst_KeyDown"
SelectionChanged="lst_SelectionChanged"
PreviewMouseLeftButtonDown="lst_MouseLeftButtonDown"
PreviewMouseLeftButtonUp="lst_PreviewMouseLeftButtonUp"
DisplayMemberPath="{Binding ElementName=me, Path=DisplayMemberPath }"
ClipToBounds="False"
>
<ListBox.Style>
<Style TargetType="ListBox">
<Setter Property="Background" Value="#f0f0f0" />
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=lst, Path=HasItems}" Value="True" />
<Condition Binding="{Binding ElementName=me, Path=IsDropDownOpen}" Value="True" />
</MultiDataTrigger.Conditions>
<Setter Property="Visibility" Value="Visible" />
</MultiDataTrigger>
<DataTrigger Binding="{Binding ElementName=me, Path=IsDropDownOpen}" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Style>
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}" Value="True">
<Setter Property="IsSelected" Value="True" />
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsSelected}" Value="True">
<Setter Property="Foreground" Value="CornflowerBlue" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Resources>
</ListBox>
</Border>
</Popup>
</Grid>
</Grid>
</UserControl>
I have a custom control like:
public sealed class BorderEx : Control
{
public static readonly RoutedEvent ReloadClickEvent = EventManager.RegisterRoutedEvent("ReloadClick", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(BorderEx));
public event RoutedEventHandler ReloadClick
{
add { AddHandler(ReloadClickEvent, value); }
remove { RemoveHandler(ReloadClickEvent, value); }
}
void RaiseReloadClickEvent()
{
var newEventArgs = new RoutedEventArgs(ReloadClickEvent);
RaiseEvent(newEventArgs);
}
static BorderEx()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(BorderEx), new FrameworkPropertyMetadata(typeof(BorderEx)));
}
}
and there is event rised on reloadButton click at generic.xaml
<ControlTemplate TargetType="{x:Type cbr:BorderEx}">
<ControlTemplate.Triggers>
<Trigger SourceName="reloadButton" Property="IsPressed" Value="True">
<Setter TargetName="reloadButton" Property="Background" Value="Green"/>
<EventSetter Event="ReloadClick" Handler="RaiseReloadClickEvent"></EventSetter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate
But I have no idea how to raise this external event from internal button. Spend last few hours googling and ended up with nothing. above EventSetter is not working.
This part was ok,
public static readonly RoutedEvent ReloadClickEvent = EventManager.RegisterRoutedEvent("ReloadClick", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(BorderEx));
public event RoutedEventHandler ReloadClick
{
add { AddHandler(ReloadClickEvent, value); }
remove { RemoveHandler(ReloadClickEvent, value); }
}
static BorderEx()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(BorderEx), new FrameworkPropertyMetadata(typeof(BorderEx)));
}
I had to create dependency property, so I can change some value on click event
public static readonly DependencyProperty ReloadProperty = DependencyProperty.Register("Reload", typeof (bool), typeof (BorderEx), new PropertyMetadata(default(bool), ReloadClicked));
public bool Reload
{
get { return (bool) GetValue(ReloadProperty); }
set { SetValue(ReloadProperty, value); }
}
and I can handle it in additional method triggered on change
private static void ReloadClicked(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
if (!((bool) e.NewValue)) return;
var sender = (BorderEx) o;
sender.RaiseEvent(new RoutedEventArgs(ReloadClickEvent));
}
then it just changing value on click was needed
<ControlTemplate.Triggers>
<Trigger SourceName="reloadButton" Property="IsPressed" Value="True">
<Setter TargetName="reloadButton" Property="Background" Value="Green"/>
<Setter Property="Reload" Value="True"/>
</Trigger>
</ControlTemplate.Triggers>
Your event looks great, but the fact that EventSetter can not be set in the trigger. Quote from link:
Because using EventSetter to wire up event handler is a compile-time feature which is plumbed through IStyleConnector interface, there is another interface called IComponentConnector which is used by the XAML compiler to wire up event handler for standalone XAML elements.
You can do this. Identify EventSetter outside trigger, such as in the early Style / Template:
<Style TargetType="{x:Type local:BorderEx}">
<EventSetter Event="Button.Click" Handler="ReloadClickEvent" />
...
</Style>
Code behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void ReloadClickEvent(object sender, RoutedEventArgs e)
{
RaiseEvent(new DemoEventArgs(BorderEx.ReloadClickEvent, sender));
}
}
public class DemoEventArgs : RoutedEventArgs
{
public DemoEventArgs(RoutedEvent routedEvent, object source) : base(routedEvent, source)
{
MessageBox.Show("Raise!");
}
}
public sealed class BorderEx : Control
{
public static readonly RoutedEvent ReloadClickEvent = EventManager.RegisterRoutedEvent("ReloadClick", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(BorderEx));
public event RoutedEventHandler ReloadClick
{
add { AddHandler(ReloadClickEvent, value); }
remove { RemoveHandler(ReloadClickEvent, value); }
}
private void RaiseReloadClickEvent()
{
var newEventArgs = new RoutedEventArgs(ReloadClickEvent);
RaiseEvent(newEventArgs);
}
static BorderEx()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(BorderEx), new FrameworkPropertyMetadata(typeof(BorderEx)));
}
}
Or alternatively, use the DependencyProperty (can also be attached). Example:
Property definition:
public static readonly DependencyProperty SampleProperty =
DependencyProperty.RegisterAttached("Sample",
typeof(bool),
typeof(SampleClass),
new UIPropertyMetadata(false, OnSample));
private static void OnSample(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is bool && ((bool)e.NewValue) == true)
{
// do something...
}
}
Calling in XAML.
In EventTrigger:
<EventTrigger SourceName="MyButton" RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="MyBox" Storyboard.TargetProperty="(local:SampleClass.Sample)">
<DiscreteObjectKeyFrame KeyTime="0:0:0">
<DiscreteObjectKeyFrame.Value>
<sys:Boolean>True</sys:Boolean>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
Using with DataTrigger (in Style/DataTemplate/etc):
<DataTrigger Binding="{Binding ElementName=MyBox, Path=Status), Mode=OneWay}" Value="True">
<Setter Property="(local:SampleClass.Sample)" Value="True" />
</DataTrigger>
Using with Trigger (in Style):
<Trigger Property="MyCheckBox.IsChecked" Value="True">
<Setter Property="(local:SampleClass.Sample)" Value="True" />
</Trigger>
Using behind code:
private void Clear_Click(object sender, RoutedEventArgs e)
{
SampleClass.SetSampleClass(MyBox, true);
}
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.