Say I have a Logger class, a LoggerViewModel class and a MainWindow with a TextBox. The Logger class is a thread-safe singleton, so I have only an instance of it in the application domain.
public sealed class Logger : INotifyPropertyChanged
{
private static readonly Logger _Instance = new Logger();
private static readonly object _SyncLock = new object();
private static List<LogEntry> _Data = new List<LogEntry>();
/// <summary>
///
/// </summary>
private Logger() { ; }
/// <summary>
///
/// </summary>
public static Logger Instance
{
get { return _Instance; }
}
/// <summary>
///
/// </summary>
/// <param name="entry"></param>
public void Write(LogEntry entry)
{
lock (_SyncLock)
{
_Data.Add(entry);
}
this.RaiseNotifyPropertyChanged("Entries");
}
/// <summary>
///
/// </summary>
/// <param name="component"></param>
/// <param name="message"></param>
public void Write(string component, string message)
{
LogEntry entry = LogEntry.Create(component, message);
Write(entry);
}
/// <summary>
///
/// </summary>
public IList<LogEntry> Entries
{
get
{
lock (_SyncLock)
{
return new ReadOnlyCollection<LogEntry>(_Data);
}
}
}
/// <summary>
///
/// </summary>
/// <param name="property"></param>
private void RaiseNotifyPropertyChanged(string property)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(property));
}
}
/// <summary>
///
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
}
The Logger unique instance is updated by more thread while the application is running, so I would update the TextBox on the MainWindow whenever the model (that is the Logger singleton class) changes.
How to connect the Model and the ViewModel between them? I emphasize that the Model is changed by only a few application thread, so it is read-only from the point of view of UI.
I provided the LoggerText property within the LoggerViewModel class, since I thought the following working mechanism.
1. When the Model (the Logger instance) changes, it notifies the ViewModel.
2. The ViewModel receives the notify by the Model and create a new string containing all the messages from the logger.
3. The ViewModel notifies the View.
public class LoggerViewModel : INotifyPropertyChanged
{
Logger _LoggerModel;
/// <summary>
///
/// </summary>
public LoggerViewModel()
{
_LoggerModel = Logger.Instance;
}
/// <summary>
///
/// </summary>
public string LoggerText
{
get
{
string text = "";
List<LogEntry> entries = new List<LogEntry>(_LoggerModel.Entries);
foreach (LogEntry entry in entries)
{
text += entry.ToString();
text += "\n";
}
return text;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
// take a copy to prevent thread issues
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
How the ViewModel can intercept the notifications sent by the Model?
First of all I do not like that you are using a singleton. When using the singleton pattern you are making it hard on yourself when testing or reusing your view controllers. I would instead inject the Logger dependency into your LoggerViewModel class.
Aside from that one way to solve your problem is to register a handler for the PropertyChanged event on your Logger and build the text when the event fires for the Entries property.
In LoggerViewModel you would then add a property handler and update the LoggerText property as needed.
public LoggerViewModel(Logger loggerModel /* Dependency injection*/)
{
_LoggerModel = loggerModel;
_LoggerModel.PropertyChanged += this.LoggerModel_PropertyChanged;
}
private void LoggerModel_PropertyChanged(object sender, PropertyChangedEventArgs args)
{
if (args.PropertyName == "Entries")
{
StringBuilder text = new StringBuilder(); // Use StringBuilder for performance
List<LogEntry> entries = new List<LogEntry>(_LoggerModel.Entries);
foreach (LogEntry entry in entries)
{
text.AppendLine(entry.ToString());
}
this.LoggerText = text.ToString();
}
}
private string _loggerText;
public string LoggerText
{
set
{
_loggerText = value;
RaisePropertyChanged("LoggerText");
}
get
{
return _loggerText;
}
}
Disclaimer: The above code is written without a compiler.
Related
I've been trying to integrate Caliburn.Micro MVVM framework in a C# WPF project I am in the middle off.
I currently only have three view models:
ShellViewModel - (A Window view with a ContentControl)
AboutViewModel - (A usercontrol view)
ChatViewModel - (Another usercontrol view)
Currently I am trying to use a button at the AboutView that is bind to the 'Chat()' method at the AboutViewModel and should take the user to the ChatView, yet I am testing this with the AboutViewModel. (As seen in the handler)
What I need is that all the Screen/ViewModels to be Singleton and only have one instance and when I try to change page, it returns to an already existant page.
The issue here is that I only have one instance registered when I do IoC.GetAllInstances(), the ShellViewModel and even though I've tried multiple configurations at the bootstrapper, I cannot register my other ViewModels in a way to make their instances "reachable"
I thank you for your time, and here is the code I think it's relevant for the issue:
Here is my bootstrapper:
public class AppBootstrapper : BootstrapperBase
{
private SimpleContainer _container = new SimpleContainer();
public AppBootstrapper()
{
Initialize();
var config = new TypeMappingConfiguration
{
DefaultSubNamespaceForViewModels = "ViewModel",
DefaultSubNamespaceForViews = "View"
};
ViewLocator.ConfigureTypeMappings(config);
Caliburn.Micro.ViewModelLocator.ConfigureTypeMappings(config);
}
protected override void Configure()
{
_container.Singleton<ShellViewModel, ShellViewModel>();
_container.Singleton<IWindowManager, WindowManager>();
//tried registering AboutViewModel in multiple ways
_container.Singleton<AboutViewModel, AboutViewModel>();
_container.RegisterSingleton(typeof(AboutViewModel), null,typeof(AboutViewModel));
/
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
DisplayRootViewFor<ShellViewModel>();
}
protected override object GetInstance(Type service, string key)
{
var instance = _container.GetInstance(service, key);
if (instance != null)
return instance;
throw new InvalidOperationException("Could not locate any instances.");
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return _container.GetAllInstances(service);
}
protected override void BuildUp(object instance)
{
_container.BuildUp(instance);
}
}
ShellViewModel.cs:
public class ShellViewModel : Conductor<object>, IHandle<NavigationMessage>
{
/// <summary>
/// Caliburn.Micro event aggregator. (Publish/Subscribe pattern)
/// </summary>
public IEventAggregator events = new EventAggregator();
public ShellViewModel()
{
//var aaa = IoC.Get<IEventAggregator>();
events.Subscribe(this);
ActivateItem(new AboutViewModel(events));
}
public void Handle(NavigationMessage message)
{
//var instance = IoC.GetInstance(message.ViewModelType,null);
var instances = IoC.GetAllInstances(null);
foreach(var i in instances)
{
MessageBox.Show(i.ToString());
}
ActivateItem(new AboutViewModel(events));
}
}
And the AboutViewModel.cs:
/// <summary>
/// ViewModel belonging to the AboutView.xaml.
/// </summary>
/// <seealso cref="AboutView.xaml"/>
public class AboutViewModel : Screen, IHandle<NavigationMessage>
{
private readonly IEventAggregator _eventAggregator;
/// <summary>
/// Private container for the 'Version' public property.
/// </summary>
/// <see cref="Version"/>
private string _version;
/// <summary>
/// Property containing a string of the application's current version (e.g.: 0.1.3.45)
/// </summary>
/// <see cref="_version"/>
[JsonIgnore]
public string Version
{
get
{
return _version;
}
set
{
_version = value;
NotifyOfPropertyChange(() => Version);
}
}
/// <summary>
/// Base constructor for the AboutViewModel class.
/// </summary>
public AboutViewModel(IEventAggregator eventAggregator)
{
Logging.Info("Initialize AboutViewModel", this.GetType());
Logging.Debug("Subscribing to the eventAggregator", this.GetType());
_eventAggregator = eventAggregator;
_eventAggregator.Subscribe(this);
_version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
Logging.Debug("Version loaded (" + _version + ")", this.GetType());
}
/// <summary>
/// Boolean method connected to the ChatCommand activates or deactivates based on it's return
/// </summary>
/// <param name="obj">Object of type GotoPageMessage received from the messenger</param>
public bool CanChat(object obj)
{
return true;
}
/// <summary>
/// Method connected to the ChatCommand that sends the user to the 'Chat' view
/// </summary>
/// <param name="obj">Object of type GotoPageMessage received from the messenger</param>
public void Chat(object obj)
{
_eventAggregator.PublishOnUIThread(new NavigationMessage(typeof(AboutViewModel)));
}
public void Handle(NavigationMessage message)
{
//This handle is used only to know how many instances I have active
MessageBox.Show("about");
}
}
Edit 1:
P.S.: I used to have my ShellViewModel as Conductor.Collection.OneActive. Still didn't work. Maybe AllActive may work?...
override the SelectAssemblies method for caliburn micro to locate all the views:
protected override IEnumerable<Assembly> SelectAssemblies()
{
return new[]
{
Assembly.GetExecutingAssembly(), typeof(MainViewModel).Assembly
};
}
more on the bootstrapper here.
I actually found a solution right now, without messing with Assemblies.
I noticed that the ShellViewModel's instance was accesible, and by saving the instance into a object and debugging it, I noticed all the viewModel instances I created were there at the 'Items'.
Basically this was what I did, in the handler that is inside the ShellViewModel:
public void Handle(NavigationMessage message)
{
ShellViewModel Shell = (ShellViewModel)IoC.GetInstance(typeof(ShellViewModel), null);
object Instance = null;
foreach (var item in Shell.Items)
{
if (item.ToString().Contains(message.ViewModelType.ToString()))
Instance = item;
}
object AuxObject = new object();
if (Instance == null)
{
try
{
Instance = Activator.CreateInstance(message.ViewModelType, Shell.events);
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
ActivateItem(Instance);
}
I'm new to Silverlight and MVVM and I'm trying to change Button.IsEnabled of one xaml from another xaml inside the app.
I tried to create a class but I'm not sure I'm using it correctly...
using System.ComponentModel;
namespace Reporting.Models
{
public class DataSourceValidation : INotifyPropertyChanged
{
private bool _isSaveEnabled;
public bool IsSaveEnabled
{
get
{
return _isSaveEnabled;
}
set
{
_isSaveEnabled = value;
RaisedPropertyChanged("IsSaveEnabled");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisedPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
}
I'd be very grateful for an explanation and example. I've been looking but its not quite clicking with me on how you bind the control to a model and then change that value from another xaml.cs... Provided I am even thinking about this the correct way.
If I need to provide more information just ask. Thanks for your time.
If you just wan't to get it done quickly (no good solution) then you can make this happen using static properties:
In ExampleA.xaml.cs create the following property:
public static ExampleA CurrentInstance { get; private set; }
and in the constructor of ExampleA (I will assume that the name of the class is ExampleA):
public ExampleA()
{
this.InitializeComponent();
CurrentInstance = this;
}
Then in your ExampleB class LostFocus method:
if (ExampleA.CurrentInstance != null)
{
var button = ExampleA.CurrentInstance.FindName("OKButton") as Button;
if (button != null)
{
button.IsEnabled = true;
}
}
On the other hand, I don't know how your classes are structured and how these XAML files are related, but a good universal solution would be by using the Event Aggregator pattern:
I am using here a slightly modified version of the EventAggregator class from the Caliburn.Micro MVVM framework:
namespace Caliburn.Micro
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
/// <summary>
/// A marker interface for classes that subscribe to messages.
/// </summary>
public interface IHandle { }
/// <summary>
/// Denotes a class which can handle a particular type of message.
/// </summary>
/// <typeparam name = "TMessage">The type of message to handle.</typeparam>
public interface IHandle<TMessage> : IHandle
{
/// <summary>
/// Handles the message.
/// </summary>
/// <param name = "message">The message.</param>
void Handle(TMessage message);
}
/// <summary>
/// Enables loosely-coupled publication of and subscription to events.
/// </summary>
public interface IEventAggregator
{
/// <summary>
/// Gets or sets the default publication thread marshaller.
/// </summary>
/// <value>
/// The default publication thread marshaller.
/// </value>
Action<System.Action> PublicationThreadMarshaller { get; set; }
/// <summary>
/// Subscribes an instance to all events declared through implementations of <see cref = "IHandle{T}" />
/// </summary>
/// <param name = "instance">The instance to subscribe for event publication.</param>
void Subscribe(object instance);
/// <summary>
/// Unsubscribes the instance from all events.
/// </summary>
/// <param name = "instance">The instance to unsubscribe.</param>
void Unsubscribe(object instance);
/// <summary>
/// Publishes a message.
/// </summary>
/// <param name = "message">The message instance.</param>
/// <remarks>
/// Uses the default thread marshaller during publication.
/// </remarks>
void Publish(object message);
/// <summary>
/// Publishes a message.
/// </summary>
/// <param name = "message">The message instance.</param>
/// <param name = "marshal">Allows the publisher to provide a custom thread marshaller for the message publication.</param>
void Publish(object message, Action<System.Action> marshal);
}
/// <summary>
/// Enables loosely-coupled publication of and subscription to events.
/// </summary>
public class EventAggregator : IEventAggregator
{
/// <summary>
/// The singleton instance.
/// </summary>
public static EventAggregator Instance = new EventAggregator();
/// <summary>
/// The default thread marshaller used for publication;
/// </summary>
public static Action<System.Action> DefaultPublicationThreadMarshaller = action => action();
readonly List<Handler> handlers = new List<Handler>();
/// <summary>
/// Initializes a new instance of the <see cref = "EventAggregator" /> class.
/// </summary>
public EventAggregator()
{
PublicationThreadMarshaller = DefaultPublicationThreadMarshaller;
}
/// <summary>
/// Gets or sets the default publication thread marshaller.
/// </summary>
/// <value>
/// The default publication thread marshaller.
/// </value>
public Action<System.Action> PublicationThreadMarshaller { get; set; }
/// <summary>
/// Subscribes an instance to all events declared through implementations of <see cref = "IHandle{T}" />
/// </summary>
/// <param name = "instance">The instance to subscribe for event publication.</param>
public virtual void Subscribe(object instance)
{
lock (handlers)
{
if (handlers.Any(x => x.Matches(instance)))
return;
handlers.Add(new Handler(instance));
}
}
/// <summary>
/// Unsubscribes the instance from all events.
/// </summary>
/// <param name = "instance">The instance to unsubscribe.</param>
public virtual void Unsubscribe(object instance)
{
lock (handlers)
{
var found = handlers.FirstOrDefault(x => x.Matches(instance));
if (found != null)
handlers.Remove(found);
}
}
/// <summary>
/// Publishes a message.
/// </summary>
/// <param name = "message">The message instance.</param>
/// <remarks>
/// Does not marshall the the publication to any special thread by default.
/// </remarks>
public virtual void Publish(object message)
{
Publish(message, PublicationThreadMarshaller);
}
/// <summary>
/// Publishes a message.
/// </summary>
/// <param name = "message">The message instance.</param>
/// <param name = "marshal">Allows the publisher to provide a custom thread marshaller for the message publication.</param>
public virtual void Publish(object message, Action<System.Action> marshal)
{
Handler[] toNotify;
lock (handlers)
toNotify = handlers.ToArray();
marshal(() =>
{
var messageType = message.GetType();
var dead = toNotify
.Where(handler => !handler.Handle(messageType, message))
.ToList();
if (dead.Any())
{
lock (handlers)
{
foreach (var handler in dead)
{
handlers.Remove(handler);
}
}
}
});
}
protected class Handler
{
readonly WeakReference reference;
readonly Dictionary<Type, MethodInfo> supportedHandlers = new Dictionary<Type, MethodInfo>();
public Handler(object handler)
{
reference = new WeakReference(handler);
var interfaces = handler.GetType().GetInterfaces()
.Where(x => typeof(IHandle).IsAssignableFrom(x) && x.IsGenericType);
foreach (var #interface in interfaces)
{
var type = #interface.GetGenericArguments()[0];
var method = #interface.GetMethod("Handle");
supportedHandlers[type] = method;
}
}
public bool Matches(object instance)
{
return reference.Target == instance;
}
public bool Handle(Type messageType, object message)
{
var target = reference.Target;
if (target == null)
return false;
foreach (var pair in supportedHandlers)
{
if (pair.Key.IsAssignableFrom(messageType))
{
pair.Value.Invoke(target, new[] { message });
return true;
}
}
return true;
}
}
}
}
Copy this class in your project.
Then create a simple message class which will represent a message to any listener that a button needs to be enabled:
public class EnableButtonMessage
{
}
Your ExampleA class then needs to implement the IHandle<EnableButtonMessage> interface (in the constructor you need to subscribe that this class is receiving some kind of messages, and the Handle method executes when an actual message of a registered type is received in which you then enable the button):
public sealed class ExampleA : UserControl, IHandle<EnableButtonMessage>
{
...
public ExampleA()
{
this.InitializeComponent();
EventAggregator.Instance.Subscribe(this);
}
...
public void Handle(EnableButtonMessage message)
{
this.OKButton.IsEnabled = true;
}
...
}
In ExampleB class' LostFocus method you just simply call to change your button property:
EventAggregator.Instance.Publish(new EnableButtonMessage());
This is a simple implementation of the EventAggregator pattern and you can then use if for any messaging between any entities inside your application globally. This is a powerful pattern and when used alongside MVVM can make your life a lot easier.
I have read many posts on binding an ObservableCollection to a ListView from people with similar issues; however, I haven't found a solution for my usecase, yet.
In the following test application, I have a simple ListView and a Button. On startup, the ListView is initialized, i.e. 2 columns and 30 rows with values from 0-29 are created. Half of the 30 rows (i.e. 15 rows) are visible. To see the remaining 15 Items I have to scroll down using the Scrollbar.
The Button is binded to an Asynchronous Command using the AsynchronousCommand Class from this article. When the button is clicked (see Start_Click), random numbers are written into those 30 rows of the ListView. This is done in an endless loop of a separate Thread (see AsynchronousCommand).
Now, when I click on the button, I would expect all ListView Items to change to random values instantaneously. However, this is not what's happening. Instead, only those Items that are not visible (i.e. the 15 Items beyond the ScrollBar) are changing their values. Sometimes, none of the Items changes its value.
Here's the XAML:
<Window x:Class="ListViewTestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="614">
<Grid x:Name="LayoutRoot">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="38*"/>
<ColumnDefinition Width="9*"/>
</Grid.ColumnDefinitions>
<ListView HorizontalAlignment="Left" ItemsSource="{Binding MyList}" Height="261" Margin="28,24,0,0" VerticalAlignment="Top" Width="454" Grid.ColumnSpan="2">
<ListView.View>
<GridView>
<GridViewColumn Width="100" Header="Name" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Width="325" Header="Data" DisplayMemberBinding="{Binding Data}" />
</GridView>
</ListView.View>
</ListView>
<Button Content="Start" Command="{Binding StartCommand}" Grid.Column="1" HorizontalAlignment="Left" Margin="21.043,42,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
</Window>
This is my Code (View's CodeBehind, ViewModel, Controller Logic, and Model):
/// <summary>
/// This is the CodeBehind of my View
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
LayoutRoot.DataContext = new ViewModel();
}
}
/// <summary>
/// This is my ViewModel
/// </summary>
public class ViewModel : NotifyPropertyChanged
{
private ObservableCollection<Document> _myList;
private Logic _logic;
private AsynchronousCommand _startCommand;
public ViewModel()
{
_myList = new ObservableCollection<Document>();
_logic = new Logic(this);
_startCommand = new AsynchronousCommand(_logic.Start_Click, true);
}
public ObservableCollection<Document> MyList
{
get { return _myList; }
set
{
if (_myList != value)
{
_myList = value;
RaisePropertyChangedEvent("MyList");
}
}
}
public AsynchronousCommand StartCommand
{
get
{
return _startCommand;
}
set
{
_startCommand = value;
}
}
}
public class NotifyPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChangedEvent(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
/// <summary>
/// This is my Controller
/// </summary>
public class Logic
{
private ViewModel _viewModel;
private Random _rnd;
public Logic(ViewModel vm)
{
_viewModel = vm;
_rnd = new Random();
for (int i = 0; i < 30; i++)
{
Document newDocument = new Document("Name " + i.ToString(), "Data " + i.ToString());
_viewModel.MyList.Add(newDocument);
}
}
public void Start_Click(object obj)
{
while (true)
{
int idx = _rnd.Next(0, 29);
_viewModel.StartCommand.ReportProgress(() =>
{
_viewModel.MyList[idx].Name = "New Name";
_viewModel.MyList[idx].Data = "New Data";
});
System.Threading.Thread.Sleep(20);
}
}
}
/// <summary>
/// This is my Model
/// </summary>
public class Document
{
public string Name { get; set; }
public string Data { get; set; }
public Document(string name, string data)
{
Name = name;
Data = data;
}
}
And this is the Code for my AsynchronousCommand, taken from Dave Kerr's article on CodeProject:
/// <summary>
/// The ViewModelCommand class - an ICommand that can fire a function.
/// </summary>
public class Command : ICommand
{
/// <summary>
/// Initializes a new instance of the <see cref="Command"/> class.
/// </summary>
/// <param name="action">The action.</param>
/// <param name="canExecute">if set to <c>true</c> [can execute].</param>
public Command(Action action, bool canExecute = true)
{
// Set the action.
this.action = action;
this.canExecute = canExecute;
}
/// <summary>
/// Initializes a new instance of the <see cref="Command"/> class.
/// </summary>
/// <param name="parameterizedAction">The parameterized action.</param>
/// <param name="canExecute">if set to <c>true</c> [can execute].</param>
public Command(Action<object> parameterizedAction, bool canExecute = true)
{
// Set the action.
this.parameterizedAction = parameterizedAction;
this.canExecute = canExecute;
}
/// <summary>
/// Executes the command.
/// </summary>
/// <param name="param">The param.</param>
public virtual void DoExecute(object param)
{
// Invoke the executing command, allowing the command to be cancelled.
CancelCommandEventArgs args = new CancelCommandEventArgs() { Parameter = param, Cancel = false };
InvokeExecuting(args);
// If the event has been cancelled, bail now.
if (args.Cancel)
return;
// Call the action or the parameterized action, whichever has been set.
InvokeAction(param);
// Call the executed function.
InvokeExecuted(new CommandEventArgs() { Parameter = param });
}
protected void InvokeAction(object param)
{
Action theAction = action;
Action<object> theParameterizedAction = parameterizedAction;
if (theAction != null)
theAction();
else if (theParameterizedAction != null)
theParameterizedAction(param);
}
protected void InvokeExecuted(CommandEventArgs args)
{
CommandEventHandler executed = Executed;
// Call the executed event.
if (executed != null)
executed(this, args);
}
protected void InvokeExecuting(CancelCommandEventArgs args)
{
CancelCommandEventHandler executing = Executing;
// Call the executed event.
if (executing != null)
executing(this, args);
}
/// <summary>
/// The action (or parameterized action) that will be called when the command is invoked.
/// </summary>
protected Action action = null;
protected Action<object> parameterizedAction = null;
/// <summary>
/// Bool indicating whether the command can execute.
/// </summary>
private bool canExecute = false;
/// <summary>
/// Gets or sets a value indicating whether this instance can execute.
/// </summary>
/// <value>
/// <c>true</c> if this instance can execute; otherwise, <c>false</c>.
/// </value>
public bool CanExecute
{
get { return canExecute; }
set
{
if (canExecute != value)
{
canExecute = value;
EventHandler canExecuteChanged = CanExecuteChanged;
if (canExecuteChanged != null)
canExecuteChanged(this, EventArgs.Empty);
}
}
}
#region ICommand Members
/// <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 set to null.</param>
/// <returns>
/// true if this command can be executed; otherwise, false.
/// </returns>
///
bool ICommand.CanExecute(object parameter)
{
return canExecute;
}
/// <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>
void ICommand.Execute(object parameter)
{
this.DoExecute(parameter);
}
#endregion
/// <summary>
/// Occurs when can execute is changed.
/// </summary>
public event EventHandler CanExecuteChanged;
/// <summary>
/// Occurs when the command is about to execute.
/// </summary>
public event CancelCommandEventHandler Executing;
/// <summary>
/// Occurs when the command executed.
/// </summary>
public event CommandEventHandler Executed;
}
/// <summary>
/// The CommandEventHandler delegate.
/// </summary>
public delegate void CommandEventHandler(object sender, CommandEventArgs args);
/// <summary>
/// The CancelCommandEvent delegate.
/// </summary>
public delegate void CancelCommandEventHandler(object sender, CancelCommandEventArgs args);
/// <summary>
/// CommandEventArgs - simply holds the command parameter.
/// </summary>
public class CommandEventArgs : EventArgs
{
/// <summary>
/// Gets or sets the parameter.
/// </summary>
/// <value>The parameter.</value>
public object Parameter { get; set; }
}
/// <summary>
/// CancelCommandEventArgs - just like above but allows the event to
/// be cancelled.
/// </summary>
public class CancelCommandEventArgs : CommandEventArgs
{
/// <summary>
/// Gets or sets a value indicating whether this <see cref="CancelCommandEventArgs"/> command should be cancelled.
/// </summary>
/// <value><c>true</c> if cancel; otherwise, <c>false</c>.</value>
public bool Cancel { get; set; }
}
/// <summary>
/// The AsynchronousCommand is a Command that runs on a thread from the thread pool.
/// </summary>
public class AsynchronousCommand : Command, INotifyPropertyChanged
{
/// <summary>
/// Initializes a new instance of the <see cref="AsynchronousCommand"/> class.
/// </summary>
/// <param name="action">The action.</param>
/// <param name="canExecute">if set to <c>true</c> the command can execute.</param>
public AsynchronousCommand(Action action, bool canExecute = true)
: base(action, canExecute)
{
// Initialise the command.
Initialise();
}
/// <summary>
/// Initializes a new instance of the <see cref="AsynchronousCommand"/> class.
/// </summary>
/// <param name="parameterizedAction">The parameterized action.</param>
/// <param name="canExecute">if set to <c>true</c> [can execute].</param>
public AsynchronousCommand(Action<object> parameterizedAction, bool canExecute = true)
: base(parameterizedAction, canExecute)
{
// Initialise the command.
Initialise();
}
/// <summary>
/// Initialises this instance.
/// </summary>
private void Initialise()
{
// Construct the cancel command.
cancelCommand = new Command(
() =>
{
// Set the Is Cancellation Requested flag.
IsCancellationRequested = true;
}, true);
}
/// <summary>
/// Executes the command.
/// </summary>
/// <param name="param">The param.</param>
public override void DoExecute(object param)
{
// If we are already executing, do not continue.
if (IsExecuting)
return;
// Invoke the executing command, allowing the command to be cancelled.
CancelCommandEventArgs args = new CancelCommandEventArgs() { Parameter = param, Cancel = false };
InvokeExecuting(args);
// If the event has been cancelled, bail now.
if (args.Cancel)
return;
// We are executing.
IsExecuting = true;
// Store the calling dispatcher.
callingDispatcher = Dispatcher.CurrentDispatcher;
// Run the action on a new thread from the thread pool (this will therefore work in SL and WP7 as well).
ThreadPool.QueueUserWorkItem(
(state) =>
{
// Invoke the action.
InvokeAction(param);
// Fire the executed event and set the executing state.
ReportProgress(
() =>
{
// We are no longer executing.
IsExecuting = false;
// If we were cancelled, invoke the cancelled event - otherwise invoke executed.
if (IsCancellationRequested)
InvokeCancelled(new CommandEventArgs() { Parameter = param });
else
InvokeExecuted(new CommandEventArgs() { Parameter = param });
// We are no longer requesting cancellation.
IsCancellationRequested = false;
}
);
}
);
}
/// <summary>
/// Raises the property changed event.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
private void NotifyPropertyChanged(string propertyName)
{
// Store the event handler - in case it changes between
// the line to check it and the line to fire it.
PropertyChangedEventHandler propertyChanged = PropertyChanged;
// If the event has been subscribed to, fire it.
if (propertyChanged != null)
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary>
/// Reports progress on the thread which invoked the command.
/// </summary>
/// <param name="action">The action.</param>
public void ReportProgress(Action action)
{
if (IsExecuting)
{
if (callingDispatcher.CheckAccess())
action();
else
callingDispatcher.BeginInvoke(((Action)(() => { action(); })));
}
}
/// <summary>
/// Cancels the command if requested.
/// </summary>
/// <returns>True if the command has been cancelled and we must return.</returns>
public bool CancelIfRequested()
{
// If we haven't requested cancellation, there's nothing to do.
if (IsCancellationRequested == false)
return false;
// We're done.
return true;
}
/// <summary>
/// Invokes the cancelled event.
/// </summary>
/// <param name="args">The <see cref="Apex.MVVM.CommandEventArgs"/> instance containing the event data.</param>
protected void InvokeCancelled(CommandEventArgs args)
{
CommandEventHandler cancelled = Cancelled;
// Call the cancelled event.
if (cancelled != null)
cancelled(this, args);
}
protected Dispatcher callingDispatcher;
/// <summary>
/// Flag indicating that the command is executing.
/// </summary>
private bool isExecuting = false;
/// <summary>
/// Flag indicated that cancellation has been requested.
/// </summary>
private bool isCancellationRequested;
/// <summary>
/// The cancel command.
/// </summary>
private Command cancelCommand;
/// <summary>
/// The property changed event.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Occurs when the command is cancelled.
/// </summary>
public event CommandEventHandler Cancelled;
/// <summary>
/// Gets or sets a value indicating whether this instance is executing.
/// </summary>
/// <value>
/// <c>true</c> if this instance is executing; otherwise, <c>false</c>.
/// </value>
public bool IsExecuting
{
get
{
return isExecuting;
}
set
{
if (isExecuting != value)
{
isExecuting = value;
NotifyPropertyChanged("IsExecuting");
}
}
}
/// <summary>
/// Gets or sets a value indicating whether this instance is cancellation requested.
/// </summary>
/// <value>
/// <c>true</c> if this instance is cancellation requested; otherwise, <c>false</c>.
/// </value>
public bool IsCancellationRequested
{
get
{
return isCancellationRequested;
}
set
{
if (isCancellationRequested != value)
{
isCancellationRequested = value;
NotifyPropertyChanged("IsCancellationRequested");
}
}
}
/// <summary>
/// Gets the cancel command.
/// </summary>
public Command CancelCommand
{
get { return cancelCommand; }
}
}
Your Document Class has to implement INotifyPropertyChanged Interface so the UI gets updated once your Properties change.
Why is it working when you scroll?
Because of Virtualization. Not visible entries have not been evualuated yet, so once they get evaluated they already receive the "New Value".
Here is your Document class that works:
/// <summary>
/// This is my Model
/// </summary>
public class Document : INotifyPropertyChanged
{
private string _name;
private string _data;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged();
}
}
public string Data
{
get { return _data; }
set
{
_data = value;
OnPropertyChanged();
}
}
public Document(string name, string data)
{
Name = name;
Data = data;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Let's say we have a control (a Button). This button is just thrown inside a grid in the most laziest way possible. The Command property is already binded, and it pops a MessageBox. I want to write a second command for my Button, let's say when Mouse enters it's surface (MouseEnter). But I want to do it this way
<Button Command="{Binding MyClick}" MySecondCommand= "{Binding MouseHoovering}"
Margin="34,39,0,0">
I know that ICommandSource forces me to implement Command property. As it is already implemented by ButtonBase and exposed in my example. What shoud I do to make "MySecondCommand" possible ? Just to declare it as a dependency property is not enough! :)
add your project --> CommandBehaviorBinding.cs,CommandBehavior.cs
use like this
<Button Command="{Binding MyClickCommand}"
Margin="34,39,0,0" local:CommandBehavior.Event="MouseHoovering"
local:CommandBehavior.Command="{Binding MouseHooveringCommand}">
CommandBehaviorBinding.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Reflection;
using System.Windows;
namespace yournamespace
{
public class CommandBehaviorBinding : IDisposable
{
#region Properties
/// <summary>
/// Get the owner of the CommandBinding ex: a Button
/// This property can only be set from the BindEvent Method
/// </summary>
public DependencyObject Owner { get; private set; }
/// <summary>
/// The event name to hook up to
/// This property can only be set from the BindEvent Method
/// </summary>
public string EventName { get; private set; }
/// <summary>
/// The event info of the event
/// </summary>
public EventInfo Event { get; private set; }
/// <summary>
/// Gets the EventHandler for the binding with the event
/// </summary>
public Delegate EventHandler { get; private set; }
#region Execution
//stores the strategy of how to execute the event handler
IExecutionStrategy strategy;
/// <summary>
/// Gets or sets a CommandParameter
/// </summary>
public object CommandParameter { get; set; }
ICommand command;
/// <summary>
/// The command to execute when the specified event is raised
/// </summary>
public ICommand Command
{
get { return command; }
set
{
command = value;
//set the execution strategy to execute the command
strategy = new CommandExecutionStrategy { Behavior = this };
}
}
Action<object> action;
/// <summary>
/// Gets or sets the Action
/// </summary>
public Action<object> Action
{
get { return action; }
set
{
action = value;
// set the execution strategy to execute the action
strategy = new ActionExecutionStrategy { Behavior = this };
}
}
#endregion
#endregion
//Creates an EventHandler on runtime and registers that handler to the Event specified
public void BindEvent(DependencyObject owner, string eventName)
{
EventName = eventName;
Owner = owner;
Event = Owner.GetType().GetEvent(EventName, BindingFlags.Public | BindingFlags.Instance);
if (Event == null)
throw new InvalidOperationException(String.Format("Could not resolve event name {0}", EventName));
//Create an event handler for the event that will call the ExecuteCommand method
EventHandler = EventHandlerGenerator.CreateDelegate(
Event.EventHandlerType, typeof(CommandBehaviorBinding).GetMethod("Execute", BindingFlags.Public | BindingFlags.Instance), this);
//Register the handler to the Event
Event.AddEventHandler(Owner, EventHandler);
}
/// <summary>
/// Executes the strategy
/// </summary>
public void Execute()
{
strategy.Execute(CommandParameter);
}
#region IDisposable Members
bool disposed = false;
/// <summary>
/// Unregisters the EventHandler from the Event
/// </summary>
public void Dispose()
{
if (!disposed)
{
Event.RemoveEventHandler(Owner, EventHandler);
disposed = true;
}
}
#endregion
}
}
CommandBehavior.cs :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;
namespace yournamespace
{
public class CommandBehavior
{
#region Behavior
/// <summary>
/// Behavior Attached Dependency Property
/// </summary>
private static readonly DependencyProperty BehaviorProperty =
DependencyProperty.RegisterAttached("Behavior", typeof(CommandBehaviorBinding), typeof(CommandBehavior),
new FrameworkPropertyMetadata((CommandBehaviorBinding)null));
/// <summary>
/// Gets the Behavior property.
/// </summary>
private static CommandBehaviorBinding GetBehavior(DependencyObject d)
{
return (CommandBehaviorBinding)d.GetValue(BehaviorProperty);
}
/// <summary>
/// Sets the Behavior property.
/// </summary>
private static void SetBehavior(DependencyObject d, CommandBehaviorBinding value)
{
d.SetValue(BehaviorProperty, value);
}
#endregion
#region Command
/// <summary>
/// Command Attached Dependency Property
/// </summary>
public static readonly DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(CommandBehavior),
new FrameworkPropertyMetadata((ICommand)null,
new PropertyChangedCallback(OnCommandChanged)));
/// <summary>
/// Gets the Command property.
/// </summary>
public static ICommand GetCommand(DependencyObject d)
{
return (ICommand)d.GetValue(CommandProperty);
}
/// <summary>
/// Sets the Command property.
/// </summary>
public static void SetCommand(DependencyObject d, ICommand value)
{
d.SetValue(CommandProperty, value);
}
/// <summary>
/// Handles changes to the Command property.
/// </summary>
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CommandBehaviorBinding binding = FetchOrCreateBinding(d);
binding.Command = (ICommand)e.NewValue;
}
#endregion
#region Action
/// <summary>
/// Action Attached Dependency Property
/// </summary>
public static readonly DependencyProperty ActionProperty =
DependencyProperty.RegisterAttached("Action", typeof(Action<object>), typeof(CommandBehavior),
new FrameworkPropertyMetadata((Action<object>)null,
new PropertyChangedCallback(OnActionChanged)));
/// <summary>
/// Gets the Action property.
/// </summary>
public static Action<object> GetAction(DependencyObject d)
{
return (Action<object>)d.GetValue(ActionProperty);
}
/// <summary>
/// Sets the Action property.
/// </summary>
public static void SetAction(DependencyObject d, Action<object> value)
{
d.SetValue(ActionProperty, value);
}
/// <summary>
/// Handles changes to the Action property.
/// </summary>
private static void OnActionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CommandBehaviorBinding binding = FetchOrCreateBinding(d);
binding.Action = (Action<object>)e.NewValue;
}
#endregion
#region CommandParameter
/// <summary>
/// CommandParameter Attached Dependency Property
/// </summary>
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.RegisterAttached("CommandParameter", typeof(object), typeof(CommandBehavior),
new FrameworkPropertyMetadata((object)null,
new PropertyChangedCallback(OnCommandParameterChanged)));
/// <summary>
/// Gets the CommandParameter property.
/// </summary>
public static object GetCommandParameter(DependencyObject d)
{
return (object)d.GetValue(CommandParameterProperty);
}
/// <summary>
/// Sets the CommandParameter property.
/// </summary>
public static void SetCommandParameter(DependencyObject d, object value)
{
d.SetValue(CommandParameterProperty, value);
}
/// <summary>
/// Handles changes to the CommandParameter property.
/// </summary>
private static void OnCommandParameterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CommandBehaviorBinding binding = FetchOrCreateBinding(d);
binding.CommandParameter = e.NewValue;
}
#endregion
#region Event
/// <summary>
/// Event Attached Dependency Property
/// </summary>
public static readonly DependencyProperty EventProperty =
DependencyProperty.RegisterAttached("Event", typeof(string), typeof(CommandBehavior),
new FrameworkPropertyMetadata((string)String.Empty,
new PropertyChangedCallback(OnEventChanged)));
/// <summary>
/// Gets the Event property. This dependency property
/// indicates ....
/// </summary>
public static string GetEvent(DependencyObject d)
{
return (string)d.GetValue(EventProperty);
}
/// <summary>
/// Sets the Event property. This dependency property
/// indicates ....
/// </summary>
public static void SetEvent(DependencyObject d, string value)
{
d.SetValue(EventProperty, value);
}
/// <summary>
/// Handles changes to the Event property.
/// </summary>
private static void OnEventChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CommandBehaviorBinding binding = FetchOrCreateBinding(d);
//check if the Event is set. If yes we need to rebind the Command to the new event and unregister the old one
if (binding.Event != null && binding.Owner != null)
binding.Dispose();
//bind the new event to the command
binding.BindEvent(d, e.NewValue.ToString());
}
#endregion
#region Helpers
//tries to get a CommandBehaviorBinding from the element. Creates a new instance if there is not one attached
private static CommandBehaviorBinding FetchOrCreateBinding(DependencyObject d)
{
CommandBehaviorBinding binding = CommandBehavior.GetBehavior(d);
if (binding == null)
{
binding = new CommandBehaviorBinding();
CommandBehavior.SetBehavior(d, binding);
}
return binding;
}
#endregion
}
}
How can I bind a MouseDoubleClick event of a DataGrid to a delegate command and get the selected item of that grid.? I want to do this in XAML file as I'm using Prism 2.0 and MVVM pattern.
Please look at attached properties, it will be helpful.
The below is the code that will help, i had seen this somewhere but don't remember the site or author's name thanks to him anyways.
/// <summary>
/// The static class the defines the attached command DP for exposing MouseDoubleClick
/// event as a command
/// </summary>
public static class CommandMouseDoubleClick
{
#region TheCommandToRun
/// <summary>
/// TheCommandToRun : The actual ICommand to run
/// </summary>
public static readonly DependencyProperty TheCommandToRunProperty =
DependencyProperty.RegisterAttached("TheCommandToRun", typeof(ICommand),
typeof(CommandMouseDoubleClick),
new FrameworkPropertyMetadata((ICommand)null));
/// <summary>
/// Gets the TheCommandToRun property.
/// </summary>
public static ICommand GetTheCommandToRun(DependencyObject d)
{
return (ICommand)d.GetValue(TheCommandToRunProperty);
}
/// <summary>
/// Sets the TheCommandToRun property.
/// </summary>
public static void SetTheCommandToRun(DependencyObject d, ICommand value)
{
d.SetValue(TheCommandToRunProperty, value);
}
#endregion
#region RoutedEventName
/// <summary>
/// RoutedEventName : The event that should actually execute the
/// ICommand
/// </summary>
public static readonly DependencyProperty RoutedEventNameProperty =
DependencyProperty.RegisterAttached("RoutedEventName", typeof(String),
typeof(CommandMouseDoubleClick),
new FrameworkPropertyMetadata((String)String.Empty,
new PropertyChangedCallback(OnRoutedEventNameChanged)));
/// <summary>
/// Gets the RoutedEventName property.
/// </summary>
public static String GetRoutedEventName(DependencyObject d)
{
return (String)d.GetValue(RoutedEventNameProperty);
}
/// <summary>
/// Sets the RoutedEventName property.
/// </summary>
public static void SetRoutedEventName(DependencyObject d, String value)
{
d.SetValue(RoutedEventNameProperty, value);
}
/// <summary>
/// Hooks up a Dynamically created EventHandler (by using the
/// <see cref="MouseDoubleClickEventHooker">MouseDoubleClickEventHooker</see> class) that when
/// run will run the associated ICommand
/// </summary>
private static void OnRoutedEventNameChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
String routedEvent = (String)e.NewValue;
//If the RoutedEvent string is not null, create a new
//dynamically created EventHandler that when run will execute
//the actual bound ICommand instance (usually in the ViewModel)
if (!String.IsNullOrEmpty(routedEvent))
{
MouseDoubleClickEventHooker eventHooker = new MouseDoubleClickEventHooker();
eventHooker.ObjectWithAttachedCommand = d;
EventInfo eventInfo = d.GetType().GetEvent(routedEvent,
BindingFlags.Public | BindingFlags.Instance);
//Hook up Dynamically created event handler
if (eventInfo != null)
{
eventInfo.AddEventHandler(d,
eventHooker.GetNewEventHandlerToRunCommand(eventInfo));
}
}
}
#endregion
}
/// <summary>
/// Contains the event that is hooked into the source RoutedEvent
/// that was specified to run the ICommand
/// </summary>
sealed class MouseDoubleClickEventHooker
{
#region Properties
/// <summary>
/// The DependencyObject, that holds a binding to the actual
/// ICommand to execute
/// </summary>
public DependencyObject ObjectWithAttachedCommand { get; set; }
#endregion
#region Public Methods
/// <summary>
/// Creates a Dynamic EventHandler that will be run the ICommand
/// when the user specified RoutedEvent fires
/// </summary>
/// <param name="eventInfo">The specified RoutedEvent EventInfo</param>
///<returns>An Delegate that points to a new EventHandler
/// that will be run the ICommand</returns>
public Delegate GetNewEventHandlerToRunCommand(EventInfo eventInfo)
{
Delegate del = null;
if (eventInfo == null)
throw new ArgumentNullException("eventInfo");
if (eventInfo.EventHandlerType == null)
throw new ArgumentException("EventHandlerType is null");
if (del == null)
del = Delegate.CreateDelegate(eventInfo.EventHandlerType, this,
GetType().GetMethod("OnEventRaised",
BindingFlags.NonPublic |
BindingFlags.Instance));
return del;
}
#endregion
#region Private Methods
/// <summary>
/// Runs the ICommand when the requested RoutedEvent fires
/// </summary>
private void OnEventRaised(object sender, EventArgs e)
{
ICommand command = (ICommand)(sender as DependencyObject).
GetValue(CommandMouseDoubleClick.TheCommandToRunProperty);
if (command != null)
{
command.Execute(null);
}
}
#endregion
}
The xaml could look something like below:
<Label local:CommandMouseLeftButtonDown.RoutedEventName="MouseLeftButtonDown"
local:CommandMouseLeftButtonDown.TheCommandToRun="{Binding Path=MouseDownCommand, RelativeSource={RelativeSource TemplatedParent}}"
local:CommandMouseDoubleClick.RoutedEventName="MouseDoubleClick"
local:CommandMouseDoubleClick.TheCommandToRun="{Binding Path=MouseDoubleClickCommand, RelativeSource={RelativeSource TemplatedParent}}"/>