In the following example the temp variable in RaisePropertyChanged() is always null. How do I subscribe to the event?
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations.Schema;
using System.Runtime.CompilerServices;
using System.Text;
namespace TestProject.Module.BusinessObjects
{
public class ContactPerson : INotifyPropertyChanged
{
public string FirstName { get; set; }
public string LastName { get; set; }
[NotMapped]
public string PersonFullName
{
set
{
if (PersonFullName != value)
{
var stringArray = value.Split();
var firstIndex = 0;
var lastIndex = stringArray.Length - 1;
if (lastIndex >= firstIndex)
{
FirstName = stringArray[firstIndex];
}
if (lastIndex > firstIndex)
{
LastName = stringArray[lastIndex];
}
RaisePropertyChanged();
}
}
get
{
var sb = new StringBuilder();
sb.Append(FirstName);
sb.Append(" ");
sb.Append(LastName);
var stringArray = sb.ToString().Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
var s = string.Join(" ", stringArray);
return s;
}
}
public event PropertyChangedEventHandler PropertyChanged;
public ContactPerson Clone()
{
var obj = new ContactPerson { FirstName = FirstName, LastName = LastName };
return obj;
}
public override string ToString()
{
return PersonFullName;
}
protected void RaisePropertyChanged([CallerMemberName] string propertName = "")
{
var temp = PropertyChanged;
if (temp != null)
{
temp(this, new PropertyChangedEventArgs(propertName));
}
}
}
}
from reading This question it seems that PropertyChanged has not been subscribed to. How do I do this subscription?
Like this:
private void Form1_Load(object sender, EventArgs e)
{
ContactPerson p = new ContactPerson();
p.PropertyChanged += P_PropertyChanged;
}
private void P_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
throw new NotImplementedException();
}
EDIT: expanding the sample code:
public partial class Form1 : Form
{
ContactPerson p;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
p = new ContactPerson();
p.PersonFullName = "Mary Jane";
p.PropertyChanged += P_PropertyChanged;
label1.Text = p.PersonFullName;
// If you use databinding instead, you get the same result in this case.
//label1.DataBindings.Add(new Binding("Text", p, "PersonFullName"));
}
private void P_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
label1.Text = p.PersonFullName;
}
private void button1_Click(object sender, EventArgs e)
{
p.PersonFullName = "John Doe";
}
}
Related
I made Behavior for transfer image to the ViewModel property.
When the user сlicks on the element, the gallery will open. When the user chooses some image from gallery, my ImageBytes will have bytes of this image. But after I assign a new value to the property, it is not passed to my VM.
My view model does not respond to changes in the Behavior.
public class FolderDialogBehavior : Behavior<View>
{
public byte[] ImageBytes
{
get { return (byte[])GetValue(ImageBytesProperty); }
private set
{
SetValue(ImageBytesProperty, value);
}
}
public readonly static BindableProperty ImageBytesProperty = BindableProperty.Create(nameof(ImageBytes), typeof(byte[]),
typeof(FolderDialogBehavior), null, BindingMode.TwoWay);
private TapGestureRecognizer tapGestureRecognizer = new TapGestureRecognizer()
{
NumberOfTapsRequired = 1
};
protected override void OnAttachedTo(View view)
{
base.OnAttachedTo(view);
tapGestureRecognizer.Tapped += OnTapGestureRecognizerTapped;
view.GestureRecognizers.Add(tapGestureRecognizer);
}
protected override void OnDetachingFrom(View view)
{
base.OnDetachingFrom(view);
tapGestureRecognizer.Tapped -= OnTapGestureRecognizerTapped;
view.GestureRecognizers.Remove(tapGestureRecognizer);
}
private void OnTapGestureRecognizerTapped(object sender, EventArgs e)
{
GetPhotoAsync();
}
private async void GetPhotoAsync()
{
try
{
var photo = await MediaPicker.PickPhotoAsync();
byte[] bytes;
using (Stream sourceStream = await photo.OpenReadAsync())
{
bytes = new byte[sourceStream.Length];
await sourceStream.ReadAsync(bytes, 0, (int)sourceStream.Length);
}
ImageBytes = bytes;
}
catch (Exception ex)
{
//await DisplayAlert("Сообщение об ошибке", ex.Message, "OK");
}
}
}
<Frame>
<Frame.Behaviors>
<local:FolderDialogBehavior ImageBytes="{Binding AddEmployee.UserImage, Mode=TwoWay}"/>
</Frame.Behaviors>
</Frame>
public class EmployeeViewModel : OnPropertyChangedClass
{
private byte[] _userImage;
public byte[] UserImage
{
get => _userImage;
// *** I don't get here with debugging.***
set => SetProperty(ref _userImage, value);
}
}
public abstract class OnPropertyChangedClass : INotifyPropertyChanged
{
/// <inheritdoc cref="INotifyPropertyChanged"/>
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected void SetProperty<T>(ref T propertyFiled, T newValue, [CallerMemberName] string propertyName = null)
{
if (!object.Equals(propertyFiled, newValue))
{
T oldValue = propertyFiled;
propertyFiled = newValue;
RaisePropertyChanged(propertyName);
OnPropertyChanged(propertyName, oldValue, newValue);
}
}
protected virtual void OnPropertyChanged(string propertyName, object oldValue, object newValue) { }
}
protected override void OnAttachedTo(View bindable)
{
BindingContext = bindable.BindingContext;
bindable.BindingContextChanged += Bindable_BindingContextChanged;
/* Some Code */
base.OnAttachedTo(bindable);
}
protected override void OnDetachingFrom(View bindable)
{
bindable.BindingContextChanged -= Bindable_BindingContextChanged;
/* Some Code */
base.OnDetachingFrom(bindable);
}
private void Bindable_BindingContextChanged(object sender, EventArgs e)
{
if (sender is View view)
{
BindingContext = (sender as View).BindingContext;
}
}
I build a simple ClipboardManager that hold all last Copy item.
So i have this simple ClipboardItem class:
public class ClipboardItem : INotifyPropertyChanged
{
private string _text { get; set; }
private int _index { get; set; }
public string Text
{
get { return _text; }
set
{
_text = value;
NotifyPropertyChanged();
}
}
public int Index
{
get { return _index; }
set
{
_index = value;
NotifyPropertyChanged();
}
}
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
And my ViewModel class that hold ObservableCollection<ClipboardItem>:
public class ViewModel : INotifyPropertyChanged
{
private ObservableCollection<ClipboardItem> _clipboards;
public ViewModel()
{
if (_clipboards == null)
{
_clipboards = new ObservableCollection<ClipboardItem>();
_clipboards.CollectionChanged += _clipboards_CollectionChanged;
}
}
private void _clipboards_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
for (int i = 0; i < _clipboards.Count; i++)
_clipboards[i].Index = i + 1;
}
public ObservableCollection<ClipboardItem> Clipboards
{
get { return _clipboards; }
set
{
_clipboards = value;
NotifyPropertyChanged();
}
}
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
So every Copy create new ClipboardItem object inside list but when i restart the application all the records gone so i wonder if there any way to store all my ClipboardItem object inside the application settings.settings file.
//Variable Creation
private string _dataFileName = #"ClipData.xml";
DataTable _clipDataTable = new DataTable();
Inside ViewModel constructor.
public ViewModel()
{
if (_clipboards == null)
{
_clipboards = new ObservableCollection<ClipboardItem>();
_clipboards.CollectionChanged += _clipboards_CollectionChanged;
}
InitDataTable();
ReadDataFile();
}
Create new Methods
/// <summary>
/// Initialize Data Table considering you have only 1 column data.
/// If you have more then you need to create more columns
/// </summary>
private void InitDataTable()
{
_clipDataTable = new DataTable();
_clipDataTable.Columns.Add("ClipHeader");
_clipDataTable.AcceptChanges();
}
//the clipboard Data is saved in xml file.
private void WriteDataFile()
{
DataSet ClipDataSet = new DataSet();
ClipDataSet.Tables.Add(_clipDataTable);
ClipDataSet.WriteXml(_dataFileName);
}
// if file exits then read the xml file and add it to the Collection, which will be reflected in UI.
private void ReadDataFile()
{
DataSet ClipDataSet = new DataSet();
if (File.Exists(_dataFileName))
{
ClipDataSet.ReadXml(_dataFileName);
foreach (DataRow item in ClipDataSet.Tables[0].Rows)
{
Clipboards.Add(new ClipboardItem { Text = Convert.ToString(item["ClipHeader"]) });
}
}
}
Using Command you can bind Method of ViewModel to Window Closing event. So whenever the user closes the window, the data in the collection will be written into the Xml file.
Data from the Collection is copied into DataTable.
private void WindowCloseCommadn(object o)
{
foreach (var item in Clipboards)
{
DataRow dataRow = _clipDataTable.NewRow();
dataRow["ClipHeader"] = item.Text;
_clipDataTable.Rows.Add(dataRow);
}
WriteDataFile();
}
Update:-
Same Code without ViewModel, by making the codebehind class has the DataContext for binding Collection.
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ClipboardMonitor clipboardMonitor;
private string _dataFileName = #"ClipData.xml";
DataTable _clipDataTable = new DataTable();
public ObservableCollection<ClipboardItem> Clipboards { get; set; }
public MainWindow()
{
InitializeComponent();
Clipboards = new ObservableCollection<ClipboardItem>();
Clipboards.CollectionChanged += Clipboards_CollectionChanged;
Loaded += MainWindow_Loaded;
InitiateClipboardMonitor();
this.Closing += MainWindow_Closing1;
this.DataContext = this;
}
private void MainWindow_Closing1(object sender, CancelEventArgs e)
{
foreach (var item in Clipboards)
{
DataRow dataRow = _clipDataTable.NewRow();
dataRow["ClipHeader"] = item.Text;
_clipDataTable.Rows.Add(dataRow);
}
WriteDataFile();
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
InitDataTable();
ReadDataFile();
}
/// <summary>
/// Initialize Data Table considering you have only 1 column data.
/// If you have more then you need to create more columns
/// </summary>
private void InitDataTable()
{
_clipDataTable = new DataTable();
_clipDataTable.Columns.Add("ClipHeader");
_clipDataTable.AcceptChanges();
}
private void WriteDataFile()
{
DataSet ClipDataSet = new DataSet();
ClipDataSet.Tables.Add(_clipDataTable);
ClipDataSet.WriteXml(_dataFileName);
}
private void ReadDataFile()
{
DataSet ClipDataSet = new DataSet();
if (File.Exists(_dataFileName))
{
ClipDataSet.ReadXml(_dataFileName);
foreach (DataRow item in ClipDataSet.Tables[0].Rows)
{
Clipboards.Add(new ClipboardItem { Text = Convert.ToString(item["ClipHeader"]) });
}
}
}
private void Clipboards_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
for (int i = 0; i < Clipboards.Count; i++)
{
Clipboards[i].Index = i + 1;
}
}
private void InitiateClipboardMonitor()
{
clipboardMonitor = new ClipboardMonitor();
clipboardMonitor.OnClipboardContentChanged += ClipboardMonitor_OnClipboardContentChanged; ;
}
private void ClipboardMonitor_OnClipboardContentChanged(object sender, EventArgs e)
{
string clipboardText = Clipboard.GetText(TextDataFormat.Text);
Clipboards.Add(new ClipboardItem { Text = clipboardText });
}
}
to know about Command, refer the article
https://www.codeproject.com/Articles/274982/Commands-in-MVVM
I want to insert html code into existing html code.
But I do not see the result. Here is the code C #:
1) Program.cs
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
wUI.DocumentReady += wUI_DocumentReady;
}
private void Form1_Load(object sender, EventArgs e)
{
// code here ?
}
void wUI_DocumentReady(object sender, DocumentReadyEventArgs e)
{
wUI.LoadHTML("<html><body>sadasdsad</body></html>");
HtmlManager html = HtmlManager.Instance;
string[] placeholders = { "asset://customdatastore/path/to/any", "type-button", "no-action", "Example link" };
html.Add("{3}", placeholders);
html.InnerCode(html.Code, wUI, "body");
wUI.Refresh();
}
}
2) HtmlManager.cs
public sealed class HtmlManager
{
private static readonly Lazy<HtmlManager> InstanceField = new Lazy<HtmlManager>(() => new HtmlManager());
private StringBuilder _stringBuilder = null;
public string Code { get { return _stringBuilder.ToString(); } }
private HtmlManager()
{
if (_stringBuilder != null)
_stringBuilder.Clear();
_stringBuilder = new StringBuilder();
}
public static HtmlManager Instance { get { return InstanceField.Value; } }
public void Add(string row, string[] placeholders = null)
{
if (placeholders != null)
_stringBuilder.AppendLine(string.Format(row, placeholders));
_stringBuilder.AppendLine(row);
}
public void InnerCode(string code, object sender, string afterTag = "html")
{
Awesomium.Windows.Forms.WebControl ui = (Awesomium.Windows.Forms.WebControl)sender;
ui.ExecuteJavascript(string.Format("document.getElementsByTagName({0})[0].innerHTML({1})", afterTag, code));
}
public void Clear()
{
_stringBuilder.Clear();
}
}
The event (DocumentReady) does not happen, I do not believe, maybe I'm wrong somewhere?
UP: I try do it:
private void Form1_Load(object sender, EventArgs e)
{
wUI.LoadHTML("<html><body>sadasdsad</body></html>");
}
void wUI_DocumentReady(object sender, DocumentReadyEventArgs e)
{
HtmlManager html = HtmlManager.Instance;
string[] placeholders = { "asset://customdatastore/path/to/any", "type-button", "no-action", "Example link" };
html.Add("{3}", placeholders);
wUI.ExecuteJavascript("document.getElementsByTagName('body').innerHTML(\"sometext\")");
//html.InnerCode(html.Code, wUI, "body");
//wUI.Refresh();
}
No result
UP 2:
public void Add(string row, string[] placeholders = null)
{
if (placeholders != null)
_stringBuilder.AppendLine(string.Format(row, placeholders));
if (placeholders == null)
_stringBuilder.AppendLine(row);
}
UP 3:
Work with:
wUI.Source = new Uri(#"http://google.com");
in Form1_Load
You can use LoadHtml method, but only after document is fully loaded (don't confuse with DocumentReadyState.Ready) It works for me at least:
private void WebControl_DocumentReady(object sender, DocumentReadyEventArgs e)
{
if (e.ReadyState != DocumentReadyState.Loaded) return;
}
But as an initialisation, you should use Source property, like you wrote in your third update
I'm trying to implement my first MVVM application. I could bound the data in a datagrid, but the changes I make in items does not fire the RaisePropertyChanged method of the Model.
This is My ViewModel
public class UsersViewModel : BaseViewModel
{
private static TOPEntities _context;
private ObservableCollection<UserModel> _usersCollection;
public UsersViewModel()
{
_usersCollection = new ObservableCollection<UserModel>(GetAllUsers());
}
public ObservableCollection<UserModel> UsersCollection
{
get { return _usersCollection; }
set
{
if (_usersCollection != value)
{
_usersCollection = value;
RaisePropertyChanged(() => UsersCollection);
}
}
}
public static List<UserModel> GetAllUsers()
{
using (_context = new TOPEntities())
{
return _context.Users.Select
(user => new UserModel
{
Id_User = user.Id_User,
Name = user.Name,
Username = user.Username,
Language = user.Language,
Password = user.Password,
Profile = user.Profile
}).ToList();
}
}
The Model, implements NotificationObject class that provides the INotifyPropertyChanged
public class UserModel : NotificationObject
{
#region Construction
/// Constructs the default instance of a UserModel
public UserModel()
{
}
#endregion
#region Model Attributes
private int _id_User;
private string _username;
private string _password;
private string _profile;
private string _name;
private string _language;
#endregion
#region Properties
public int Id_User
{
get { return _id_User; }
set
{
if (_id_User != value)
{
_id_User = value;
RaisePropertyChanged(() => Id_User);
}
}
}
public string Username
{
get { return _username; }
set
{
if (_username != value)
{
_username = value;
RaisePropertyChanged(() => Id_User);
}
}
}
public string Password
{
get { return _password; }
set
{
if (_password != value)
{
_password = value;
RaisePropertyChanged(() => Id_User);
}
}
}
public string Profile
{
get { return _profile; }
set
{
if (_profile != value)
{
_profile = value;
RaisePropertyChanged(() => Id_User);
}
}
}
public string Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
RaisePropertyChanged(() => Name);
}
}
}
public string Language
{
get { return _language; }
set
{
if (_language != value)
{
_language = value;
RaisePropertyChanged(() => Language);
}
}
}
#endregion
}
}
And finally, the View:
<Window x:Class="TOP2.Views.UsersView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:viewModels="clr-namespace:TOP2.ViewModels"
xmlns:local="TOP2"
Title="Sample App"
WindowStartupLocation="CenterScreen"
Height="459"
Width="795">
<Window.Resources>
<viewModels:UsersViewModel x:Key="Windows1ViewModel" />
</Window.Resources>
<Grid DataContext="{StaticResource Windows1ViewModel}">
<DataGrid ItemsSource="{Binding UsersCollection, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" AutoGenerateColumns="False" HorizontalAlignment="Left" Margin="81,51,0,0" VerticalAlignment="Top" Height="332" Width="622">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Username, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTextColumn Binding="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
What do I am forgetting or doing wrong?
Thanks in advance!
Oscar
Thank you so much Loetn and Andras Sebö, your clues resulted very helpful! The solution below has been which I have adopted and it has worked perfectly!!!
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel; // ObservableCollection
using System.ComponentModel; // INotifyPropertyChanged
using System.Collections.Specialized; // NotifyCollectionChangedEventHandler
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ObservableCollectionTest
{
class Program
{
static void Main(string[] args)
{
// ATTN: Please note it's a "TrulyObservableCollection" that's instantiated. Otherwise, "Trades[0].Qty = 999" will NOT trigger event handler "Trades_CollectionChanged" in main.
// REF: http://stackoverflow.com/questions/8490533/notify-observablecollection-when-item-changes
TrulyObservableCollection<Trade> Trades = new TrulyObservableCollection<Trade>();
Trades.Add(new Trade { Symbol = "APPL", Qty = 123 });
Trades.Add(new Trade { Symbol = "IBM", Qty = 456});
Trades.Add(new Trade { Symbol = "CSCO", Qty = 789 });
Trades.CollectionChanged += Trades_CollectionChanged;
Trades.ItemPropertyChanged += PropertyChangedHandler;
Trades.RemoveAt(2);
Trades[0].Qty = 999;
Console.WriteLine("Hit any key to exit");
Console.ReadLine();
return;
}
static void PropertyChangedHandler(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine(DateTime.Now.ToString() + ", Property changed: " + e.PropertyName + ", Symbol: " + ((Trade) sender).Symbol + ", Qty: " + ((Trade) sender).Qty);
return;
}
static void Trades_CollectionChanged(object sender, EventArgs e)
{
Console.WriteLine(DateTime.Now.ToString() + ", Collection changed");
return;
}
}
#region TrulyObservableCollection
public class TrulyObservableCollection<T> : ObservableCollection<T>
where T : INotifyPropertyChanged
{
public event PropertyChangedEventHandler ItemPropertyChanged;
public TrulyObservableCollection()
: base()
{
CollectionChanged += new NotifyCollectionChangedEventHandler(TrulyObservableCollection_CollectionChanged);
}
void TrulyObservableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (Object item in e.NewItems)
{
(item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
}
}
if (e.OldItems != null)
{
foreach (Object item in e.OldItems)
{
(item as INotifyPropertyChanged).PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
}
}
}
void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
NotifyCollectionChangedEventArgs a = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
OnCollectionChanged(a);
if (ItemPropertyChanged != null)
{
ItemPropertyChanged(sender, e);
}
}
}
#endregion
#region Sample entity
class Trade : INotifyPropertyChanged
{
protected string _Symbol;
protected int _Qty = 0;
protected DateTime _OrderPlaced = DateTime.Now;
public DateTime OrderPlaced
{
get { return _OrderPlaced; }
}
public string Symbol
{
get
{
return _Symbol;
}
set
{
_Symbol = value;
NotifyPropertyChanged("Symbol");
}
}
public int Qty
{
get
{
return _Qty;
}
set
{
_Qty = value;
NotifyPropertyChanged("Qty");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
#endregion
}
Bind this event to the CollectionChanged event of your ObservableCollection:
private void ObservableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (var item in e.NewItems)
{
item.PropertyChanged += this.Item_PropertyChanged;
}
}
if (e.OldItems != null)
{
foreach (var item in e.OldItems)
{
item.PropertyChanged -= this.Item_PropertyChanged;
}
}
}
private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
// do something
}
Because the items are in the collection and the collection itself does not change. You have to subscribe in the UsersViewModel class every UserModel changes before you add it to the collection.
Here is a possible solution:
http://social.msdn.microsoft.com/Forums/vstudio/en-US/c03b9edd-e9a9-4674-82d3-56caaf67d6d9/observablecollectiont-listen-for-changes-in-child-elements
How do I capture a key down event in WPF even if my application is not focused?
For me, the best way is this:
public MainWindow()
{
InitializeComponent();
CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
}
void CompositionTarget_Rendering(object sender, EventArgs e)
{
if ((Keyboard.GetKeyStates(Key.W) & KeyStates.Down) > 0)
{
player1.walk();
}
}
The rendering event runs every time.
Global keyboard hook can slow down your debugging.
I prefer to use this approach:
Create KeyboardListener class
public class KeyboardListener : IDisposable
{
private readonly Thread keyboardThread;
//Here you can put those keys that you want to capture
private readonly List<KeyState> numericKeys = new List<KeyState>
{
new KeyState(Key.D0),
new KeyState(Key.D1),
new KeyState(Key.D2),
new KeyState(Key.D3),
new KeyState(Key.D4),
new KeyState(Key.D5),
new KeyState(Key.D6),
new KeyState(Key.D7),
new KeyState(Key.D8),
new KeyState(Key.D9),
new KeyState(Key.NumPad0),
new KeyState(Key.NumPad1),
new KeyState(Key.NumPad2),
new KeyState(Key.NumPad3),
new KeyState(Key.NumPad4),
new KeyState(Key.NumPad5),
new KeyState(Key.NumPad6),
new KeyState(Key.NumPad7),
new KeyState(Key.NumPad8),
new KeyState(Key.NumPad9),
new KeyState(Key.Enter)
};
private bool isRunning = true;
public KeyboardListener()
{
keyboardThread = new Thread(StartKeyboardListener) { IsBackground = true };
keyboardThread.Start();
}
private void StartKeyboardListener()
{
while (isRunning)
{
Thread.Sleep(15);
if (Application.Current != null)
{
Application.Current.Dispatcher.Invoke(() =>
{
if (Application.Current.Windows.Count > 0)
{
foreach (var keyState in numericKeys)
{
if (Keyboard.IsKeyDown(keyState.Key) && !keyState.IsPressed) //
{
keyState.IsPressed = true;
KeyboardDownEvent?.Invoke(null, new KeyEventArgs(Keyboard.PrimaryDevice, PresentationSource.FromDependencyObject(Application.Current.Windows[0]), 0, keyState.Key));
}
if (Keyboard.IsKeyUp(keyState.Key))
{
keyState.IsPressed = false;
}
}
}
});
}
}
}
public event KeyEventHandler KeyboardDownEvent;
/// <summary>
/// Состояние клавиши
/// </summary>
private class KeyState
{
public KeyState(Key key)
{
this.Key = key;
}
public Key Key { get; }
public bool IsPressed { get; set; }
}
public void Dispose()
{
isRunning = false;
Task.Run(() =>
{
if (keyboardThread != null && !keyboardThread.Join(1000))
{
keyboardThread.Abort();
}
});
}
}
Subscribe to KeyboardDownEvent in code-behind (or where you need it).
public partial class MainWindow : Window
{
private KeyboardListener listener;
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
listener = new KeyboardListener();
listener.KeyboardDownEvent += ListenerOnKeyPressed;
}
private void ListenerOnKeyPressed(object sender, KeyEventArgs e)
{
// TYPE YOUR CODE HERE
}
private void Window_OnUnloaded(object sender, RoutedEventArgs e)
{
listener.KeyboardDownEvent -= ListenerOnKeyPressed;
}
}
Done
See this questions for hooking the keyboard Using global keyboard hook (WH_KEYBOARD_LL) in WPF / C#