I created my own DataGrid which implements a RowClick Event.
However while trying to bind a Command to it, it'll throw the exception:
{"The event \"RowClick\" on type \"ExtendedDataGrid\" has an incompatible signature. Make sure the event is public and satisfies the EventHandler delegate."}
Since I am new to MVVM my already hurts from all the Input I got in the last couple days about MVVM..Can someone hint me the (mostly) obvious error?
Thanks in advance
Here's my (testproject) code:
public class ExtendedDataGrid : DataGrid
{
public event EventHandler<DataGridRow> RowClick;
public ExtendedDataGrid()
{
this.DefaultStyleKey = typeof(DataGrid);
}
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
var row = (DataGridRow)element;
row.PreviewKeyDown += RowOnKeyDown;
row.MouseLeftButtonUp += RowOnMouseLeftButtonUp;
base.PrepareContainerForItemOverride(element, item);
}
protected override void ClearContainerForItemOverride(DependencyObject element, object item)
{
var row = (DataGridRow)element;
row.KeyUp -= RowOnKeyDown;
row.MouseLeftButtonUp -= RowOnMouseLeftButtonUp;
base.ClearContainerForItemOverride(element, item);
}
private void RowOnMouseLeftButtonUp(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
mouseButtonEventArgs.Handled = true;
this.OnRowClick((DataGridRow)sender);
}
private void RowOnKeyDown(object sender, KeyEventArgs keyEventArgs)
{
if (keyEventArgs.Key != Key.Enter)
return;
keyEventArgs.Handled = true;
this.OnRowClick((DataGridRow)sender);
}
protected virtual void OnRowClick(DataGridRow clickedRow)
{
if (null == this.RowClick)
return;
this.RowClick(this, clickedRow);
}
}
Window.xaml
<controls1:ExtendedDataGrid x:Name="extGrid">
<i:Interaction.Triggers>
<i:EventTrigger EventName="RowClick" SourceObject="{Binding ElementName=extGrid}">
<i:InvokeCommandAction Command="{Binding MyCommand}" CommandParameter="{Binding SelectedItem,ElementName=extGrid}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<controls1:ExtendedDataGrid.Items>
<TextBlock Text="Text" />
</controls1:ExtendedDataGrid.Items>
</controls1:ExtendedDataGrid>
window.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
this._selectCommand = new DelegateCommand<DataGridRow>(x =>
{
});
//following works fine..
this.extGrid.RowClick += (s, e) =>
{
};
}
private DelegateCommand<DataGridRow> _selectCommand;
public ICommand MyCommand
{
get
{
return this._selectCommand;
}
}
}
DelegateCommand Implementation:
public class DelegateCommand<T> : DelegateCommand
{
public DelegateCommand(Action<T> executeHandler)
: this(null, executeHandler)
{ }
public DelegateCommand(Func<T, bool> canExecuteHandler, Action<T> executeHandler)
: base(o => null == canExecuteHandler || canExecuteHandler((T)o), o => executeHandler((T)o))
{
if (null == executeHandler)
throw new ArgumentNullException("executeHandler");
}
}
/// <summary>
/// Stellt ein standard DelegateCommand dar.
/// </summary>
public class DelegateCommand : ICommand
{
#region Events
public event EventHandler CanExecuteChanged;
#endregion
#region Variablen
private readonly Action<object> _executeHandler;
private readonly Func<object, bool> _canExecuteHandler;
private bool _isExecuting = false;
#endregion
#region Eigenschaften
public bool IsSingleExecution { get; set; }
#endregion
#region Konstruktor
public DelegateCommand(Action<object> executeHandler)
: this(null, executeHandler)
{ }
public DelegateCommand(Func<object, bool> canExecuteHandler, Action<object> executeHandler)
{
if (null == executeHandler)
throw new ArgumentNullException("executeHandler");
this._executeHandler = executeHandler;
this._canExecuteHandler = canExecuteHandler;
}
#endregion
#region Public Methoden
public virtual bool CanExecute(object parameter)
{
return (!this.IsSingleExecution || (this.IsSingleExecution && !this._isExecuting)) && (null == this._canExecuteHandler || this._canExecuteHandler(parameter));
}
public virtual void Execute(object parameter)
{
if (this.CanExecute(parameter))
{
this._isExecuting = true;
this.RaiseCanExecuteChanged();
try
{
this._executeHandler(parameter);
}
finally
{
this._isExecuting = false;
this.RaiseCanExecuteChanged();
}
}
}
public void RaiseCanExecuteChanged()
{
if (null != CanExecuteChanged)
CanExecuteChanged(this, EventArgs.Empty);
}
#endregion
The problem is coming from this line:
<i:EventTrigger EventName="RowClick" SourceObject="{Binding ElementName=extGrid}">
The EventTrigger class is expecting a routed event which uses the RoutedEventHandler delegate not the EventHandler delegate.
These are the changes you have to make in your code to make it work:
In ExtendedDataGrid:
public class ExtendedDataGrid : DataGrid
{
public static readonly RoutedEvent RowClickEvent = EventManager.RegisterRoutedEvent("RowClick",
RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ExtendedDataGrid));
public event RoutedEventHandler RowClick
{
add { AddHandler(RowClickEvent, value); }
remove { RemoveHandler(RowClickEvent, value); }
}
public ExtendedDataGrid()
{
this.DefaultStyleKey = typeof(DataGrid);
}
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
var row = (DataGridRow)element;
row.PreviewKeyDown += RowOnKeyDown;
row.MouseLeftButtonUp += RowOnMouseLeftButtonUp;
base.PrepareContainerForItemOverride(element, item);
}
protected override void ClearContainerForItemOverride(DependencyObject element, object item)
{
var row = (DataGridRow)element;
row.KeyUp -= RowOnKeyDown;
row.MouseLeftButtonUp -= RowOnMouseLeftButtonUp;
base.ClearContainerForItemOverride(element, item);
}
private void RowOnMouseLeftButtonUp(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
mouseButtonEventArgs.Handled = true;
this.OnRowClick((DataGridRow)sender);
}
private void RowOnKeyDown(object sender, KeyEventArgs keyEventArgs)
{
if (keyEventArgs.Key != Key.Enter)
return;
keyEventArgs.Handled = true;
this.OnRowClick((DataGridRow)sender);
}
protected virtual void OnRowClick(DataGridRow clickedRow)
{
var args = new RowClickRoutedEventArgs(clickedRow);
args.RoutedEvent = RowClickEvent;
RaiseEvent(args);
}
}
Here I removed the previous RowClick event and changed the OnRowClick method.
Add a new class called RowClickRoutedEventArgs:
public class RowClickRoutedEventArgs : RoutedEventArgs
{
public RowClickRoutedEventArgs(DataGridRow dataGridRow)
{
Row = dataGridRow;
}
public DataGridRow Row { get; set; }
}
Related
I think this is a stupid problem but...
I have a Window class with a Treeview bind to a Observablecollection<T> foo; T is my class (in another file .cs).
My Observablecollection foo have OnPropertyChanged for add/delete element and works ok.
The problem is for my class T: it has 5 properties and one have OnPropertyChanged: when I programmatically change an element of my class T I have OnPropertyChanged in class T (obviously) but... how can I know it from Window class for do something?
You want to be able to listen to propertychange events on the items inside the observable collection?
To do this, I created an extension to ObsercableCollection that bubbles up these events to a collection level, so I can subscribe to collection.CollectionChanged (adding and removing items) and collection.ItemInCollectionChange (property of an item inside the collection has changed):
//public class ItemInCollectionChangeEventArgs
public class ItemInCollectionChangeEventArgs : EventArgs
{
public object Item { get; internal set; }
public PropertyChangedEventArgs PropertyChangedEventArgs { get; internal set; }
}
public delegate void ItemInCollectionChangeEventHandler(Object sender, ItemInCollectionChangeEventArgs e);
//TODO Make this throw a separate event for refreshing
public class ItemsChangeObservableCollection<T> :
ObservableCollection<T> where T : INotifyPropertyChanged
{
public event ItemInCollectionChangeEventHandler ItemInCollectionChangeEvent;
public ItemsChangeObservableCollection() : base()
{
}
public ItemsChangeObservableCollection(IEnumerable<T> collection) : base()
{
foreach (T item in collection)
{
Add(item);
}
}
protected virtual void OnItemInCollectionChange(ItemInCollectionChangeEventArgs e)
{
ItemInCollectionChangeEventHandler handler = ItemInCollectionChangeEvent;
if (handler != null)
{
handler(this, e);
}
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
RegisterPropertyChanged(e.NewItems);
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
UnRegisterPropertyChanged(e.OldItems);
}
else if (e.Action == NotifyCollectionChangedAction.Replace)
{
UnRegisterPropertyChanged(e.OldItems);
RegisterPropertyChanged(e.NewItems);
}
base.OnCollectionChanged(e);
}
protected override void ClearItems()
{
UnRegisterPropertyChanged(this);
base.ClearItems();
}
private void RegisterPropertyChanged(IList items)
{
foreach (INotifyPropertyChanged item in items)
{
if (item != null)
{
item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
}
}
}
private void UnRegisterPropertyChanged(IList items)
{
foreach (INotifyPropertyChanged item in items)
{
if (item != null)
{
item.PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
}
}
}
private void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
OnItemInCollectionChange(new ItemInCollectionChangeEventArgs { Item = sender, PropertyChangedEventArgs = e });
//base.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
I need to know, if the SelectedItems got filled when Ctrl or Shift was pressed or not. Is there an easy way (without creating a new controltemplate) to get this info? I prefer solutions without code behind.
Best regards
Yannik
You can wire up selection changed event, and check if Modifier Keys are pressed for Selection.
void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var isCtrlorShiftDown = (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl) || Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift));
if (isCtrlorShiftDown)
{
// Write your Logic Here;
}
}
I found a solution with minimal code behind.
The main concept is, that I attach to KeyDown and KeyUp events in the MainWindow and set a property "CurrentKeyboardKeyPressed" on the MainViewModel, which propagates the pressed key info to the child view models, which in turn fire a custom event with a special Selection class that has the currently pressed key info.
The posted source code is heavyli shortened and does not run at all. If somebody is interested in the working solution, just ask me and I will email it.
MainWindow.xaml.cs
public partial class MainWindow : Window
{
private readonly MainWindowViewModel _mainWindowViewModel;
public MainWindow()
{
_mainWindowViewModel = new MainWindowViewModel();
InitializeComponent();
DataContext = _mainWindowViewModel;
}
private void MainWindow_OnKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.LeftCtrl || e.Key == Key.RightCtrl)
{
_mainWindowViewModel.CurrentKeyboardKeyPressed = PressedKeyboardKey.Ctrl;
return;
}
if (e.Key == Key.LeftShift || e.Key == Key.RightShift)
{
_mainWindowViewModel.CurrentKeyboardKeyPressed = PressedKeyboardKey.Shift;
}
}
private void MainWindow_OnKeyUp(object sender, KeyEventArgs e)
{
_mainWindowViewModel.CurrentKeyboardKeyPressed = PressedKeyboardKey.None;
}
}
MainWindowViewModel.cs
public class MainWindowViewModel : ViewModelBase
{
// child view models
private readonly ObservableCollection<TTSViewModel> _ttsViewModels;
private PressedKeyboardKey _currentKeyboardKeyPressed;
public EventHandler<KeyboardKeyPressedEventArgs> CurrentKeyboardKeyPressedChanged;
public MainWindowViewModel()
{
_ttsViewModels = new ObservableCollection<TTSViewModel>();
}
public PressedKeyboardKey CurrentKeyboardKeyPressed
{
get { return _currentKeyboardKeyPressed; }
set
{
if (_currentKeyboardKeyPressed != value)
{
_currentKeyboardKeyPressed = value;
OnCurrentKeyboardKeyPressedChanged(_currentKeyboardKeyPressed);
}
}
}
// create child view models
public void PopulateTTSList(int itemsToCreated)
{
for (int i = 0; i < itemsToCreated; i++)
{
var tts = new TTSViewModel("TTS " + i);
tts.FractionSelectionChanged += OnTTSFractionSelectionChanged;
CurrentKeyboardKeyPressedChanged += tts.CurrentKeyboardKeyPressedChanged;
_ttsViewModels.Add(tts);
}
}
private void OnCurrentKeyboardKeyPressedChanged(PressedKeyboardKey currentKeyboardKeyPressed)
{
var handler = CurrentKeyboardKeyPressedChanged;
if (handler != null)
{
handler(this, new KeyboardKeyPressedEventArgs(currentKeyboardKeyPressed));
}
}
// selection changed handler for each child view model
private void OnTTSFractionSelectionChanged(object sender, ItemSelectionChangedEventArgs fractionSelectionChangedEventArgs)
{
var sendingTTS = sender as TTSViewModel;
if (fractionSelectionChangedEventArgs.Selection.PressedKeyOnSelection == PressedKeyboardKey.None)
{
// single selection action goes here
// ....
}
else
{
// multi selection action goes here
// ....
}
}
}
TTSViewModel.cs (child view model)
public class TTSViewModel : ViewModelBase
{
private readonly SmartObservableCollection<FractionViewModel> _currentlySelectedfractions;
public EventHandler<ItemSelectionChangedEventArgs> FractionSelectionChanged;
private PressedKeyboardKey _currentKeyboardKeyPressed;
public TTSViewModel()
{
_currentlySelectedfractions = new SmartObservableCollection<FractionViewModel>();
_currentlySelectedfractions.CollectionChanged += CurrentlySelectedfractionsOnCollectionChanged;
}
public void CurrentKeyboardKeyPressedChanged(object sender, KeyboardKeyPressedEventArgs currentKeyboardKeyPressedEventArgs)
{
_currentKeyboardKeyPressed = currentKeyboardKeyPressedEventArgs.CurrentKeyboardKeyPressed;
}
private void CurrentlySelectedfractionsOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
if (FractionSelectionChanged != null)
{
if (notifyCollectionChangedEventArgs.Action == NotifyCollectionChangedAction.Replace && notifyCollectionChangedEventArgs.NewItems != null)
{
var removed = _oldSelectedfractions.Except(_currentlySelectedfractions);
var added = _currentlySelectedfractions.Except(_oldSelectedfractions);
var selection = new Selection<FractionViewModel>(added, removed, _currentKeyboardKeyPressed);
_oldSelectedfractions.Clear();
foreach (var currentlySelectedfraction in _currentlySelectedfractions)
{
_oldSelectedfractions.Add(currentlySelectedfraction);
}
var selectionChangedArgs = new ItemSelectionChangedEventArgs(selection);
FractionSelectionChanged(this, selectionChangedArgs);
}
}
}
}
Selection.cs
public sealed class Selection<T>
{
private readonly List<T> _added;
private readonly List<T> _removed;
private readonly PressedKeyboardKey _currentKeyboardKeyPressed;
public Selection()
{
_added = new List<T>();
_removed = new List<T>();
}
/// <summary>
/// Initializes a new instance of the <see cref="Selection{T}" /> class.
/// </summary>
/// <param name="addedItems">[NotNull]</param>
/// <param name="removedItems">[NotNull]</param>
/// <param name="currentKeyboardKeyPressed">The current keyboard key pressed.</param>
public Selection(IEnumerable<T> addedItems, IEnumerable<T> removedItems, PressedKeyboardKey currentKeyboardKeyPressed)
: this()
{
_added.AddRange(addedItems);
_removed.AddRange(removedItems);
_currentKeyboardKeyPressed = currentKeyboardKeyPressed;
}
public IReadOnlyList<T> Added
{
get { return _added; }
}
public IReadOnlyList<T> Removed
{
get { return _removed; }
}
public PressedKeyboardKey PressedKeyOnSelection
{
get { return _currentKeyboardKeyPressed; }
}
}
KeyboardKeyPressedEventArgs.cs
public sealed class KeyboardKeyPressedEventArgs : EventArgs
{
public KeyboardKeyPressedEventArgs(PressedKeyboardKey currentKeyboardKeyPressed)
{
CurrentKeyboardKeyPressed = currentKeyboardKeyPressed;
}
public PressedKeyboardKey CurrentKeyboardKeyPressed { get; private set; }
}
ItemSelectionChangedEventArgs.cs
public sealed class ItemSelectionChangedEventArgs : EventArgs
{
public ItemSelectionChangedEventArgs(Selection<FractionViewModel> newSelection)
{
Selection = newSelection;
}
public Selection<FractionViewModel> Selection { get; private set; }
}
PressedKeyboardKey.cs
public enum PressedKeyboardKey
{
None,
Ctrl,
Shift
}
I want to create a custom class of a CommandBinding, where a RelayCommand of my ViewModel is executed when the RoutedCommand is executed.
Currently there is only the possibility to create CommandBindings which have Executed methods in the codebehind class.
Example:
<CommandBinding Command="ApplicationCommands.Close" Executed="CloseCommandHandler"
CanExecute="CanExecuteHandler"/>
This needs the CloseCommandHandler methode in the code behind.
I would want to write the following.
<CommandBinding RoutedCommand="ApplicationCommands.Close" Command={Binding Path=CloseCommand}/>
The only problem is that i can't find the bubble down and up event of the RoutedCommands.
There is no
OnPreviewCommand(object command, object commandParammeter)
OnCommand(object command, object commandParammeter)
Where is the RoutedCommand bubble down and up handled?
I came up with a solutions of my own. Its not the most beautiful, but its working.
I derived from the ContentControl. The new Control has a RoutedCommandBindings property, which contains a list of "sort of" CommandBindings between RoutedCommands and RelayCommands.
It can be used like this.
<CSControls:RoutedCommandBinder>
<CSControls:RoutedCommandBinder.RoutedCommandBindings>
<CSControls:RoutedCommandBindingCollection>
<CSControls:RoutedCommandBinding RoutedCommand="{x:Static ApplicationCommands.New}" Command="{Binding Path=AddInstanceCommand}"/>
</CSControls:RoutedCommandBindingCollection>
</CSControls:RoutedCommandBinder.RoutedCommandBindings>
<CSControls:RoutedCommandBinder.Content>
<!-- Every RoutedCommand of type ApplicationCommands.New will execute the binded RelayCommand "AddInstanceCommand-->
</CSControls:RoutedCommandBinder.Content>
</CSControls:RoutedCommandBinder>
Here is the CustomControl code.
public class RoutedCommandBinder : ContentControl
{
public RoutedCommandBinder()
{
this.DataContextChanged += RoutedCommandBinder_DataContextChanged;
}
public static readonly DependencyProperty RoutedCommandBindingsProperty = DependencyProperty.Register("RoutedCommandBindings", typeof(RoutedCommandBindingCollection), typeof(RoutedCommandBinder), new PropertyMetadata(new RoutedCommandBindingCollection(), OnRoutedCommandBindingsChanged));
public RoutedCommandBindingCollection RoutedCommandBindings
{
get { return (RoutedCommandBindingCollection)this.GetValue(RoutedCommandBindingsProperty); }
set { SetValue(RoutedCommandBindingsProperty, value); }
}
private static void OnRoutedCommandBindingsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
RoutedCommandBinder binder = (RoutedCommandBinder)d;
binder.CommandBindings.Clear();
SetDataContextForCommandBindings(binder);
if (e.NewValue != null)
{
RoutedCommandBindingCollection bindings = (RoutedCommandBindingCollection)e.NewValue;
foreach (RoutedCommandBinding binding in bindings)
{
binder.CommandBindings.Add(new CommandBinding(binding.RoutedCommand, binder.RoutedCommandExecuted, binder.CanExecuteRoutedEventHandler));
}
}
}
private void RoutedCommandBinder_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
SetDataContextForCommandBindings(this);
}
private static void SetDataContextForCommandBindings(RoutedCommandBinder binder)
{
if (binder.DataContext != null && binder.RoutedCommandBindings != null)
{
foreach (RoutedCommandBinding binding in binder.RoutedCommandBindings)
{
binding.DataContext = binder.DataContext;
}
}
}
private void RoutedCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{
RoutedCommandBinding binding = this.RoutedCommandBindings.FirstOrDefault(t => t.RoutedCommand == e.Command);
if (binding != null)
{
binding.Command.Execute(e.Parameter);
}
}
private void CanExecuteRoutedEventHandler(object sender, CanExecuteRoutedEventArgs e)
{
RoutedCommandBinding binding = this.RoutedCommandBindings.FirstOrDefault(t => t.RoutedCommand == e.Command);
if (binding != null)
{
e.CanExecute = binding.Command.CanExecute(e.Parameter);
}
}
}
public class RoutedCommandBindingCollection : List<RoutedCommandBinding>
{
}
public class RoutedCommandBinding : FrameworkElement
{
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(RoutedCommandBinding), new PropertyMetadata(null));
public ICommand Command
{
get { return (ICommand)this.GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty RoutedCommandProperty = DependencyProperty.Register("RoutedCommand", typeof(RoutedCommand), typeof(RoutedCommandBinding), new PropertyMetadata(null));
public RoutedCommand RoutedCommand
{
get { return (RoutedCommand)this.GetValue(RoutedCommandProperty); }
set { SetValue(RoutedCommandProperty, value); }
}
}
I am using Phone7.Fx R1
The following works. The system does not react when a user presses the button. This means, than there is no reaction if Stop Game is pressed without a game has been started and vice versa.
However the button looks active. I am aware that I can bind the IsEnabled to a different property, but I would like it to bind to NewGameCanExecute and StopGameCanExecute. Is this possible?
Some XAML code:
<Preview:BindableApplicationBarIconButton Command="{Binding NewGame}" IconUri="/images/icons/appbar.add.rest.png" Text="New game" />
<Preview:BindableApplicationBarIconButton Command="{Binding StopGame}" IconUri="/images/icons/appbar.stop.rest.png" Text="Stop game" />
And the relay commands:
public RelayCommand NewGame { get; private set; }
public RelayCommand StopGame { get; private set; }
//Constructor
NewGame = new RelayCommand(NewGameExecute, NewGameCanExecute);
StopGame = new RelayCommand(StopGameExecute, StopGameCanExecute);
void NewGameExecute()
{
_gameStarted = true;
_gameControlModel.StartNewGame();
StopGame.RaiseCanExecuteChanged();
}
bool NewGameCanExecute()
{
return !_gameStarted;
}
void StopGameExecute()
{
_gameControlModel.StopGame();
_gameStarted = false;
NewGame.RaiseCanExecuteChanged();
}
bool StopGameCanExecute()
{
return _gameStarted;
}
Couple of questions and answers:
Q: Have you tried to set a breakpoint in the CanExecute functions to see if it gets called?
A: Yes. It does get called, but the icon is not grayed out, eventhough false is returned.
The Execute method is not executed, if the CanExecute method returns false. But I want the icon to be grayed out like a normal button.
SOLUTION
I spend some time and came up with a solution, which can be found here:
http://pastebin.com/MM75xACj
This is obviously a bug in whatever BindableApplicationBarIconButton implementation you're using.
Ask the author of it for help, or debug your 3rd party software yourself.
SOLUTION
I spend some time and came up with a solution and edited the applicationbariconbutton class.
namespace Phone7.Fx.Controls
{
public class BindableApplicationBarIconButton : FrameworkElement, IApplicationBarIconButton
{
public int Index { get; set; }
public static DependencyProperty CommandProperty = DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(BindableApplicationBarIconButton), new PropertyMetadata(null, OnCommandPropertyChanged));
private static void OnCommandPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != e.OldValue)
{
((BindableApplicationBarIconButton)d).Command = (ICommand)e.NewValue;
}
}
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set {
Command.CanExecuteChanged -= ValueOnCanExecuteChanged;
SetValue(CommandProperty, value);
Command.CanExecuteChanged += ValueOnCanExecuteChanged;
}
}
private void ValueOnCanExecuteChanged(object sender, EventArgs eventArgs)
{
ICommand commandSender = sender as ICommand;
if(commandSender != null)
{IsEnabled = commandSender.CanExecute(null);}
}
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.RegisterAttached("CommandParameter", typeof(object), typeof(BindableApplicationBarIconButton), new PropertyMetadata(null, OnCommandParameterPropertyChanged));
private static void OnCommandParameterPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var invokeCommand = d as BindableApplicationBarIconButton;
if (invokeCommand != null)
{
invokeCommand.SetValue(CommandParameterProperty, e.NewValue);
}
}
public object CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set
{
SetValue(CommandParameterProperty, value);
}
}
public static readonly DependencyProperty CommandParameterValueProperty =
DependencyProperty.RegisterAttached("CommandParameterValue", typeof(object), typeof(BindableApplicationBarIconButton), null);
public object CommandParameterValue
{
get
{
var returnValue = GetValue(CommandParameterValueProperty);
return returnValue;
}
set { SetValue(CommandParameterValueProperty, value); }
}
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(BindableApplicationBarIconButton), new PropertyMetadata(true, OnEnabledChanged));
private static void OnEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != e.OldValue)
{
((BindableApplicationBarIconButton)d).Button.IsEnabled = (bool)e.NewValue;
}
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.RegisterAttached("Text", typeof(string), typeof(BindableApplicationBarIconButton), new PropertyMetadata(OnTextChanged));
public new static readonly DependencyProperty VisibilityProperty =
DependencyProperty.RegisterAttached("Visibility", typeof(Visibility), typeof(BindableApplicationBarIconButton), new PropertyMetadata(OnVisibilityChanged));
private static void OnVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != e.OldValue)
{
var button = ((BindableApplicationBarIconButton)d);
BindableApplicationBar bar = button.Parent as BindableApplicationBar;
bar.Invalidate();
}
}
public new Visibility Visibility
{
get { return (Visibility)GetValue(VisibilityProperty); }
set { SetValue(VisibilityProperty, value); }
}
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != e.OldValue)
{
((BindableApplicationBarIconButton)d).Button.Text = e.NewValue.ToString();
}
}
public ApplicationBarIconButton Button { get; set; }
public BindableApplicationBarIconButton()
{
Button = new ApplicationBarIconButton();
Button.Text = "Text";
Button.Click += ApplicationBarIconButtonClick;
}
void ApplicationBarIconButtonClick(object sender, EventArgs e)
{
if (Command != null && CommandParameter != null)
Command.Execute(CommandParameter);
else if (Command != null)
Command.Execute(CommandParameterValue);
if (Click != null)
Click(this, e);
}
public bool IsEnabled
{
get { return (bool)GetValue(IsEnabledProperty); }
set { SetValue(IsEnabledProperty, value); }
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public event EventHandler Click;
public Uri IconUri
{
get { return Button.IconUri; }
set { Button.IconUri = value; }
}
}
I've started using databinding with MVVM pattern in Silverlight. There's one problem for me.
I have a listbox, when I select an item there one property in the View-Model have to change it's value. I implemented DelegateCommand and made custom CommandListBox class as follows
public class DelegateCommand<T> : ICommand
{
private readonly Action<T> _execute;
private readonly Predicate<T> _canExecute;
public DelegateCommand(Action<T> execute)
: this(execute, x => true)
{
}
public DelegateCommand(Action<T> execute, Predicate<T> canExecute)
{
if (canExecute == null) throw new ArgumentNullException("canExecute");
if (execute == null) throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
public void Execute(object parameter)
{
_execute((T)parameter);
}
public bool CanExecute(object parameter)
{
return _canExecute((T)parameter);
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
CanExecuteChanged.DynamicInvoke(this);//.Raise(this);
}
}
public class DelegateCommand : DelegateCommand<object>
{
public DelegateCommand(Action execute)
: base(execute != null ? x => execute() : (Action<object>)null)
{
}
public DelegateCommand(Action execute, Func<bool> canExecute)
: base(execute != null ? x => execute() : (Action<object>)null,
canExecute != null ? x => canExecute() : (Predicate<object>)null)
{
}
}
public class CommandListBox : ListBox
{
public CommandListBox()
{
SelectionChanged += (sender, e) =>
{
if (Command != null && Command.CanExecute(CommandParameter))
Command.Execute(CommandParameter);
};
}
public static DependencyProperty CommandProperty =
DependencyProperty.Register("Command",
typeof(ICommand), typeof(CommandListBox),
new PropertyMetadata(null, CommandChanged));
private static void CommandChanged(DependencyObject source, DependencyPropertyChangedEventArgs args)
{
var treeList = source as CommandListBox;
if (treeList == null) return;
treeList.RegisterCommand(args.OldValue as ICommand, args.NewValue as ICommand);
}
private void RegisterCommand(ICommand oldCommand, ICommand newCommand)
{
if (oldCommand != null)
oldCommand.CanExecuteChanged -= HandleCanExecuteChanged;
if (newCommand != null)
newCommand.CanExecuteChanged += HandleCanExecuteChanged;
HandleCanExecuteChanged(newCommand, EventArgs.Empty);
}
private void HandleCanExecuteChanged(object sender, EventArgs args)
{
if (Command != null)
IsEnabled = Command.CanExecute(CommandParameter);
}
public ICommand Command
{
get { return GetValue(CommandProperty) as ICommand; }
set { SetValue(CommandProperty, value); }
}
public static DependencyProperty CommandParameterProperty =
DependencyProperty.Register("CommandParameter",
typeof(object), typeof(CommandListBox),
new PropertyMetadata(null));
public object CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
}
I'm catching the event, when the selected item in listbox is changed but I don't know how to take it's value.
Could you help me please?
Your CommandListBox derives from a ListBox, it exposes a SelectionChanged event, you're attaching to that event already.
SelectionChanged += (sender, e) =>
{
if (Command != null && Command.CanExecute(CommandParameter))
Command.Execute(CommandParameter);
};
In order to obtain the currently selected item you can do something like this :
SelectionChanged += (sender, e) =>
{
CommandListBox source = sender as CommandListBox; // This is the sender
if(source != null) // just to be sure
{
var value = source.SelectedItem;
}
if (Command != null && Command.CanExecute(CommandParameter))
Command.Execute(CommandParameter);
};
Does this help ?
You can bind your listbox's SelectedItem property to an appropriate property in your ViewModel.