I have the following ContextMenu defined for my data grid:
<igDP:XamDataGrid.ContextMenu>
<ContextMenu ItemsSource="{Binding CommandViewModels}" >
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding Command}" />
<Setter Property="CommandParameter" Value="{Binding CommandParameter}" />
<Setter Property="Header" Value="{Binding Name}" />
<Setter Property="Icon" Value="{Binding Icon}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</igDP:XamDataGrid.ContextMenu>
A CommandViewModel class is defined as follows:
public class CommandViewModel : ICommandViewModel
{
public CommandViewModel(string name, Image icon, ICommand command, object commandParameter = null, int index = 0)
{
Name = name;
Icon = icon;
Command = command;
CommandParameter = commandParameter;
Index = index;
}
public string Name { get; set; }
public Image Icon { get; set; }
public ICommand Command { get; set; }
public object CommandParameter { get; set; }
public int Index { get; set; }
}
When I right click on a row in the grid, each MenuItem of the ContextMenu is correctly styled. The icon, label and command of the MenuItem is as expected. However, the command parameter, CommandViewModel.CommandParameter, that should be passed as argument to the RelayCommand bound to MenuItem.Command is null.
I am fairly certain that the command parameter available for the binding is not null. This is WPF application running on .NET 4.0.
Anyone experienced this?
This is apparently a known problem with the CommandParameter binding.
Since I did not want to edit Prism code, I ended up using the CommandParameterBehavior class defined in the referenced CodePlex post.
Modifying my custom RelayCommand class to implement IDelegateCommand as follows:
public class RelayCommand : IDelegateCommand
{
readonly protected Predicate<object> _canExecute;
readonly protected Action<object> _execute;
public RelayCommand(Predicate<object> canExecute, Action<object> execute)
{
_canExecute = canExecute;
_execute = execute;
}
public void RaiseCanExecuteChanged()
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, EventArgs.Empty);
}
public virtual bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public event EventHandler CanExecuteChanged;
public virtual void Execute(object parameter)
{
_execute(parameter);
}
}
and modifying my original style to use the CommandParameterBehavior like so:
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding Command}" />
<Setter Property="CommandParameter" Value="{Binding CommandParameter}" />
<Setter Property="Header" Value="{Binding Name}" />
<Setter Property="Icon" Value="{Binding Icon}" />
<Setter Property="utility:CommandParameterBehavior.IsCommandRequeriedOnChange" Value="true"
</Style>
The CommandParameter is now passed correctly.
Related
I have some elements on my main filter, in my wpf application.
But I donĀ“t want to set visibility and isEnabled one by one in get/set. Is there more elegant way, how to change it from view model?
Thank you! :)
You can use a DataTrigger to change some properties of your Button based on the view model properties:
<Window.Resources>
<local:MyViewModel x:Key="viewModelInstance"></local:MyViewModel>
</Window.Resources>
<StackPanel>
<Button DataContext="{StaticResource viewModelInstance}" Content="My Button">
<Button.Style>
<Style TargetType="Button">
<!-- Default style is Visible and Enabled -->
<Setter Property="IsEnabled" Value="True"></Setter>
<Setter Property="Visibility" Value="Visible"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsAllowed}" Value="False">
<!-- Hide and disable when IsAllowed is false -->
<Setter Property="IsEnabled" Value="False"></Setter>
<Setter Property="Visibility" Value="Hidden"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
Assuming you have a view model class defined like:
public class MyViewModel : INotifyPropertyChanged {
public bool IsAllowed { get; set; } = true;
//Put more logic here of course.
}
MyViewModel should implement INotifyPropertyChanged to automatically notify the UI to update the view when the IsAllowed property changes, for example like this:
public class MyViewModel : INotifyPropertyChanged {
//Backing field for IsAllowed
private bool _isAllowed = true;
/// <summary>
/// Gets or sets the IsAllowed property.
/// </summary>
public bool IsAllowed {
get => _isAllowed; set {
if (_isAllowed != value) {
_isAllowed = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsAllowed)));
}
}
}
//INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
}
You can also look into the IValueCOnverter interface and XAML converters for other ways.
Context :
I have a treeview with a separate details view injecting by PRISM library when I click on one of my treeviewitem (and I can update all properties of my item with it). All my items have a Enabled property.
Problem :
When I update programmatically my viewmodels property, my object is updated. If I click on an other treeviewitem and come back to the first one, I see the property was updated.
All the updates are good when I enable/disable the item using my details view (the foreground is going grey and the property is change)
But in my case, when I try to update it by a command triggered by a contextMenu it doesn't trigger the view and all the updates... but my viewmodel property is updated...
What am I going wrong ?
I am using ObservableCollection in my treeview, maybe I need to change the type of my collection ?
I have my BaseViewModel who implements NotifyPropertyChanged
public abstract class NotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(Expression<Func<object>> propertyExpression)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(GetPropertyName(propertyExpression)));
}
private string GetPropertyName(Expression<Func<object>> propertyExpression)
{
var unaryExpression = propertyExpression.Body as UnaryExpression;
var memberExpression = unaryExpression == null ? (MemberExpression)propertyExpression.Body : (MemberExpression)unaryExpression.Operand;
var propertyName = memberExpression.Member.Name;
return propertyName;
}
}
So I call the property change method, but Why my view is not updating then ?
[DefaultValue(true)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
public bool Enabled
{
get
{
return Model.Enabled;
}
set
{
if (value != Model.Enabled)
{
Model.Enabled = value;
OnPropertyChanged(() => Model.Enabled);
}
}
}
Here is the code of my view (for the command)
<MenuItem Header="Enable/Disable this equipment" Command="{Binding PlacementTarget.Tag.DataContext.ToogleEquipmentCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"
CommandParameter="{Binding}" InputGestureText="CTRL+D"/>
And here is the code of my view (Hierarchical data template from my treeview)
<!-- ModuleItems > IP / Name -->
<HierarchicalDataTemplate DataType="{x:Type siemens:ModuleItemSiemensViewModel}" >
<StackPanel Orientation="Horizontal">
<TextBlock Name="ItemIp"
Text="{Binding Path=Ip}" ContextMenu="{StaticResource ContextMenuEquipment}" Tag="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Enabled}" Value="False">
<Setter Property="Background" Value="LightGray"/>
<Setter Property="Foreground" Value="Black"/>
</DataTrigger>
<DataTrigger Binding="{Binding Enabled}" Value="True">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock Text=" / " ContextMenu="{StaticResource ContextMenuEquipment}" Tag="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Enabled}" Value="False">
<Setter Property="Background" Value="LightGray"/>
</DataTrigger>
<DataTrigger Binding="{Binding Enabled}" Value="True">
<Setter Property="Background" Value="Transparent"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock Name="ItemName" ContextMenu="{StaticResource ContextMenuEquipment}" Tag="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}"
Text="{Binding Path=Name}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Enabled}" Value="False">
<Setter Property="Background" Value="LightGray"/>
<Setter Property="Foreground" Value="Black"/>
</DataTrigger>
<DataTrigger Binding="{Binding Enabled}" Value="True">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="Blue"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
EDIT :
Here is the code from my viewmodel and models :
My real problem is when I update an Item (with my property enabled) it update the item, but my list (ModuleItems) is not updated, what I need to do to correctly implement MVVM and to make my fields automatically updated ?
public class ModuleParamSiemensViewModel : ModuleParamBaseViewModel
{
#region Attributes
private ObservableCollection<ModuleItemSiemensViewModel> _moduleItems;
private ModuleParamSiemens _model;
private string _moduleType;
#endregion
#region Constructor
public ModuleParamSiemensViewModel(ModuleParamSiemens moduleParam) : base(moduleParam)
{
this.Model = moduleParam;
this.ModuleType = "Siemens";
ModuleItems = new ObservableCollection<ModuleItemSiemensViewModel>();
Initialize();
}
#endregion
#region Properties
public new ModuleParamSiemens Model
{
get
{
return _model;
}
set
{
if (value != _model)
{
_model = value;
OnPropertyChanged(() => Model);
}
}
}
public new ObservableCollection<ModuleItemSiemensViewModel> ModuleItems
{
get
{
return _moduleItems;
}
set
{
this._moduleItems = value;
OnPropertyChanged(() => ModuleItems);
}
}
public override string ModuleType
{
get
{
return _moduleType;
}
set
{
this._moduleType = value;
OnPropertyChanged(() => ModuleType);
}
}
#endregion
#region Public Methods
public void Initialize()
{
foreach (ModuleItemSiemens item in this.Model.ModuleItems)
{
Add(new ModuleItemSiemensViewModel(item));
}
}
public void Add(ModuleItemSiemensViewModel item)
{
ModuleItems.Add(item);
}
#endregion
}
Model :
public class ModuleParamSiemens : ModuleParam
{
public new ObservableCollection<ModuleItemSiemens> ModuleItems { get; set; }
public ModuleParamSiemens()
{
ModuleItems = new ObservableCollection<ModuleItemSiemens>();
}
}
EDIT 2 :
Add ItemSiemensViewModel
public class ItemSiemensViewModel : ItemBaseViewModel
{
#region Attributes
private ItemSiemens _model;
#endregion
#region Constructor
public ItemSiemensViewModel(ItemSiemens item)
{
this.Model = item;
}
#endregion
#region Properties
public new ItemSiemens Model
{
get
{
return _model;
}
set
{
if (value != _model)
{
_model = value;
OnPropertyChanged(() => Model);
}
}
}
public new OPCInfo Opc
{
get
{
return Model.Opc;
}
set
{
if (value != Model.Opc)
{
Model.Opc = value;
OnPropertyChanged(() => Model.Opc);
}
}
}
public ProtocolInfoSiemens Protocol
{
get
{
return Model.Protocol;
}
set
{
if (value != Model.Protocol)
{
Model.Protocol = value;
OnPropertyChanged(() => Model.Protocol);
}
}
}
#endregion
#region Public Methods
#endregion
}
ItemSiemens :
public class ItemSiemens : Item
{
public ProtocolInfoSiemens Protocol { get; set; }
}
ItemBaseViewModel
public abstract class ItemBaseViewModel : BaseViewModel
{
public OPCInfoBaseViewModel Opc { get; set; }
public ItemBaseViewModel()
{
}
}
Item
public abstract class Item
{
public OPCInfo Opc { get; set; }
}
I have found the answer.
My bindings are correct (or at least it works)
The problem is that I used ObservableCollection collection and when an item is update in this collection it's not even fire an event to say that something has changed (it does for adding and removing items)
So I have implemented my own ItemsChangeObservableCollection (you can look this answer : https://stackoverflow.com/a/33866549/8237280)
And now all my problems in all my app are solved !
You are sending INotifyPropertyChanged on your ModuleItemSiemensViewModel for the property Model.Enabled. This does not make much sense, as nobody is listening on the VM (ModuleItemSiemensViewModel) for this change. The INPC interface does not allow such a kind of update. Every control listens on the same object that it's binding a property. That means you can only send PropertyChanged for properties that are in the same class/instance the interface is declared.
You have to move the NotifyPropertyChanged to the "Model" instance and call it there like this:
[DefaultValue(true)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
public bool Enabled
{
get
{
return Model.Enabled;
}
set
{
if (value != Model.Enabled)
{
Model.Enabled = value;
Model.OnPropertyChanged(() => Enabled);
}
}
}
I want to create a Tab Control which can hold for multiple User Controls.
<TabControl Padding="0">
<TabItem Header="{x:Static p:Resources.Scheduler}"
Visibility="{Binding ShellService.IsSchedulerEnabled,
Converter={StaticResource BoolToVisibilityConverter}}">
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<Trigger Property="IsVisible" Value="True">
<Setter Property="Content"
Value="{Binding ShellService.LazySchedulerView.Value}"/>
</Trigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</TabItem>
</TabControl>
The xaml is only for 1 tab item, which control by ShellService.IsSchedulerEnabled and the content is ShellService.LazySchedulerView.Value.
My problem here is that if I want to create a new TabItem, I have to create a new TabItem tag in the xaml.
How can I create a dynamic tab control to hold more than 1 tab item without specifying 'Value' in ContentControl.
public interface IShellService : INotifyPropertyChanged
{
object ShellView { get; }
bool IsSchedulerEnabled { get; set; }
Lazy<object> LazySchedulerView { get; set; }
}
[Export(typeof(IShellService)), Export]
internal class ShellService : Model, IShellService
{
private object _shellView;
private bool _isSchedulerEnabled;
private Lazy<object> _lazySchedulerView;
public object ShellView
{
get { return _shellView; }
set { SetProperty(ref _shellView, value); }
}
public bool IsSchedulerEnabled
{
get { return _isSchedulerEnabled; }
set { SetProperty(ref _isSchedulerEnabled, value); }
}
public Lazy<object> LazySchedulerView
{
get { return _lazySchedulerView; }
set { SetProperty(ref _lazySchedulerView, value); }
}
}
You can use Style for this TabItem. I created some example for you. You should change Bindings to your own. And you should create ObservableCollection of ShellServices and bind it to the TabControl. I hope this helps.
<TabControl ItemsSource="{Binding Objects}">
<TabControl.Resources>
<Style TargetType="TabItem" x:Key="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding Header}"></Setter>
<Style.Triggers>
<Trigger Property="IsVisible" Value="True">
<Setter Property="Content" Value="{Binding Text}"/>
</Trigger>
</Style.Triggers>
</Style>
</TabControl.Resources>
</TabControl>
Update
ViewModel Sample
public class OwnObject : ViewModelBase
{
private string _text;
public string Text
{
get { return _text; }
set { _text = value; NotifyPropertyChanged( "Text" ); }
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
protected void NotifyPropertyChanged( String info )
{
if ( PropertyChanged != null )
{
PropertyChanged( this, new PropertyChangedEventArgs( info ) );
}
}
}
I bound these objects to TabControl.
private ObservableCollection<OwnObject> _objects = new ObservableCollection<OwnObject>();
public ObservableCollection<OwnObject> Objects
{
get { return _objects; }
set { _objects = value; NotifyPropertyChanged( "Objects" ); }
}
I'm trying to disable a menuitem depending on objects in an ObservableCollection.
MainViewModel:
public ObservableCollection<ThumbnailModel> Thumbnails { get; set; }
public MainWindowViewModel()
{
Thumbnails = new ObservableCollection<ThumbnailModel>();
this.CreateMenu();
}
private void CreateMenu()
{
//TODO: Add tooltip to menu with short description
var items = new List<MenuItemViewModel>();
var item = new MenuItemViewModel();
item.MenuText = "File";
item.MenuItems = new List<MenuItemViewModel> {
new MenuItemViewModel { MenuText = "Select all", MenuCommand = this.SelectAllCommand, IsEnabled = SelectAllCommand.CanExecute(Thumbnails) },
new MenuItemViewModel { MenuText = "Unselect all", MenuCommand = this.UnselectAllCommand, IsEnabled = true },
};
items.Add(item);
//And so on
MenuItems = items;
}
public ICommand SelectAllCommand
{
get
{
return this.selectAllCommand ??
(this.selectAllCommand = new DelegateCommand(SelectAll, ((t) => ((ObservableCollection<ThumbnailModel>)t).Any(o => !o.IsChecked))));
}
}
Xaml:
<Window.Resources>
<!--Menu template-->
<HierarchicalDataTemplate DataType="{x:Type viewModels:MenuItemViewModel}"
ItemsSource="{Binding Path=MenuItems}">
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command"
Value="{Binding MenuCommand}"/>
<Setter Property="CommandParameter"
Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
<Setter Property="IsEnabled"
Value="{Binding IsEnabled}"/>
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding MenuIcon}" />
<TextBlock Text="{Binding MenuText}" />
</StackPanel>
</HierarchicalDataTemplate>
</Window.Resources>
<Menu DockPanel.Dock="Top" ItemsSource="{Binding Path=MenuItems}" />
When opening the File-menu, I get an exception.
System.ArgumentNullException was unhandled HResult=-2147467261
Message=Value cannot be null. Parameter name: source
Source=System.Core
ParamName=source
StackTrace:
at System.Linq.Enumerable.Any[TSource](IEnumerable1 source, Func2 predicate)
at KoenHoefman.PhotoResizer.ViewModels.MainWindowViewModel.b__e(Object
t) in d:\000 TFS
Workspace\KoenHoefman.PhotoResizer\Main\KoenHoefman.PhotoResizer\ViewModels\MainWindowViewModel.cs:line
126
at KoenHoefman.PhotoResizer.ViewModels.DelegateCommand.CanExecute(Object
parameter) in d:\000 TFS
Workspace\KoenHoefman.PhotoResizer\Main\KoenHoefman.PhotoResizer\ViewModels\DelegateCommand.cs:line
95
at MS.Internal.Commands.CommandHelpers.CanExecuteCommandSource(ICommandSource
commandSource)
at System.Windows.Controls.MenuItem.UpdateCanExecute()
at System.Windows.Controls.MenuItem.HookCommand(ICommand command)
...
At first I tought the reason was the fact that there are no items in MenuItems at the start.
However, when I run the folowing code after my menu-creation it returns false (as expected).
var y = SelectAllCommand.CanExecute(Thumbnails);
Any idea what's going wrong here and of course how to fix it?
UPDATE
Must have looked over it before but when the CanExecute-method is hit, the parameter is null, although I've specified it to be Thumbnails ?
DelegateCommand:
using System;
using System.Windows.Input;
public class DelegateCommand : ICommand
{
private readonly Action<object> execute;
private readonly Predicate<object> canExecute;
public DelegateCommand(Action<object> execute)
: this(execute, null)
{}
public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
this.execute = execute;
this.canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
this.execute(parameter);
}
public bool CanExecute(object parameter) // parameter is null when breakpoint is hit
{
return this.canExecute == null || this.canExecute(parameter);
}
}
If I understand predicates correctly (which is not sure), the method will be executed every time it's called. But what about the parameter I've put in at the time of the assignment? Is this only used one time?
The definition of a Predicate is this:
public delegate bool Predicate<in T>( T obj)
All it does is some type of compare or test on the obj and returns true or false. We see this all the time in LINQ.
var myList = getEmployees();
var filter = myList.Where(p=>p.lastName == "Jones");
The delegate is the "p" or parameter, and the comparison part is the predicate or bool value.. Notice that the type passed in in "implied" this is because linq has a static class "Where" extension method allowing up to pass in any collection type which will take a predicate as the parm. Like this:
public static IEnumerable<TSource> Where<TSource>(
this IEnumerable<TSource> source,
Func<TSource, int, bool> predicate
)
Per the example at MSFT site on the Delegate command we see the new creation of one, and the second parm is passing a method (pointer) named "CanSubmit" to be called when needed.
public MyClass()
{
this.submitCommand = new DelegateCommand<int?>(this.Submit, this.CanSubmit);
}
private bool CanSubmit(int? customerId)
{
return (customerId.HasValue && customers.Contains(customerId.Value));
}
Finally figured it out while going through the code, step by step and stumbling upon this question
Turns out that
By default menu items become disabled when its command cannot be
executed (CanExecute = false).
(Could not find any reference to that in MSDN??)
So the solution became a lot simpler since I didn't need the IsEnabled property on my MenuItemViewModel anymore.
My XAML looks now like:
<Window.Resources>
<!--Menu template-->
<HierarchicalDataTemplate DataType="{x:Type viewModels:MenuItemViewModel}"
ItemsSource="{Binding Path=MenuItems}">
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command"
Value="{Binding MenuCommand}"/>
<Setter Property="CommandParameter"
Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
<!-- No longer needed. By default menu items become disabled when its command cannot be executed (CanExecute = false).
<Setter Property="IsEnabled"
Value="{Binding IsEnabled}"/>-->
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding MenuIcon}" />
<TextBlock Text="{Binding MenuText}" />
</StackPanel>
</HierarchicalDataTemplate>
</Window.Resources>
And my commands:
public ICommand SelectAllCommand
{
get
{
return this.selectAllCommand ?? (this.selectAllCommand = new DelegateCommand(SelectAll, delegate(object obj) { return Thumbnails.Any(t => !t.IsChecked); }));
}
}
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.