is there an issue with two way binding to the IsChecked property on a ToggleButton in .NET 3.5?
I have this XAML:
<ToggleButton
Name="tbMeo"
Command="{Binding FilterOnSatelliteTypeCmd}"
IsChecked="{Binding ShowMeoDataOnly, Mode=TwoWay}"
ToolTip="Show MEO data only">
<Image Source="../images/32x32/Filter_Meo.png" Height="16" />
</ToggleButton>
I have a ViewModel with the following property:
private bool _showMeoDataOnly;
public bool ShowMeoDataOnly
{
get { return _showMeoDataOnly; }
set
{
if (_showMeoDataOnly != value)
{
_showMeoDataOnly = value;
RaisePropertyChangedEvent("ShowMeoDataOnly");
}
}
}
If I click on the ToggleButton, the value of ShowMeoDataOnly is set accordingly. However, if I set ShowMeoDataOnly to true from code behind, the ToggleButton's visual state does not change to indicate that IsChecked is true. However, if I manually set the ToggleButton's IsChecked property instead of setting ShowMeoDataOnly to true in code behind, the button's visual state changes accordingly.
Unfortunately, switching over to .NET 4/4.5 is not an option right now, so I cannot confirm if this is a .NET 3.5 problem.
Is there anything wrong with my code?
Using a .NET 3.5 project to test this and the binding seems to work for me. Do you have INotifyPropertyChanged implemented on your ViewModel and use it appropriately when ShowMeoDataOnly gets set? You didn't post all your code, so it's hard to tell what the ViewModel is doing.
Here's what I have that worked. When I run the application, the button is selected. That's because ViewModelBase implements INotifyPropertyChanged and I do base.OnPropertyChanged("ShowMeoDataOnly") when the property is set.
MainWindow.xaml
<Window x:Class="ToggleButtonIsCheckedBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ToggleButtonIsCheckedBinding"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<ToggleButton IsChecked="{Binding ShowMeoDataOnly, Mode=TwoWay}">
Show Meo Data Only
</ToggleButton>
</Grid>
</Window>
MainWindowViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ToggleButtonIsCheckedBinding
{
class MainWindowViewModel : ViewModelBase
{
bool _showMeoDataOnly;
public bool ShowMeoDataOnly {
get
{
return _showMeoDataOnly;
}
set
{
_showMeoDataOnly = value;
base.OnPropertyChanged("ShowMeoDataOnly");
}
}
public MainWindowViewModel()
{
ShowMeoDataOnly = true;
}
}
}
ViewModelBase.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.ComponentModel;
namespace ToggleButtonIsCheckedBinding
{
/// <summary>
/// Base class for all ViewModel classes in the application.
/// It provides support for property change notifications
/// and has a DisplayName property. This class is abstract.
/// </summary>
public abstract class ViewModelBase : INotifyPropertyChanged, IDisposable
{
#region Constructor
protected ViewModelBase()
{
}
#endregion // Constructor
#region DisplayName
/// <summary>
/// Returns the user-friendly name of this object.
/// Child classes can set this property to a new value,
/// or override it to determine the value on-demand.
/// </summary>
public virtual string DisplayName { get; protected set; }
#endregion // DisplayName
#region Debugging Aides
/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
if (this.ThrowOnInvalidPropertyName)
throw new Exception(msg);
else
Debug.Fail(msg);
}
}
/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail() is used
/// when an invalid property name is passed to the VerifyPropertyName method.
/// The default value is false, but subclasses used by unit tests might
/// override this property's getter to return true.
/// </summary>
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#endregion // Debugging Aides
#region INotifyPropertyChanged Members
/// <summary>
/// Raised when a property on this object has a new value.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName">The property that has a new value.</param>
protected virtual void OnPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
#endregion // INotifyPropertyChanged Members
#region IDisposable Members
/// <summary>
/// Invoked when this object is being removed from the application
/// and will be subject to garbage collection.
/// </summary>
public void Dispose()
{
this.OnDispose();
}
/// <summary>
/// Child classes can override this method to perform
/// clean-up logic, such as removing event handlers.
/// </summary>
protected virtual void OnDispose()
{
}
#if DEBUG
/// <summary>
/// Useful for ensuring that ViewModel objects are properly garbage collected.
/// </summary>
~ViewModelBase()
{
string msg = string.Format("{0} ({1}) ({2}) Finalized", this.GetType().Name, this.DisplayName, this.GetHashCode());
System.Diagnostics.Debug.WriteLine(msg);
}
#endif
#endregion // IDisposable Members
}
}
(Note: ViewModelBase is pulled from this project: http://msdn.microsoft.com/en-us/magazine/dd419663.aspx )
Verify that you DataContext is set up correctly.
DataContext = this;
... in your MainWindow.xaml.cs constructor is the easiest way, assuming the code we're looking at is in the MainWindow class.
Related
I've tried lots of solutions online but I still get this issue.
I have a combobox whose item source is a list of customized class. Each element in the source is displayed as a checkbox in the combobox. Now I have a button whose "Click" function is to uncheck all the checkboxes.
The customized class:
public class LinkObject: INotifyPropertyChanged
{
public int index { set; get; }
public string LO_Name { set; get; }
private bool _checkStatus { set; get; }
public event PropertyChangedEventHandler PropertyChanged;
public void Notify(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public bool checkStatus
{
get { return _checkStatus; }
set
{
_checkStatus = value;
Notify("IsChecked");
}
}
}
The XAML:
<ComboBox Name="cbx1" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="126,82,0,0" VerticalAlignment="Top" Width="50" Height="20" IsEditable="True" IsTextSearchEnabled="True" StaysOpenOnEdit="True" TextBoxBase.TextChanged="cbx1_TextChanged">
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox Name="cbk1" IsChecked="{Binding checkStatus, Mode=TwoWay}" CommandParameter="{Binding index}" Checked="chk_Checked" Unchecked="chk_Unchecked">
<CheckBox.Content>
<TextBlock Text="{Binding LO_Name}"/>
</CheckBox.Content>
</CheckBox>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Main function by initialization:
cbx1.ItemsSource = LinkObjectsList_cbx1;
LinkObjectsList_cbx1 is a List<LinkObject>.
The button has a name "clearAllTopView", the click function is:
private void clearAllTopViewBtn_Click(object sender, RoutedEventArgs e)
{
LinkObjectsList_cbx1.Where(l => l.checkStatus == true).ToList().ForEach(lo => lo.checkStatus = false);
}
But when I click the button, nothing happens. Could someone give me a hint or advice? Thank you.
You have an accidental error due to name confusion.
In order not to make such simple but subtle mistakes, I advise you to use a simple base class, but with a better implementation of INPC.
Here is my version of this class.
Copy it and include it in your Solution.
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Simplified
{
/// <summary>Base class with implementation of the <see cref="INotifyPropertyChanged"/> interface.</summary>
public abstract class BaseInpc : INotifyPropertyChanged
{
/// <inheritdoc cref="INotifyPropertyChanged"/>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>The protected method for raising the event <see cref = "PropertyChanged"/>.</summary>
/// <param name="propertyName">The name of the changed property.
/// If the value is not specified, the name of the method in which the call was made is used.</param>
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary> Protected method for assigning a value to a field and raising
/// an event <see cref = "PropertyChanged" />. </summary>
/// <typeparam name = "T"> The type of the field and assigned value. </typeparam>
/// <param name = "propertyField"> Field reference. </param>
/// <param name = "newValue"> The value to assign. </param>
/// <param name = "propertyName"> The name of the changed property.
/// If no value is specified, then the name of the method
/// in which the call was made is used. </param>
/// <remarks> The method is intended for use in the property setter. <br/>
/// To check for changes,
/// used the <see cref = "object.Equals (object, object)" /> method.
/// If the assigned value is not equivalent to the field value,
/// then it is assigned to the field. <br/>
/// After the assignment, an event is created <see cref = "PropertyChanged" />
/// by calling the method <see cref = "RaisePropertyChanged (string)" />
/// passing the parameter <paramref name = "propertyName" />. <br/>
/// After the event is created,
/// the <see cref = "OnPropertyChanged (string, object, object)" />
/// method is called. </remarks>
protected void Set<T>(ref T propertyField, T newValue, [CallerMemberName] string propertyName = null)
{
if (!object.Equals(propertyField, newValue))
{
T oldValue = propertyField;
propertyField = newValue;
RaisePropertyChanged(propertyName);
OnPropertyChanged(propertyName, oldValue, newValue);
}
}
/// <summary> The protected virtual method is called after the property has been assigned a value and after the event is raised <see cref = "PropertyChanged" />. </summary>
/// <param name = "propertyName"> The name of the changed property. </param>
/// <param name = "oldValue"> The old value of the property. </param>
/// <param name = "newValue"> The new value of the property. </param>
/// <remarks> Can be overridden in derived classes to respond to property value changes. <br/>
/// It is recommended to call the base method as the first operator in the overridden method. <br/>
/// If the overridden method does not call the base class, then an unwanted change in the base class logic is possible. </remarks>
protected virtual void OnPropertyChanged(string propertyName, object oldValue, object newValue) { }
}
}
With this basic implementation, your entity class will have code like this:
using Simplified;
namespace Febr20y
{
public class LinkObject : BaseInpc
{
public int Index { set; get; }
public string LO_Name { set; get; }
private bool _checkStatus;
public bool CheckStatus { get => _checkStatus; set => Set(ref _checkStatus, value); }
}
}
This is not relevant to your question, but I advise you to follow the Naming Guidelines.
Namely, property names must begin with the Upper letter.
Could you please provide me some samples where DataGrid in WPF updates live.
I am trying to write an app, which will be updating a LIST regularly and that i want to show on a DataGrid using WPF.
Following is the code snippet.
MainWindow.XAMl
Model _model = new Model();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = _model;
}
DataGrid Xaml
<DataGrid
Height="214"
HorizontalAlignment="Left"
Margin="12,135,0,0"
Name="resultDataGrid"
VerticalAlignment="Top"
Width="720"
ItemsSource="{Binding Path=Results, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
/>
Code where I am updating the Results.
public class Model : INotifyPropertyChanged
{
ObservableCollection<Result> _results = new ObservableCollection<Result>();
public void X()
{
foreach (var file in Files)
{
_results.Add(new Result() { File = file, Status = "passsed" });
}
}
public ObservableCollection<Result> Results
{
get { return _results; }
set { _results = value; OnPropertyChanged("Results"); }
}
}
When I am adding to _results collection Live update is not happening.
Use databinding (by binding DataGrid.ItemsSource to your collection of items) and remember to fire INotifyPropertyChanged.PropertyChanged when an item is updated. Or if it the collection of items and not individual items that change fire INotifyCollectionChanged.CollectionChanged. Obviously you need to databind to classes that implement these interfaces for this to work.
Try using an Observable Collection instead of a normal list. This collection implements INotifyCollectionChanged already.
Note that this will work for adding or removing items in the list, but if you change the item's properties themselves and want to update the ObservableCollection, you need to have an ObservableCollection of ViewModels, which are implementing INotifyPropertyChanged on each property.
EDIT
This may be a silly question but where are you actually calling that x method? I copied your code pretty much exactly, created my own Result class, and implemented INotifyPropertyChanged and created an implementation of the RelayCommand pattern, bound that to the command of the button and it all worked. When I click the button, the datagrid changes.
All I can think of is that you haven't actually implemented the INotifyPropertyChanged, or you aren't running the x method.
here is the code I did:
public class Model : INotifyPropertyChanged
{
ObservableCollection<Result> _results = new ObservableCollection<Result>();
private List<string> Files;
public void X()
{
foreach (var file in Files)
{
_results.Add(new Result() { File = file, Status = "passsed" });
}
_results.Add(new Result() { File = DateTime.Now.ToString(), Status = "passed" });
}
public ObservableCollection<Result> Results
{
get { return _results; }
set { _results = value; OnPropertyChanged("Results"); }
}
public ICommand XCmd { get; protected set; }
private void InitializeCommands()
{
this.XCmd = new RelayCommand((param) => { this.X(); },
(param) => { return true; });
}
public Model()
{
Files = new List<string>();
Files.Add("ONE");
Files.Add("TWO");
Files.Add("THREE");
Files.Add("FOUR");
_results.Add(new Result() { File = "ZERO", Status = "Pending" });
_results.Add(new Result() { File = DateTime.Now.ToString(), Status = "Pending" });
InitializeCommands();
}
#region INotifyPropertyChanged Members
/// <summary>
/// Raised when a property on this object has a new value.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName">The property that has a new value.</param>
protected virtual void OnPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
#endregion // INotifyPropertyChanged Members
#region Debugging Aides
/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
if (this.ThrowOnInvalidPropertyName)
throw new Exception(msg);
else
Debug.Fail(msg);
}
}
/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail() is used
/// when an invalid property name is passed to the VerifyPropertyName method.
/// The default value is false, but subclasses used by unit tests might
/// override this property's getter to return true.
/// </summary>
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#endregion // Debugging Aides
Note that the INotifyPropertyChanged Members region implements the PropertyChanged event, and the Debugging aids region just checks that the property specified in the OnPropertyChanged handler actually exists.
Here is the xaml:
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<DataGrid
HorizontalAlignment="Stretch"
Name="resultDataGrid"
VerticalAlignment="Stretch"
ItemsSource="{Binding Path=Results, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
/>
<Button Grid.Row="2" Command="{Binding XCmd}" Margin="5,5,5,5">click</Button>
</Grid>
Its not pretty I know but you can style it as you please
And here is the relaycommand implementation that I linked you earlier:
public class RelayCommand : ICommand
{
#region Private Accessor Fields
/// <summary>
/// A boolean function that contains the code to enable/disable the command and the associated UI elements.
/// </summary>
private readonly Func<object, bool> _canExecute = null;
/// <summary>
/// A generic delegate that will contain the code to execute.
/// </summary>
private readonly Action<object> _executeAction = null;
#endregion //Private Accessor Fields
#region Constructor
/// <summary>
/// Initializes a new instance of the RelayCommannd class
/// </summary>
/// <param name="executeAction">The execute action.</param>
/// <param name="canExecute">The can execute.</param>
public RelayCommand(Action<object> executeAction, Func<object, bool> canExecute)
{
this._executeAction = executeAction;
this._canExecute = canExecute;
}
#endregion
//Modified on 15 August 2011. CanExecuteChanged
#region Implementation of ICommand
/// <summary>
/// Occurs when changes occur that affect whether or not the command should execute.
/// </summary>
public event EventHandler CanExecuteChanged
{
//RequerySuggested occurs when the CommandManager detects conditions that might
//change the ability of a command to execute.
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
/// <summary>
/// Defines the method that determines whether the command can execute in its current state.
/// </summary>
/// <param name="parameter">Data used by the command. If the command does not require data to be passed,
/// this object can be null.</param>
/// <returns>true if this command can be executed; otherwise, false.</returns>
public bool CanExecute(object parameter)
{
if (this._canExecute == null)
{
return true;
}
return this._canExecute(parameter);
}
/// <summary>
/// Defines the method to be called when the command is invoked.
/// </summary>
/// <param name="parameter">Data used by the command. If the command does not require data to be passed,
/// this object can be set to null</param>
public void Execute(object parameter)
{
if (this._executeAction != null)
{
this._executeAction(parameter);
}
}
#endregion
If this doesn't work you will have to show me more of your code, because I really don't know why it's not working.
I'm trying to convert an event to a command on a devexpress wpf grid context menu item which is derived from FrameworkContentElement instead of FrameworkElement. This causes a runtime error :
{"Cannot attach type \"EventToCommand\" to type \"BarButtonItem\". Instances of type \"EventToCommand\" can only be attached to objects of type \"FrameworkElement\"."}
Is there any workaround?
<dxg:TableView.RowCellMenuCustomizations>
<dxb:BarButtonItem Name="deleteRowItem" Content="Delete" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="ItemClick">
<cmd:EventToCommand Command="{Binding FooChangeCommand}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</dxb:BarButtonItem>
<!--ItemClick="deleteRowItem_ItemClick"/>-->
</dxg:TableView.RowCellMenuCustomizations>
Unfortunately devexpress have run into problems changing the base class to FrameworkElement having intended to make that change...
The FrameworkConentElement is a class that is only available in WPF and not in Silverlight. As MVVM Light is intended to provide a common functionality for all WPF dialects (WPF 3.5, WPF 4, Silverlight 3, Silverlight 4, Sivlverlight 5, WP 7, WP 7.1) it cannot include an implementation that only works in one of the frameworks.
For a discussion about the differences between FrameworkElement and FrameworkContentElement see here.
However, you can just easily implement your own EventToCommand class supporting ContentElement (from which FrameworkContentElement inherits). The class was copied from BL0015 of the MVVM Light source code and modified:
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interactivity;
namespace GalaSoft.MvvmLight.Command
{
/// <summary>
/// This <see cref="System.Windows.Interactivity.TriggerAction" /> can be
/// used to bind any event on any FrameworkElement to an <see cref="ICommand" />.
/// Typically, this element is used in XAML to connect the attached element
/// to a command located in a ViewModel. This trigger can only be attached
/// to a FrameworkElement or a class deriving from FrameworkElement.
/// <para>To access the EventArgs of the fired event, use a RelayCommand<EventArgs>
/// and leave the CommandParameter and CommandParameterValue empty!</para>
/// </summary>
////[ClassInfo(typeof(EventToCommand),
//// VersionString = "3.0.0.0",
//// DateString = "201003041420",
//// Description = "A Trigger used to bind any event to an ICommand.",
//// UrlContacts = "http://stackoverflow.com/q/6955785/266919",
//// Email = "")]
public partial class EventToCommandWpf : TriggerAction<DependencyObject>
{
/// <summary>
/// Gets or sets a value indicating whether the EventArgs passed to the
/// event handler will be forwarded to the ICommand's Execute method
/// when the event is fired (if the bound ICommand accepts an argument
/// of type EventArgs).
/// <para>For example, use a RelayCommand<MouseEventArgs> to get
/// the arguments of a MouseMove event.</para>
/// </summary>
public bool PassEventArgsToCommand
{
get;
set;
}
/// <summary>
/// Provides a simple way to invoke this trigger programatically
/// without any EventArgs.
/// </summary>
public void Invoke()
{
Invoke(null);
}
/// <summary>
/// Executes the trigger.
/// <para>To access the EventArgs of the fired event, use a RelayCommand<EventArgs>
/// and leave the CommandParameter and CommandParameterValue empty!</para>
/// </summary>
/// <param name="parameter">The EventArgs of the fired event.</param>
protected override void Invoke(object parameter)
{
if (AssociatedElementIsDisabled())
{
return;
}
var command = GetCommand();
var commandParameter = CommandParameterValue;
if (commandParameter == null
&& PassEventArgsToCommand)
{
commandParameter = parameter;
}
if (command != null
&& command.CanExecute(commandParameter))
{
command.Execute(commandParameter);
}
}
private static void OnCommandChanged(
EventToCommandWpf element,
DependencyPropertyChangedEventArgs e)
{
if (element == null)
{
return;
}
if (e.OldValue != null)
{
((ICommand)e.OldValue).CanExecuteChanged -= element.OnCommandCanExecuteChanged;
}
var command = (ICommand)e.NewValue;
if (command != null)
{
command.CanExecuteChanged += element.OnCommandCanExecuteChanged;
}
element.EnableDisableElement();
}
private bool AssociatedElementIsDisabled()
{
var element = GetAssociatedObject();
return AssociatedObject == null
|| (element != null
&& !element.IsEnabled);
}
private void EnableDisableElement()
{
var element = GetAssociatedObject();
if (element == null)
{
return;
}
var command = this.GetCommand();
if (this.MustToggleIsEnabledValue
&& command != null)
{
SetIsEnabled(element, command.CanExecute(this.CommandParameterValue));
}
}
private void OnCommandCanExecuteChanged(object sender, EventArgs e)
{
EnableDisableElement();
}
/// <summary>
/// Identifies the <see cref="CommandParameter" /> dependency property
/// </summary>
public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register(
"CommandParameter",
typeof(object),
typeof(EventToCommandWpf),
new PropertyMetadata(
null,
(s, e) => {
var sender = s as EventToCommandWpf;
if (sender == null)
{
return;
}
if (sender.AssociatedObject == null)
{
return;
}
sender.EnableDisableElement();
}));
/// <summary>
/// Identifies the <see cref="Command" /> dependency property
/// </summary>
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
"Command",
typeof(ICommand),
typeof(EventToCommandWpf),
new PropertyMetadata(
null,
(s, e) => OnCommandChanged(s as EventToCommandWpf, e)));
/// <summary>
/// Identifies the <see cref="MustToggleIsEnabled" /> dependency property
/// </summary>
public static readonly DependencyProperty MustToggleIsEnabledProperty = DependencyProperty.Register(
"MustToggleIsEnabled",
typeof(bool),
typeof(EventToCommandWpf),
new PropertyMetadata(
false,
(s, e) => {
var sender = s as EventToCommandWpf;
if (sender == null)
{
return;
}
if (sender.AssociatedObject == null)
{
return;
}
sender.EnableDisableElement();
}));
private object _commandParameterValue;
private bool? _mustToggleValue;
/// <summary>
/// Gets or sets the ICommand that this trigger is bound to. This
/// is a DependencyProperty.
/// </summary>
public ICommand Command
{
get
{
return (ICommand)GetValue(CommandProperty);
}
set
{
SetValue(CommandProperty, value);
}
}
/// <summary>
/// Gets or sets an object that will be passed to the <see cref="Command" />
/// attached to this trigger. This is a DependencyProperty.
/// </summary>
public object CommandParameter
{
get
{
return this.GetValue(CommandParameterProperty);
}
set
{
SetValue(CommandParameterProperty, value);
}
}
/// <summary>
/// Gets or sets an object that will be passed to the <see cref="Command" />
/// attached to this trigger. This property is here for compatibility
/// with the Silverlight version. This is NOT a DependencyProperty.
/// For databinding, use the <see cref="CommandParameter" /> property.
/// </summary>
public object CommandParameterValue
{
get
{
return this._commandParameterValue ?? this.CommandParameter;
}
set
{
_commandParameterValue = value;
EnableDisableElement();
}
}
/// <summary>
/// Gets or sets a value indicating whether the attached element must be
/// disabled when the <see cref="Command" /> property's CanExecuteChanged
/// event fires. If this property is true, and the command's CanExecute
/// method returns false, the element will be disabled. If this property
/// is false, the element will not be disabled when the command's
/// CanExecute method changes. This is a DependencyProperty.
/// </summary>
public bool MustToggleIsEnabled
{
get
{
return (bool)this.GetValue(MustToggleIsEnabledProperty);
}
set
{
SetValue(MustToggleIsEnabledProperty, value);
}
}
/// <summary>
/// Gets or sets a value indicating whether the attached element must be
/// disabled when the <see cref="Command" /> property's CanExecuteChanged
/// event fires. If this property is true, and the command's CanExecute
/// method returns false, the element will be disabled. This property is here for
/// compatibility with the Silverlight version. This is NOT a DependencyProperty.
/// For databinding, use the <see cref="MustToggleIsEnabled" /> property.
/// </summary>
public bool MustToggleIsEnabledValue
{
get
{
return this._mustToggleValue == null
? this.MustToggleIsEnabled
: this._mustToggleValue.Value;
}
set
{
_mustToggleValue = value;
EnableDisableElement();
}
}
/// <summary>
/// Called when this trigger is attached to a DependencyObject.
/// </summary>
protected override void OnAttached()
{
base.OnAttached();
EnableDisableElement();
}
/// <summary>
/// This method is here for compatibility
/// with the Silverlight version.
/// </summary>
/// <returns>The object to which this trigger
/// is attached casted as a FrameworkElement.</returns>
private IInputElement GetAssociatedObject()
{
return AssociatedObject as IInputElement;
}
private void SetIsEnabled(IInputElement element, bool value)
{
if (element is UIElement)
{
((UIElement)element).IsEnabled = value;
}
else if (element is ContentElement)
{
((ContentElement)element).IsEnabled = value;
}
else
{
throw new InvalidOperationException("Cannot set IsEnabled. Element is neither ContentElemen, nor UIElement.");
}
}
/// <summary>
/// This method is here for compatibility
/// with the Silverlight version.
/// </summary>
/// <returns>The command that must be executed when
/// this trigger is invoked.</returns>
private ICommand GetCommand()
{
return Command;
}
}
}
To inlcude it into your code you have to define a xml namespace pointing to the correct dll and then use it just like the normal EventToCommand class.
NOTE: This class does not work in Silverlight!
For those trying to solve this specific issue using dev express, this will do the trick!
<dxg:TableView.RowCellMenuCustomizations>
<dxb:BarButtonItem Name="deleteRowItem" Content="Delete" Command="{Binding View.DataContext.DeleteSelectionCommand}" />
</dxg:TableView.RowCellMenuCustomizations>
I was following their example which had an event on the button, little realising there was also a command I could use. Then the challenge was working out the binding as the menu item is not on the main visual tree. However the above solves that.
I found this to work with DEV express
<dxb:BarButtonItem Content="123" Name="item1">
<dxmvvm:Interaction.Triggers>
<dxmvvm:EventToCommand EventName="ItemClick" Command="{Binding SomeCommand}" CommandParameter="{Binding ElementName=item1, Path=Content}"/>
</dxmvvm:Interaction.Triggers>
</dxb:BarButtonItem>
My application's main component is a tab control which holds N number of views and those views' datacontext is a separate ViewModel object. I have a statusbar at the bottom of the app and it contains a few textboxes. I want one of the textboxes to reflect a timestamp for the currently selected tab. The timestamp is a property of the ViewModel object that's set as the view's datacontext.
I'm a WPF newb and not really sure how to bind that property to the status bar.
Make sure your ViewModel implements INotifyPropertyChanged.
For example...
/// <summary>
/// Sample ViewModel.
/// </summary>
public class ViewModel : INotifyPropertyChanged
{
#region Public Properties
/// <summary>
/// Timestamp property
/// </summary>
public DateTime Timestamp
{
get
{
return this._Timestamp;
}
set
{
if (value != this._Timestamp)
{
this._Timestamp = value;
// NOTE: This is where the ProperyChanged event will get raised
// which will result in the UI automatically refreshing itself.
OnPropertyChanged("Timestamp");
}
}
}
#endregion
#region INotifyPropertyChanged Members
/// <summary>
/// Event
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raise the PropertyChanged event.
/// </summary>
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
#region Private Fields
private DateTime _Timestamp;
#endregion
}
Something like:
<TextBox Text="{Binding ElementName=tabControl, Path=SelectedItem.DataContext.Timestamp}" />
A little depending on if your tabcontrol's itemssource is databound or not.
I want to be able to route the double-click of a grid to a Command. I'm using Rx to simulate the double click but I can't figure out what control to attach the mouse handler to (the mouse event on the e.Row object in DataGrid.RowLoading event doesn't seem to work).
Anyone got any ideas?
Rx code for handling the Double click is as follows:
Observable.FromEvent<MouseButtonEventArgs>(e.Row, "MouseLeftButtonDown").TimeInterval().Subscribe(evt =>
{
if (evt.Interval.Milliseconds <= 300)
{
// Execute command on double click
}
});
I changed this code from handling MouseLeftButtonDown to MouseLeftButtonUp and it works now. The row must have something else handling the button down events.
Observable.FromEvent<MouseButtonEventArgs>(e.Row, "MouseLeftButtonUp").TimeInterval().Subscribe(evt =>
{
if (evt.Interval != TimeSpan.Zero && evt.Interval.TotalMilliseconds <= 300)
{
// Execute command on double click
}
});
Full source code is included below:
CommandBehaviorBase.cs
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interactivity;
/// <summary>
/// Provides the base implementation of all Behaviors that can be attached to a <see cref="FrameworkElement"/> which trigger a command.
/// </summary>
/// <typeparam name="T">The type of control this behavior can be attached to, must derive from <see cref="FrameworkElement"/>.</typeparam>
public abstract class CommandBehaviorBase<T> : Behavior<T> where T : FrameworkElement
{
#region Constants and Fields
/// <summary>The DependencyProperty backing store for CommandParameter. This enables animation, styling, binding, etc...</summary>
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register(
"CommandParameter",
typeof(object),
typeof(CommandBehaviorBase<T>),
new PropertyMetadata(null, OnCommandParameterPropertyChanged));
/// <summary>The DependencyProperty backing store for Command. This enables animation, styling, binding, etc...</summary>
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
"Command", typeof(ICommand), typeof(CommandBehaviorBase<T>), new PropertyMetadata(null));
#endregion
/// <summary>
/// Gets or sets the command to execute
/// </summary>
public ICommand Command
{
get
{
return (ICommand)this.GetValue(CommandProperty);
}
set
{
this.SetValue(CommandProperty, value);
}
}
/// <summary>
/// Gets or sets the command parameter to execute with.
/// </summary>
public object CommandParameter
{
get
{
return this.GetValue(CommandParameterProperty);
}
set
{
this.SetValue(CommandParameterProperty, value);
}
}
/// <summary>
/// Gets or sets the command binding path (Hack for SL3).
/// </summary>
/// <remarks>This is a hack to overcome the fact that we cannot
/// bind to the Command dependency property due to a limitation in Silverlight 3.0
/// This shouldn't be necessary as in Silverlight 4.0 <see cref="DependencyObject"/> supports data binding hooray!</remarks>
public string CommandPath { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this mapping is currently enabled.
/// </summary>
public bool IsEnabled { get; set; }
/// <summary>
/// Implements the logic that disables the key mapping based on whether the command can be executed.
/// </summary>
/// <summary>
/// Updates the target object's IsEnabled property based on the commands ability to execute.
/// </summary>
public virtual void UpdateEnabledState()
{
if (this.Command == null && !string.IsNullOrEmpty(this.CommandPath))
{
this.Command = this.AssociatedObject.DataContext.GetPropertyPathValue<ICommand>(this.CommandPath, null);
}
if (this.AssociatedObject == null)
{
this.Command = null;
this.CommandParameter = null;
}
else if (this.Command != null)
{
this.IsEnabled = this.Command.CanExecute(this.CommandParameter);
}
}
/// <summary>
/// Executes the command, if it's set, providing the <see cref="CommandParameter"/>
/// </summary>
protected virtual void ExecuteCommand()
{
if (this.Command != null)
{
this.Command.Execute(this.CommandParameter);
}
}
/// <summary>
/// Attaches to the target <see cref="FrameworkElement"/> and sets up the command.
/// </summary>
protected override void OnAttached()
{
base.OnAttached();
this.UpdateEnabledState();
}
/// <summary>
/// Raised when the command parameter changes, re-evaluates whether the Command can execute
/// </summary>
/// <param name="sender">The KeyCommandBehavior that command parameter changed for.</param>
/// <param name="args">The parameter is not used.</param>
private static void OnCommandParameterPropertyChanged(
DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
((CommandBehaviorBase<T>)sender).UpdateEnabledState();
}
}
DoubleClickCommandBehavior.cs
using System;
using System.Linq;
using System.Windows;
using System.Windows.Input;
/// <summary>
/// Provides behavior for any clickable control and will execute a command when the control is double clicked.
/// Does not disable the control if the command cannot be executed.
/// </summary>
public class DoubleClickCommandBehavior : CommandBehaviorBase<FrameworkElement>
{
#region Constants and Fields
/// <summary>
/// Stores the observable that subscribes to click events.
/// </summary>
private IDisposable observable;
#endregion
#region Constructors and Destructors
/// <summary>
/// Initializes a new instance of the DoubleClickCommandBehavior class.
/// </summary>
public DoubleClickCommandBehavior()
{
// Default double-click interval is 220 milliseconds.
this.Interval = 220;
}
#endregion
/// <summary>
/// Gets or sets the double click interval in milliseconds.
/// </summary>
public int Interval
{
get;
set;
}
/// <summary>
/// Subscribes to the MouseLeftButtonUp of the data grid and times the intervals between the events,
/// if the time between clicks is less than the configured interval the command is executed.
/// </summary>
/// <remarks>Originally attached to MouseLeftButtonDown but the events were not firing.</remarks>
protected override void OnAttached()
{
base.OnAttached();
this.observable =
Observable.FromEvent<MouseButtonEventArgs>(this.AssociatedObject, "MouseLeftButtonUp").TimeInterval().
Subscribe(
evt =>
{
if (evt.Interval != TimeSpan.Zero && evt.Interval.TotalMilliseconds <= this.Interval)
{
this.UpdateEnabledState();
this.ExecuteCommand();
}
});
}
/// <summary>
/// Disposes of the observable
/// </summary>
protected override void OnDetaching()
{
if (this.observable != null)
{
this.observable.Dispose();
this.observable = null;
}
base.OnDetaching();
}
}
I'm having similar problems (though not using Rx to handle the double click, instead using a generic DoubleClickTrigger). My specific problem is more related to the fact that I'm not sure how or where to hook up my trigger.
I've tried something like the following:
<data:DataGrid.Resources>
<ControlTemplate x:Key="rowTemplate" TargetType="data:DataGridRow">
<data:DataGridRow>
<fxui:Interaction.Triggers>
<fxui:DoubleClickTrigger>
<Interactivity:InvokeCommandAction Command="{Binding Source={StaticResource selectCommand}}" CommandParameter="{Binding}"/>
</fxui:DoubleClickTrigger>
</fxui:Interaction.Triggers>
</data:DataGridRow>
</ControlTemplate>
</data:DataGrid.Resources>
<data:DataGrid.RowStyle>
<Style TargetType="data:DataGridRow">
<Setter Property="Template" Value="{StaticResource rowTemplate}"/>
</Style>
</data:DataGrid.RowStyle>
With no luck.